diff options
| author | Tomas Bzatek <tbzatek@redhat.com> | 2024-10-11 23:38:44 +0200 |
|---|---|---|
| committer | Tomas Bzatek <tbzatek@redhat.com> | 2024-10-23 18:13:05 +0200 |
| commit | 7d757b8452daed2575e3a9cc300459d7e8674345 (patch) | |
| tree | 1b4c0107964cc871dc6e2d732c615af710592c4c /UCoreWorkers.pas | |
| parent | 81c8cd22e61701c1145dc267b0925add6cc0e78a (diff) | |
| download | tuxcmd-7d757b8452daed2575e3a9cc300459d7e8674345.tar.xz | |
Full port to GErrorv0.6.81
Started 15 years ago, picking up on that work.
Introduced the new TUXCMD_ERROR domain to distinguish between operations
or their steps. Plugins may continue reporting the G_IO_ERROR domain.
Diffstat (limited to 'UCoreWorkers.pas')
| -rw-r--r-- | UCoreWorkers.pas | 781 |
1 files changed, 402 insertions, 379 deletions
diff --git a/UCoreWorkers.pas b/UCoreWorkers.pas index 7c0fb02..3ff94f8 100644 --- a/UCoreWorkers.pas +++ b/UCoreWorkers.pas @@ -20,7 +20,7 @@ unit UCoreWorkers; interface -uses glib2, gtk2, SyncObjs, Classes, GTKForms, GTKView, ULibc, UEngines, UCoreUtils, UVFSCore, uVFSprototypes, UCore, UDirDelete; +uses glib2, gtk2, SyncObjs, Classes, GTKForms, ULibc, UEngines, UCoreUtils, UVFSCore, uVFSprototypes, UCore, UDirDelete; type TWorkerThreadJobType = (WORKER_JOB_DUMMY, WORKER_JOB_DELETE, WORKER_JOB_COPY, WORKER_JOB_MOVE, WORKER_JOB_EXTRACT_TO_TEMP, @@ -80,7 +80,10 @@ type TVFSCallbackThread = class(TThread) // Copy worker progress values FTotalSize, FTotalDone, FFileSize: cuLongLong; FCopySkipAllErrors: boolean; + FCopyErrorHandledInProgress: boolean; + FCopySilentCancel: boolean; FCopyProgressFunc: TEngineProgressFunc; + FCopySourceFile, FCopyDestFile: string; // Dialogs FCancelMessage: string; @@ -95,8 +98,7 @@ type TVFSCallbackThread = class(TThread) FGUIChanged: boolean; FDirDeleteButtonsType: TFDirDeleteButtonSet; - FDirDeleteTitle, FDirDeleteFileName: string; - FDirDeleteError: PGError; + FDirDeleteText1, FDirDeleteFileName, FDirDeleteText2: string; FOverwriteShowAppend: boolean; FOverwriteSourceItem, FOverwriteDestItem: PDataItem; @@ -117,7 +119,7 @@ type TVFSCallbackThread = class(TThread) procedure SetProgress2Params(const ProgressMax: Int64); procedure UpdateCaption1(const CaptionText: string); procedure UpdateCaption2(const CaptionText: string); - function ShowDirDeleteDialog(ButtonsType: TFDirDeleteButtonSet; const Title, FileName: string; Error: PGError): integer; + function ShowDirDeleteDialog(ButtonsType: TFDirDeleteButtonSet; const Text1, FileName, Text2: string): integer; function ShowOverwriteDialog(ShowAppend: boolean; SourceItem, DestItem: PDataItem; const SourceFile, DestFile: string; var RenameStr: string): integer; function ShowInaccessibleDialog(InaccessiblePaths: TStringList): integer; function ShowNewDirDialog(Caption, LabelCaption, Edit: string): integer; @@ -137,7 +139,7 @@ type TVFSCallbackThread = class(TThread) public JobType: TWorkerThreadJobType; SrcEngine, DestEngine: TPanelEngine; - ErrorHappened: boolean; + ErrorHappened: boolean; // checked in UMain // For getting list of selected items in the panel DataList: TList; @@ -214,8 +216,8 @@ procedure ProcessThreadEvents(SenderThread: TVFSCallbackThread); implementation uses SysUtils, DateUtils, StrUtils, UConfig, UOverwrite, ULocale, - UFileAssoc, UCoreClasses, URemoteWait, UMain, UGnome, UNewDir, UProgress, - crc; + UCoreClasses, URemoteWait, UMain, UGnome, UNewDir, UProgress, + UError, crc; @@ -502,12 +504,12 @@ begin FGUIMutex.Release; end; -function TWorkerThread.ShowDirDeleteDialog(ButtonsType: TFDirDeleteButtonSet; const Title, FileName: string; Error: PGError): integer; +function TWorkerThread.ShowDirDeleteDialog(ButtonsType: TFDirDeleteButtonSet; const Text1, FileName, Text2: string): integer; begin FDialogResultDirDelete := DIR_DELETE_CANCEL; - FDirDeleteTitle := Title; + FDirDeleteText1 := Text1; FDirDeleteFileName := FileName; - FDirDeleteError := Error; + FDirDeleteText2 := Text2; FDirDeleteButtonsType := ButtonsType; FDialogShowDirDelete := True; FCallbackLockEvent.ResetEvent; @@ -579,11 +581,11 @@ begin with PDataItem(DataList[i])^ do if (not UpDir) and Selected then InputFiles.Add(CurrPath + String(FName)); - // If not files are selected, take into account the current active item + // If no files are selected, take into account the current active item if (InputFiles.Count = 0) and Assigned(SelectedItem) and (not SelectedItem^.UpDir) then InputFiles.Add(CurrPath + String(SelectedItem^.FName)); - FillDirFiles(AEngine, AList, InputFiles, DoNotRecurse, True, InaccessiblePaths); + FillDirFiles(AEngine, AList, InputFiles, DoNotRecurse, True, InaccessiblePaths, @FCancelled); InputFiles.Free; end; @@ -709,10 +711,10 @@ begin try AFDirDelete := TFDirDelete.Create(ParentDialogForm); AFDirDelete.AddButtons(FDirDeleteButtonsType); - AFDirDelete.Label1.Caption := FDirDeleteTitle; + AFDirDelete.Label1.Caption := FDirDeleteText1; AFDirDelete.Label2.Caption := FDirDeleteFileName; - if FDirDeleteError <> nil then - AFDirDelete.Label3.Caption := FDirDeleteError^.message; + AFDirDelete.Label3.Caption := FDirDeleteText2; + AFDirDelete.Label3.Visible := Length(FDirDeleteText2) > 0; FDialogResultDirDelete := Integer(AFDirDelete.Run); if (FDirDeleteButtonsType = DIR_DELETE_SET_COPY_ERROR) and (FDialogResultDirDelete = DIR_DELETE_IGNORE) and (JobType = WORKER_JOB_MOVE) then case Application.MessageBox(LANGIgnoreError, [mbYes, mbNo{, mbCancel}], mbWarning, mbYes, mbNo) of @@ -830,21 +832,17 @@ var SkipAll: boolean; // DebugMsg(['Debug: IsDir: ', AFileRec^.IsDir, ', Stage1: ', AFileRec^.Stage1, ', IsLnk: ', AFileRec^.IsLnk, '; Result = ', AFileRec^.IsDir and AFileRec^.Stage1 and (not AFileRec^.IsLnk)]); if AFileRec^.DataItem^.IsDir and AFileRec^.Stage1 and (not AFileRec^.DataItem^.IsLnk) then Exit; Res := AEngine.Remove(String(AFileRec^.DataItem^.FName), @Error); -// DebugMsg(['Result : ', Res]); - if not Res then - if SkipAll then Result := True else - begin - Response := ShowDirDeleteDialog(DIR_DELETE_SET_DELETE_ERROR, 'Error deleting file/directory:', String(AFileRec^.DataItem^.FDisplayName), Error); - if Error <> nil then + if not Res then begin + if SkipAll then Result := True + else begin + Response := ShowDirDeleteDialog(DIR_DELETE_SET_DELETE_ERROR, 'Error deleting file/directory', AFileRec^.DataItem^.FDisplayName, Error^.message); g_error_free(Error); - case Response of - DIR_DELETE_SKIP : Result := True; - DIR_DELETE_SKIP_ALL : begin - SkipAll := True; - Result := True; - end; - DIR_DELETE_RETRY : Result := HandleDelete(AFileRec); - else Result := False; + case Response of + DIR_DELETE_SKIP : Result := True; + DIR_DELETE_SKIP_ALL : begin SkipAll := True; Result := True; end; + DIR_DELETE_RETRY : Result := HandleDelete(AFileRec); + else Result := False; + end; end; end; end; @@ -893,7 +891,7 @@ begin if (not DeleteAll) and (PDataItemSL(AList[i])^.Level = 1) and PDataItemSL(AList[i])^.Stage1 and PDataItemSL(AList[i])^.DataItem^.IsDir and (not PDataItemSL(AList[i])^.DataItem^.IsLnk) and (i < AList.Count - 2) and (PDataItemSL(AList[i + 1])^.Level = 2) then begin - Response := ShowDirDeleteDialog(DIR_DELETE_SET_DELETE_NON_EMPTY, 'The directory is not empty, do you want to delete it with all its files and subdirectories?', string(PDataItemSL(AList[i])^.DataItem^.FDisplayName), nil); + Response := ShowDirDeleteDialog(DIR_DELETE_SET_DELETE_NON_EMPTY, 'The directory is not empty, do you want to delete it with all its files and subdirectories?', PDataItemSL(AList[i])^.DataItem^.FDisplayName, ''); case Response of DIR_DELETE_DELETE : ; // Do nothing in this case - I will not bother with changing the structure; it works :-) DIR_DELETE_ALL : DeleteAll := True; @@ -925,6 +923,35 @@ end; (********************************************************************************************************************************) (********************************************************************************************************************************) + procedure GetCopyProgressErrorLabels(Thread: TWorkerThread; Error: PGError; var s1, s2: string); + begin + if (Thread.JobType in [WORKER_JOB_COPY, WORKER_JOB_EXTRACT_TO_TEMP]) then s1 := LANGCopyError + else s1 := LANGMoveError; + s2 := Thread.FCopySourceFile; + if (Error^.domain = TUXCMD_ERROR) then + case TuxcmdErrorEnum(Error^.code) of + TUXCMD_ERROR_ALLOC_FAILED: s1 := LANGMemoryAllocationFailed; + TUXCMD_ERROR_SOURCE_OPEN: s1 := LANGCannotOpenSourceFile; + TUXCMD_ERROR_TARGET_OPEN: begin + s1 := LANGCannotOpenDestinationFile; + s2 := Thread.FCopyDestFile; + end; + TUXCMD_ERROR_SOURCE_READ: s1 := LANGCannotReadFromSourceFile; + TUXCMD_ERROR_TARGET_WRITE: begin + s1 := LANGCannotWriteToDestinationFile; + s2 := Thread.FCopyDestFile; + end; + TUXCMD_ERROR_SOURCE_CLOSE: s1 := LANGCannotCloseSourceFile; + TUXCMD_ERROR_TARGET_CLOSE: begin + s1 := LANGCannotCloseDestinationFile; + s2 := Thread.FCopyDestFile; + end; + TUXCMD_ERROR_RENAME: begin + s1 := 'Cannot move the file to'; + s2 := Thread.FCopyDestFile; + end; + end; + end; // Keep in sync with uVFSprototypes.pas/TVFSProgressCallback function vfs_copy_progress_callback(position: guint64; error: PGError; user_data: Pointer): gboolean; cdecl; @@ -941,147 +968,146 @@ end; // Keep in sync with UEngines.pas/TEngineProgressFunc function CopyFilesWorker_ProgressFunc(Sender: Pointer; BytesDone: Int64; Error: PGError): boolean; cdecl; + var s, s2: string; begin Result := True; try if Assigned(Sender) and (TObject(Sender) is TWorkerThread) then with TWorkerThread(Sender) do begin - if BytesDone = 0 then UpdateProgress1(0, '0%') - else UpdateProgress1(BytesDone, Format('%d%%', [Round(BytesDone / FFileSize * 100)])); - UpdateProgress2(FTotalDone + BytesDone, Format('%d%%', [Round((FTotalDone + BytesDone) / FTotalSize * 100)])); Result := not FCancelled; - CommitGUIUpdate; + if Error <> nil then begin + // Error handling + if FCopySkipAllErrors then Exit; + GetCopyProgressErrorLabels(TWorkerThread(Sender), Error, s, s2); + case ShowDirDeleteDialog(DIR_DELETE_SET_COPY_ERROR, s, s2, StrToUTF8(Error^.message)) of + DIR_DELETE_IGNORE : Result := True; + DIR_DELETE_SKIP : Result := False; + DIR_DELETE_SKIP_ALL : begin + FCopySkipAllErrors := True; + Result := False; + end; + DIR_DELETE_CANCEL, 124, 255 : begin + Result := False; + FCancelled := True; + end; + else begin + Result := False; // Cancel + FCopySilentCancel := True; + end; + end; + FCopyErrorHandledInProgress := not Result; + end else begin + // Progress update + if BytesDone = 0 then UpdateProgress1(0, '0%') + else UpdateProgress1(BytesDone, Format('%d%%', [Round(BytesDone / FFileSize * 100)])); + UpdateProgress2(FTotalDone + BytesDone, Format('%d%%', [Round((FTotalDone + BytesDone) / FTotalSize * 100)])); + CommitGUIUpdate; + end; end else DebugMsg(['*** CopyFilesWorker: Sender is not TWorkerThread']); except on E: Exception do DebugMsg(['*** Exception raised in ProgressFunc(Sender=', Sender, ', BytesDone=', BytesDone, '): (', E.ClassName, '): ', E.Message]); end; end; - // Return True to ignore the error (Skip, Skip All, Ignore, Cancel) - function CopyFilesWorker_ErrorFunc(Sender: Pointer; ErrorType, ErrorNum: integer; FileName: string): boolean; cdecl; - var s, s2, s3: string; - begin - Result := False; - with TWorkerThread(Sender) do begin - if FCopySkipAllErrors then begin - Result := True; - Exit; - end; - case ErrorType of - 0 : begin - FCancelled := True; - Exit; - end; - 1 : s := LANGMemoryAllocationFailed; - 2 : s := LANGCannotOpenSourceFile; - 3 : s := LANGCannotOpenDestinationFile; - 4 : s := LANGCannotCloseDestinationFile; - 5 : s := LANGCannotCloseSourceFile; - 6 : s := LANGCannotReadFromSourceFile; - 7 : s := LANGCannotWriteToDestinationFile; - end; - if (JobType in [WORKER_JOB_COPY, WORKER_JOB_EXTRACT_TO_TEMP]) then s2 := LANGCopyError - else s2 := LANGMoveError; - if ErrorType <> 1 then s3 := StrToUTF8(FileName) - else s3 := ''; - - // * TODO: fix error string, port to GError - case ShowDirDeleteDialog(DIR_DELETE_SET_COPY_ERROR, s, s3, nil) of - DIR_DELETE_CANCEL : begin - Result := False; - FCancelled := True; - end; - DIR_DELETE_IGNORE : Result := True; - DIR_DELETE_SKIP_ALL : begin - FCopySkipAllErrors := True; { Skip All Err } - Result := False; //** True? - end; - else Result := False; // Skip - end; - end; - end; - procedure TWorkerThread.CopyFilesWorker; var DefResponse: integer; // Global variables for this function - SkipAll: boolean; // Returns True if file was successfully copied, if not, the file will be deleted in LocalCopyFile - function ManualCopyFile(SourceFile, DestFile: string; Append: boolean): boolean; + function ManualCopyFile(SourceFile, DestFile: string; Append: boolean; Error: PPGError): boolean; var fsrc, fdst: TEngineFileDes; BSize: integer; Buffer: Pointer; BytesDone, BytesRead, BytesWritten: Int64; - Res: boolean; - Error: PGError; + LocalError: PGError; begin - DebugMsg(['ManualCopyFile: ', SourceFile, ' ---> ', DestFile]); Result := False; - Error := nil; - fsrc := SrcEngine.OpenFile(SourceFile, omRead, @Error); + LocalError := nil; + DebugMsg(['ManualCopyFile: ', SourceFile, ' ---> ', DestFile]); + fsrc := SrcEngine.OpenFile(SourceFile, omRead, @LocalError); if fsrc = nil then begin - // * TODO: set real error, also free it - CopyFilesWorker_ErrorFunc(Self, 2, 1 { Error }, SourceFile); // Cannot open source file + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_SOURCE_OPEN), LocalError^.message); + g_error_free(LocalError); + CopyFilesWorker_ProgressFunc(Self, 0, Error^); // Cannot open source file Exit; end; - if Append then fdst := DestEngine.OpenFile(DestFile, omAppend, @Error) - else fdst := DestEngine.OpenFile(DestFile, omWrite, @Error); + if Append then fdst := DestEngine.OpenFile(DestFile, omAppend, @LocalError) + else fdst := DestEngine.OpenFile(DestFile, omWrite, @LocalError); if fdst = nil then begin - // * TODO: set real error, also free it SrcEngine.CloseFile(fsrc, nil); - CopyFilesWorker_ErrorFunc(Self, 3, 1 { Error }, SourceFile); // Cannot open target file + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_TARGET_OPEN), LocalError^.message); + g_error_free(LocalError); + CopyFilesWorker_ProgressFunc(Self, 0, Error^); // Cannot open target file Exit; end; BytesDone := 0; - Res := True; - BSize := DestEngine.GetBlockSize; Buffer := malloc(BSize); if Buffer = nil then begin - CopyFilesWorker_ErrorFunc(Self, 1, errno, SourceFile); // Memory allocation failed - libc_free(Buffer); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_ALLOC_FAILED), '%m'); + CopyFilesWorker_ProgressFunc(Self, 0, Error^); // Memory allocation failed Exit; end; memset(Buffer, 0, BSize); BytesWritten := 0; repeat - BytesRead := SrcEngine.ReadFile(fsrc, Buffer, BSize, @Error); - if (BytesRead = 0) and (Error <> nil) then - // * TODO: set real error, also free it - Res := CopyFilesWorker_ErrorFunc(Self, 6, 1 { Error }, SourceFile); // Cannot read from source file + BytesRead := SrcEngine.ReadFile(fsrc, Buffer, BSize, @LocalError); + if (BytesRead = 0) and (Error^ <> nil) then begin + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_SOURCE_READ), LocalError^.message); + g_error_free(LocalError); + LocalError := nil; + Result := CopyFilesWorker_ProgressFunc(Self, BytesWritten, Error^); // Cannot read from source file + if Result then begin + g_error_free(Error^); + Error^ := nil; + Continue; + end else Break; + end; if BytesRead > 0 then begin - Error := nil; - BytesWritten := DestEngine.WriteFile(fdst, Buffer, BytesRead, @Error); - if (BytesWritten < BytesRead) then - // * TODO: set real error, also free it - Res := CopyFilesWorker_ErrorFunc(Self, 7, 1 { Error }, DestFile); // Cannot write to source file + BytesWritten := DestEngine.WriteFile(fdst, Buffer, BytesRead, @LocalError); + if (BytesWritten < BytesRead) then begin + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_TARGET_WRITE), LocalError^.message); + g_error_free(LocalError); + LocalError := nil; + Result := CopyFilesWorker_ProgressFunc(Self, BytesWritten, Error^); // Cannot write to target file + if Result then begin + g_error_free(Error^); + Error^ := nil; + Continue; + end else Break; + end; end; BytesDone := BytesDone + BytesRead; - if not CopyFilesWorker_ProgressFunc(Self, BytesDone, nil) then begin - Res := False; - Break; - end; + Result := CopyFilesWorker_ProgressFunc(Self, BytesDone, nil); + if not Result then Break; until (BytesRead = 0) or (BytesWritten < BytesRead); libc_free(Buffer); - // * TODO: set real error, also free it - if not DestEngine.CloseFile(fdst, nil) then begin - CopyFilesWorker_ErrorFunc(Self, 4, errno, DestFile); // Cannot close target file + if not DestEngine.CloseFile(fdst, @LocalError) then begin + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_TARGET_CLOSE), LocalError^.message); + g_error_free(LocalError); + Result := False; + SrcEngine.CloseFile(fsrc, nil); + CopyFilesWorker_ProgressFunc(Self, BytesDone, Error^); // Cannot close target file Exit; end; - // * TODO: set real error, also free it - if not SrcEngine.CloseFile(fsrc, nil) then begin - CopyFilesWorker_ErrorFunc(Self, 5, errno, SourceFile); // Cannot close source file - Exit; + if not SrcEngine.CloseFile(fsrc, @LocalError) then begin + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_SOURCE_CLOSE), LocalError^.message); + g_error_free(LocalError); + Result := CopyFilesWorker_ProgressFunc(Self, BytesDone, Error^); // Cannot close source file + if Result then begin + // user has chosen to ignore the error + g_error_free(Error^); + Error^ := nil; + end else Exit; end; - Result := Res; end; // Returns True if the file was successfully copied and will be deleted on move - function LocalCopyFile(SourceFile, DestFile: string; Append: boolean): boolean; + function LocalCopyFile(SourceFile, DestFile: string; Append: boolean; Error: PPGError): boolean; var DataSrc, DataDest: PDataItem; begin Result := False; @@ -1091,28 +1117,28 @@ var DefResponse: integer; // Global variables for this function // local -> local if (SrcEngine is TLocalTreeEngine) and (DestEngine is TLocalTreeEngine) - then Result := DestEngine.CopyFileIn(SourceFile, DestFile, Append, @CopyFilesWorker_ProgressFunc, Self) + then Result := DestEngine.CopyFileIn(SourceFile, DestFile, Append, @CopyFilesWorker_ProgressFunc, Self, Error) else // from local engine to VFS engine if (SrcEngine is TLocalTreeEngine) and (DestEngine is TVFSEngine) then begin AEngine := DestEngine; - Result := (DestEngine as TVFSEngine).CopyFileInEx(SourceFile, DestFile, Append); + Result := (DestEngine as TVFSEngine).CopyFileInEx(SourceFile, DestFile, Append, Error); end else // from VFS engine to local (most common use) if (SrcEngine is TVFSEngine) and (DestEngine is TLocalTreeEngine) then begin AEngine := SrcEngine; - Result := (SrcEngine as TVFSEngine).CopyFileOutEx(SourceFile, DestFile, Append); + Result := (SrcEngine as TVFSEngine).CopyFileOutEx(SourceFile, DestFile, Append, Error); end - // VFS to VFS (not supported yet) + // VFS to VFS (mostly unsupported) else begin AEngine := SrcEngine; - Result := ManualCopyFile(SourceFile, DestFile, Append); + Result := ManualCopyFile(SourceFile, DestFile, Append, Error); end; AEngine := nil; @@ -1130,7 +1156,11 @@ var DefResponse: integer; // Global variables for this function FreeDataItem(DataDest); end; except - on E: Exception do DebugMsg(['*** Exception raised in LocalCopyFile(SourceFile=', SourceFile, ', DestFile=', DestFile, ', Append=', Append, '): (', E.ClassName, '): ', E.Message]); + on E: Exception do begin + Result := False; + tuxcmd_set_error_from_exception(Error, E); + DebugMsg(['*** Exception raised in LocalCopyFile(SourceFile=', SourceFile, ', DestFile=', DestFile, ', Append=', Append, '): (', E.ClassName, '): ', E.Message]); + end; end; end; @@ -1161,31 +1191,24 @@ var DefResponse: integer; // Global variables for this function Result := TestCaseInsensitiveFS and DestEngine.TwoSameFiles(Path1, Path2, False); end; - function DoOperation(AFileRec: PDataItemSL; const Dst: string; var ErrorKind: integer; const Append: boolean): integer; + function DoOperation(AFileRec: PDataItemSL; const Dst: string; const Append: boolean; Error: PPGError): boolean; begin - ErrorKind := 0; - Result := 0; + Result := False; try with AFileRec^ do begin if DataItem^.IsLnk then begin // Explicit copy the file if (JobType in [WORKER_JOB_COPY, WORKER_JOB_EXTRACT_TO_TEMP]) or (not IsOnSameFS(String(DataItem^.FName), ExtractFileDir(Dst))) then begin - // * TODO: check error - ErrorKind := Ord(DestEngine.MakeSymLink(Dst, String(DataItem^.LnkPointTo), nil)); -// if ErrorKind <> 0 then Result := ERRCreateLink; - if JobType = WORKER_JOB_MOVE then begin - // * TODO: check error - ErrorKind := Ord(SrcEngine.Remove(String(DataItem^.FName), nil)); -// if ErrorKind <> 0 then Result := ERRRemove; - end; + Result := DestEngine.MakeSymLink(Dst, String(DataItem^.LnkPointTo), Error); + if Result and (JobType = WORKER_JOB_MOVE) then + Result := SrcEngine.Remove(String(DataItem^.FName), Error); end else begin // Move the file - // * TODO: check error - ErrorKind := Ord(DestEngine.RenameFile(String(DataItem^.FName), Dst, nil)); -// if ErrorKind <> 0 then Result := ERRCopyMove; + Result := DestEngine.RenameFile(String(DataItem^.FName), Dst, Error); end; - end else // is not link + end else // is not a symlink if (JobType in [WORKER_JOB_COPY, WORKER_JOB_EXTRACT_TO_TEMP]) then begin // Copy mode - if LocalCopyFile(String(DataItem^.FName), Dst, Append) then begin + Result := LocalCopyFile(String(DataItem^.FName), Dst, Append, Error); + if Result then begin if IsOnRO and ConfClearReadOnlyAttr and (DataItem^.Mode and S_IWUSR = 0) then DataItem^.Mode := DataItem^.Mode or S_IWUSR; // * TODO: check error DestEngine.Chmod(Dst, DataItem^.Mode, nil); @@ -1196,187 +1219,179 @@ var DefResponse: integer; // Global variables for this function if IsOnSameFS(String(DataItem^.FName), ExtractFileDir(Dst)) then begin if TwoSameFiles(String(DataItem^.FName), Dst, True) and (not TwoSameFiles(String(DataItem^.FName), Dst, False)) then begin DebugMsg(['*** Activating double-rename due to renaming on case-insensitive FS']); - // * TODO: check error - ErrorKind := Ord(DestEngine.RenameFile(String(DataItem^.FName), Dst + '_tcmd', nil)); - if ErrorKind = 0 then ErrorKind := Ord(DestEngine.RenameFile(Dst + '_tcmd', Dst, nil)); - end else ErrorKind := Ord(DestEngine.RenameFile(String(DataItem^.FName), Dst, nil)); -// if ErrorKind <> 0 then Result := ERRCopyMove; + Result := DestEngine.RenameFile(String(DataItem^.FName), Dst + '_tcmd', Error); + if Result then + Result := DestEngine.RenameFile(Dst + '_tcmd', Dst, Error); + end else Result := DestEngine.RenameFile(String(DataItem^.FName), Dst, Error); end else begin - if LocalCopyFile(String(DataItem^.FName), Dst, Append) then begin + Result := LocalCopyFile(String(DataItem^.FName), Dst, Append, Error); + if Result then begin if IsOnRO and ConfClearReadOnlyAttr and (DataItem^.Mode and S_IWUSR = 0) then DataItem^.Mode := DataItem^.Mode or S_IWUSR; // * TODO: check error DestEngine.Chmod(Dst, DataItem^.Mode, nil); DestEngine.Chown(Dst, DataItem^.UID, DataItem^.GID, nil); DestEngine.ChangeTimes(Dst, DataItem^.mtime, DataItem^.atime, nil); - if not FCancelled then begin - ErrorKind := Ord(SrcEngine.Remove(String(DataItem^.FName), nil)); -// if ErrorKind <> 0 then Result := ERRRemove; - end; + Result := SrcEngine.Remove(String(DataItem^.FName), Error); end; end; end; // DebugMsg(['(II) CopyFilesWorker.DoOperation: finished']); except - on E: Exception do DebugMsg(['*** Exception raised in DoOperation(AFileRec=', AFileRec, ', Dst=', Dst, ', ErrorKind=', ErrorKind, ', Append=', Append, '): (', E.ClassName, '): ', E.Message]); + on E: Exception do begin + Result := False; + tuxcmd_set_error_from_exception(Error, E); + DebugMsg(['*** Exception raised in DoOperation(AFileRec=', AFileRec, ', Dst=', Dst, ', Append=', Append, '): (', E.ClassName, '): ', E.Message]); + end; end; end; // Return False to break the processing (Cancel) function HandleCopy(AFileRec: PDataItemSL; NewFilePath: string): boolean; - var Res, Response, ErrorKind, r: integer; + var Response: integer; + Res: boolean; Item: PDataItem; - s, s1, s3, cap: string; - FromInfoLabel, ToInfoLabel, InfoLabelFormat: string; + s, s2, s3: string; RenameStr: string; + Error: PGError; begin Result := True; + Res := True; + Error := nil; try + // Second stage - change permissions + if (not AFileRec^.Stage1) and ((JobType in [WORKER_JOB_COPY, WORKER_JOB_EXTRACT_TO_TEMP]) or (not AFileRec^.ForceMove)) then begin + with AFileRec^ do begin + if IsOnRO and ConfClearReadOnlyAttr and (DataItem^.Mode and S_IWUSR = 0) then DataItem^.Mode := DataItem^.Mode or S_IWUSR; + // * TODO: check error + DestEngine.Chmod(NewFilePath, DataItem^.Mode, nil); + DestEngine.Chown(NewFilePath, DataItem^.UID, DataItem^.GID, nil); + DestEngine.ChangeTimes(NewFilePath, DataItem^.mtime, DataItem^.atime, nil); + if JobType = WORKER_JOB_MOVE then + Res := SrcEngine.Remove(String(DataItem^.FName), @Error); // Remove directory + end; + end else - // Second stage - change permissions - if (not AFileRec^.Stage1) and ((JobType in [WORKER_JOB_COPY, WORKER_JOB_EXTRACT_TO_TEMP]) or (not AFileRec^.ForceMove)) then - with AFileRec^ do begin - if IsOnRO and ConfClearReadOnlyAttr and (DataItem^.Mode and S_IWUSR = 0) then DataItem^.Mode := DataItem^.Mode or S_IWUSR; - // * TODO: check error - DestEngine.Chmod(NewFilePath, DataItem^.Mode, nil); - DestEngine.Chown(NewFilePath, DataItem^.UID, DataItem^.GID, nil); - DestEngine.ChangeTimes(NewFilePath, DataItem^.mtime, DataItem^.atime, nil); - if JobType = WORKER_JOB_MOVE then - SrcEngine.Remove(String(DataItem^.FName), nil); // Remove directory - Exit; - end; - - // First stage - copy data - if AFileRec^.DataItem^.IsDir then begin - Res := 0; - if AFileRec^.ForceMove and (JobType = WORKER_JOB_MOVE) + // First stage - copy data + if AFileRec^.DataItem^.IsDir then begin + if AFileRec^.ForceMove and (JobType = WORKER_JOB_MOVE) then begin if TwoSameFiles(ExcludeTrailingPathDelimiter(string(AFileRec^.DataItem^.FName)), ExcludeTrailingPathDelimiter(string(AFileRec^.ADestination)), True) and (not TwoSameFiles(ExcludeTrailingPathDelimiter(string(AFileRec^.DataItem^.FName)), ExcludeTrailingPathDelimiter(string(AFileRec^.ADestination)), False)) then begin DebugMsg(['*** Activating double-rename due to renaming on case-insensitive FS']); - // * TODO: check error - ErrorKind := Ord(DestEngine.RenameFile(string(AFileRec^.DataItem^.FName), ExcludeTrailingPathDelimiter(string(AFileRec^.ADestination)) + '_tcmd', nil)); - if ErrorKind = 0 then ErrorKind := ord(DestEngine.RenameFile(ExcludeTrailingPathDelimiter(string(AFileRec^.ADestination)) + '_tcmd', ExcludeTrailingPathDelimiter(string(AFileRec^.ADestination)), nil)); - end else ErrorKind := Ord(DestEngine.RenameFile(string(AFileRec^.DataItem^.FName), string(AFileRec^.ADestination), nil)); -{ if ErrorKind <> 0 then Res := ERRCopyMove - else Res := 0; } + Res := DestEngine.RenameFile(string(AFileRec^.DataItem^.FName), ExcludeTrailingPathDelimiter(string(AFileRec^.ADestination)) + '_tcmd', @Error); + if Res then Res := DestEngine.RenameFile(ExcludeTrailingPathDelimiter(string(AFileRec^.ADestination)) + '_tcmd', ExcludeTrailingPathDelimiter(string(AFileRec^.ADestination)), @Error); + end else Res := DestEngine.RenameFile(string(AFileRec^.DataItem^.FName), string(AFileRec^.ADestination), @Error); end else if not DestEngine.DirectoryExists(NewFilePath, False) then begin - // * TODO: check error - ErrorKind := Ord(DestEngine.MakeDir(NewFilePath, nil)); -{ if ErrorKind <> 0 then Res := ERRMkDir - else Res := 0; } - end; - end else begin // not a directory - if not DestEngine.DirectoryExists(ExtractFileDir(NewFilePath), False) then - // * TODO: check error - DestEngine.MakeDir(ExtractFileDir(NewFilePath), nil); - SetProgress1Params(AFileRec^.DataItem^.Size + Ord(AFileRec^.DataItem^.Size = 0)); - FFileSize := AFileRec^.DataItem^.Size; - CopyFilesWorker_ProgressFunc(Self, 0, nil); - Res := 0; - if DestEngine.FileExists(NewFilePath, False) and - (not ((JobType = WORKER_JOB_MOVE) and (not TwoSameFiles(NewFilePath, AFileRec^.DataItem^.FName, False)) and TwoSameFiles(NewFilePath, AFileRec^.DataItem^.FName, True))) - then begin - Response := DefResponse; - // * TODO: check error --> display dialog - // * TODO: should be SrcEngine? - Item := DestEngine.GetFileInfo(NewFilePath, False, True, nil); - if Item = nil then begin - DebugMsg(['Something went terribly wrong during copy - Item := DestEngine.GetFileInfoSL(NewFilePath) == NULL!']); - Result := False; - Exit; - end; - if Response = 0 then begin - case ConfSizeFormat of - 5: InfoLabelFormat := '%s, %s'; - else InfoLabelFormat := LANGOvewriteSBytesS; - end; - RenameStr := ExtractFileName(NewFilePath); - Response := ShowOverwriteDialog(JobType in [WORKER_JOB_COPY], Item, AFileRec^.DataItem, NewFilePath, AFileRec^.DataItem^.FName, RenameStr); - case Response of - // OVERWRITE_OVERWRITE - // OVERWRITE_SKIP - OVERWRITE_OVERWRITE_ALL, OVERWRITE_OVERWRITE_ALL_OLDER, OVERWRITE_SKIP_ALL: DefResponse := Response; - OVERWRITE_CANCEL, 124 {Close Window}, 255: begin - Result := False; - Exit; - end; - OVERWRITE_RENAME: begin - NewFilePath := Copy(NewFilePath, 1, LastDelimiter(PathDelim, NewFilePath)) + RenameStr; - Result := HandleCopy(AFileRec, NewFilePath); - Exit; - end; - OVERWRITE_APPEND: begin - Res := DoOperation(AFileRec, NewFilePath, ErrorKind, True); - end; + Res := DestEngine.MakeDir(NewFilePath, @Error); end; - end; - - // Remove destination file if exists and should be overwritten - if (Response in [OVERWRITE_OVERWRITE, OVERWRITE_OVERWRITE_ALL]) or ((Response = OVERWRITE_OVERWRITE_ALL_OLDER) and (Item^.mtime < AFileRec^.DataItem^.mtime)) then begin + end else begin // not a directory + if not DestEngine.DirectoryExists(ExtractFileDir(NewFilePath), False) then // * TODO: check error - r := ord(DestEngine.Remove(NewFilePath, nil)); - while r <> 0 do begin - // * TODO: check error, port to GError - Res := ShowDirDeleteDialog(DIR_DELETE_SET_DELETE_ERROR, 'The file could not be deleted', StrToUTF8(String(NewFilePath)), nil); - case Res of - DIR_DELETE_SKIP: begin - Result := True; - Exit; - end; - // * TODO: check error - DIR_DELETE_RETRY: r := Ord(DestEngine.Remove(NewFilePath, nil)); - DIR_DELETE_CANCEL, 124, 255: begin - Result := False; - Exit; - end; + DestEngine.MakeDir(ExtractFileDir(NewFilePath), nil); + SetProgress1Params(AFileRec^.DataItem^.Size + Ord(AFileRec^.DataItem^.Size = 0)); + FFileSize := AFileRec^.DataItem^.Size; + CopyFilesWorker_ProgressFunc(Self, 0, nil); + // Overwrite handling + if DestEngine.FileExists(NewFilePath, False) and + (not ((JobType = WORKER_JOB_MOVE) and (not TwoSameFiles(NewFilePath, AFileRec^.DataItem^.FName, False)) and TwoSameFiles(NewFilePath, AFileRec^.DataItem^.FName, True))) + then begin + Response := DefResponse; + Item := DestEngine.GetFileInfo(NewFilePath, False, True, @Error); + if Item = nil then begin + // Display an error and bail out + Res := False; + end else begin + if Response = 0 then begin + RenameStr := ExtractFileName(NewFilePath); + Response := ShowOverwriteDialog(JobType in [WORKER_JOB_COPY], Item, AFileRec^.DataItem, NewFilePath, AFileRec^.DataItem^.FName, RenameStr); + case Response of + // OVERWRITE_OVERWRITE + // OVERWRITE_SKIP + OVERWRITE_OVERWRITE_ALL, OVERWRITE_OVERWRITE_ALL_OLDER, OVERWRITE_SKIP_ALL: DefResponse := Response; + OVERWRITE_RENAME: begin + NewFilePath := Copy(NewFilePath, 1, LastDelimiter(PathDelim, NewFilePath)) + RenameStr; + FCopyDestFile := NewFilePath; // ... for the label + Result := HandleCopy(AFileRec, NewFilePath); + Exit; + end; + OVERWRITE_APPEND: Res := DoOperation(AFileRec, NewFilePath, True, @Error); + OVERWRITE_CANCEL, 124 {Close Window}, 255, 252: begin + Result := False; + Exit; + end; + end; + end; + + // Remove destination file if exists and should be overwritten + if (Response in [OVERWRITE_OVERWRITE, OVERWRITE_OVERWRITE_ALL]) or ((Response = OVERWRITE_OVERWRITE_ALL_OLDER) and (Item^.mtime < AFileRec^.DataItem^.mtime)) then begin + while not DestEngine.Remove(NewFilePath, @Error) do begin + Response := ShowDirDeleteDialog(DIR_DELETE_SET_DELETE_ERROR, 'Error deleting file', StrToUTF8(String(NewFilePath)), Error^.message); + g_error_free(Error); + Error := nil; + case Response of + DIR_DELETE_SKIP: begin + Result := True; + Exit; + end; + DIR_DELETE_RETRY: ; // Continue + DIR_DELETE_CANCEL, 124, 255: begin + Result := False; + Exit; + end; + else begin // Cancel + Result := False; + Exit; + end; + end; + end; + Res := DoOperation(AFileRec, NewFilePath, False, @Error); end; end; - Res := DoOperation(AFileRec, NewFilePath, ErrorKind, False); - end; - end else Res := DoOperation(AFileRec, NewFilePath, ErrorKind, False); - end; + end else Res := DoOperation(AFileRec, NewFilePath, False, @Error); + end; // Error handling - if (Res <> 0) and (not SkipAll) then begin - if (JobType in [WORKER_JOB_COPY, WORKER_JOB_EXTRACT_TO_TEMP]) then cap := LANGCopy - else cap := LANGMove; - // * TODO: port to GError -{ case Res of - ERRCreateLink: begin - s1 := LANGTheSymbolicLink; - if ErrorKind = 0 then s3 := LANGCouldNotBeCreated else - s3 := Format(LANGCouldNotBeCreatedS, [GetErrorString(ErrorKind)]); - end; - ERRMkDir: begin - s1 := LANGTheDirectory; - if ErrorKind = 0 then s3 := LANGCouldNotBeCreated else - s3 := Format(LANGCouldNotBeCreatedS, [GetErrorString(ErrorKind)]); - end; - ERRRemove: begin - if AFileRec^.DataItem^.IsDir then s1 := LANGTheDirectory else - if AFileRec^.DataItem^.IsLnk then s1 := LANGTheSymbolicLink else - s1 := LANGTheFile; - if ErrorKind = 0 then s3 := LANGCouldNotBeDeleted else - s3 := Format(LANGCouldNotBeDeletedS, [GetErrorString(ErrorKind)]); - end; - ERRCopyMove: begin - if ParamBool3 then s1 := LANGCannotCopyFile else - s1 := LANGCannotMoveFile; - if ErrorKind = 0 then s3 := '' else - s3 := GetErrorString(ErrorKind); - end; - end; } - Response := ShowDirDeleteDialog(DIR_DELETE_SET_DELETE_ERROR, s1, StrToUTF8(String(NewFilePath)), nil); + if (not Res) and (not FCopySkipAllErrors) then begin + if FCopyErrorHandledInProgress then begin + if FCopySilentCancel then Result := False; // Break the processing + Exit; + end; + GetCopyProgressErrorLabels(Self, Error, s, s2); + s3 := StrToUTF8(Error^.message); + if (Error^.domain = TUXCMD_ERROR) then + case TuxcmdErrorEnum(Error^.code) of + TUXCMD_ERROR_SYMLINK: begin + s := LANGTheSymbolicLink; + s2 := FCopyDestFile; + s3 := Format(LANGCouldNotBeCreatedS, [StrToUTF8(Error^.message)]); + end; + TUXCMD_ERROR_MKDIR: begin + s := LANGTheDirectory; + s2 := FCopyDestFile; + s3 := Format(LANGCouldNotBeCreatedS, [StrToUTF8(Error^.message)]); + end; + TUXCMD_ERROR_REMOVE: begin + if AFileRec^.DataItem^.IsDir then s := LANGTheDirectory else + if AFileRec^.DataItem^.IsLnk then s := LANGTheSymbolicLink else + s := LANGTheFile; + s2 := FCopyDestFile; + s3 := Format(LANGCouldNotBeDeletedS, [StrToUTF8(Error^.message)]); + end; + end; + Response := ShowDirDeleteDialog(DIR_DELETE_SET_DELETE_ERROR, s, s2, s3); case Response of DIR_DELETE_SKIP : Result := True; // Skip DIR_DELETE_RETRY : Result := HandleCopy(AFileRec, NewFilePath); // Retry DIR_DELETE_SKIP_ALL : begin // Skip All - SkipAll := True; + FCopySkipAllErrors := True; Result := True; end; 0, 124, 255 : Result := False; // Cancel + else begin + Result := False; + end; end; end; // DebugMsg(['(II) CopyFilesWorker.HandleCopy: finished']); @@ -1417,7 +1432,6 @@ var DefResponse: integer; // Global variables for this function if Info = nil then begin InaccessiblePaths.Add(FullPath); DebugMsg(['$$$ Copy: Something went wrong while building the filelist...']); - // TODO: ? ErrorHappened := True; end else begin Info^.ADestination := strdup(PChar(s)); @@ -1426,17 +1440,18 @@ var DefResponse: integer; // Global variables for this function end; end else InputFiles.Add(FullPath); end; - FillDirFiles(SrcEngine, AList, InputFiles, False, True, InaccessiblePaths); + FillDirFiles(SrcEngine, AList, InputFiles, False, True, InaccessiblePaths, @FCancelled); InputFiles.Free; end; var i: longint; List: TList; InaccessiblePaths: TStringList; + SkipInaccessible: boolean; CurrPath, SaveDestPath, SaveSrcPath, s: string; StartPassed: boolean; - SkipInaccessible: boolean; begin + FCopySilentCancel := False; List := TList.Create; InaccessiblePaths := TStringList.Create; ErrorHappened := False; @@ -1448,13 +1463,13 @@ begin // Prepare list of files to copy if JobType = WORKER_JOB_EXTRACT_TO_TEMP then begin - if not ExtractFromVFSAll then HandleProcessPattern(List, InaccessiblePaths, CurrPath, ExtractFile, ExtractFileName(ExtractFile), False, False) - else begin - SaveSrcPath := IncludeTrailingPathDelimiter(SrcEngine.Path); - SrcEngine.SetPath('/'); - CurrPath := '/'; - HandleProcessPattern(List, InaccessiblePaths, '/', '/', '', True, False); - end; + if not ExtractFromVFSAll then HandleProcessPattern(List, InaccessiblePaths, CurrPath, ExtractFile, ExtractFileName(ExtractFile), False, False) + else begin + SaveSrcPath := IncludeTrailingPathDelimiter(SrcEngine.Path); + SrcEngine.SetPath('/'); + CurrPath := '/'; + HandleProcessPattern(List, InaccessiblePaths, '/', '/', '', True, False); + end; end else if QuickRenameDataItem <> nil then begin // Quick-Rename with QuickRenameDataItem^ do @@ -1482,6 +1497,8 @@ begin end; end; + // * TODO: cancellation check + { if DestEngine.ChangeDir(CurrPath) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour may occur.']); if SrcEngine.ChangeDir(CurrPath) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour may occur.']); } DebugWriteListSL(List); @@ -1509,7 +1526,6 @@ begin CommitGUIUpdate; DefResponse := 0; - SkipAll := False; FCopySkipAllErrors := False; if List.Count > 0 then begin @@ -1530,12 +1546,14 @@ begin // DebugMsg(['s2 = ', Copy(PDataItemSL(List[i])^.AName, Length(CurrPath) + 1, Length(PDataItemSL(List[i])^.AName) - Length(CurrPath)), ', s = ', s]); end; - if not (SrcEngine is TVFSEngine) then UpdateCaption1(Format(LANGFromS, [string(PDataItemSL(List[i])^.DataItem^.FDisplayName)])) else - if (SrcEngine as TVFSEngine).ArchiveMode then UpdateCaption1(Format(LANGFromS, [Format(ConstFullPathFormatStr, [(SrcEngine as TVFSEngine).ArchivePath, string(PDataItemSL(List[i])^.DataItem^.FDisplayName)])])) - else UpdateCaption1(Format(LANGFromS, [GetURIPrefix((SrcEngine as TVFSEngine).GetPathURI) + StrToUTF8(string(PDataItemSL(List[i])^.DataItem^.FDisplayName))])); - if not (DestEngine is TVFSEngine) then UpdateCaption2(Format(LANGToS, [StrToUTF8(s)])) else - if (DestEngine as TVFSEngine).ArchiveMode then UpdateCaption2(Format(LANGToS, [Format(ConstFullPathFormatStr, [(DestEngine as TVFSEngine).ArchivePath, StrToUTF8(s)])])) - else UpdateCaption2(Format(LANGToS, [GetURIPrefix((DestEngine as TVFSEngine).GetPathURI) + StrToUTF8(s)])); + if not (SrcEngine is TVFSEngine) then FCopySourceFile := string(PDataItemSL(List[i])^.DataItem^.FDisplayName) else + if (SrcEngine as TVFSEngine).ArchiveMode then FCopySourceFile := Format(ConstFullPathFormatStr, [(SrcEngine as TVFSEngine).ArchivePath, string(PDataItemSL(List[i])^.DataItem^.FDisplayName)]) + else FCopySourceFile := GetURIPrefix((SrcEngine as TVFSEngine).GetPathURI) + StrToUTF8(string(PDataItemSL(List[i])^.DataItem^.FDisplayName)); + if not (DestEngine is TVFSEngine) then FCopyDestFile := StrToUTF8(s) else + if (DestEngine as TVFSEngine).ArchiveMode then FCopyDestFile := Format(ConstFullPathFormatStr, [(DestEngine as TVFSEngine).ArchivePath, StrToUTF8(s)]) + else FCopyDestFile := GetURIPrefix((DestEngine as TVFSEngine).GetPathURI) + StrToUTF8(s); + UpdateCaption1(Format(LANGFromS, [FCopySourceFile])); + UpdateCaption2(Format(LANGToS, [FCopyDestFile])); CommitGUIUpdate; if TwoSameFiles(s, string(PDataItemSL(List[i])^.DataItem^.FName), (JobType in [WORKER_JOB_COPY, WORKER_JOB_EXTRACT_TO_TEMP])) and (not PDataItemSL(List[i])^.DataItem^.IsDir) then begin FCancelMessage := LANGCannotCopyFileToItself; @@ -1547,7 +1565,7 @@ begin // if s <> string(PDataItemSL(List[i])^.DataItem^.FName) then if not HandleCopy(List[i], s) then begin ErrorHappened := True; - Break; + Break; // Cancelled end; if (not PDataItemSL(List[i])^.DataItem^.IsDir) and (not PDataItemSL(List[i])^.DataItem^.IsLnk) then FTotalDone := FTotalDone + PDataItemSL(List[i])^.DataItem^.Size; @@ -1596,42 +1614,39 @@ var FD: TEngineFileDes; PrivateCancel: boolean; SizeDone: Int64; TargetName: string; - Error: PGError; function PasteFile(FName: string): boolean; var FDR: TEngineFileDes; wCount: integer; Stat: PDataItem; + Error: PGError; begin Result := False; + Error := nil; if MergeHasInitialCRC then UpdateCaption2(Format(LANGToS, [StrToUTF8(FName)])) - else UpdateCaption1(Format(LANGFromS, [StrToUTF8(FName)])); + else UpdateCaption1(Format(LANGFromS, [StrToUTF8(FName)])); UpdateProgress1(0, '0 %'); CommitGUIUpdate; - // * TODO: check error Stat := AEngine.GetFileInfo(FName, True, True, nil); if not Assigned(Stat) then Exit; SetProgress1Params(Stat^.Size); FreeDataItem(Stat); - // * TODO: check error - Error := nil; FDR := AEngine.OpenFile(FName, omRead, @Error); if FDR = nil then Exit; repeat - // * TODO: check error Count := AEngine.ReadFile(FDR, Buffer, MergeBlockSize, @Error); if Error <> nil then begin AEngine.CloseFile(FD, nil); Exit; end; - // * TODO: check error wCount := AEngine.WriteFile(FD, Buffer, Count, @Error); if (Error <> nil) or (Count <> wCount) then begin FCancelMessage := Format(LANGAnErrorOccuredWhileWritingFileSS, [ExtractFileName(TargetName), Error^.message]); FShowCancelMessage := True; PrivateCancel := True; Result := True; // Fake this to don't show next disc dialog + g_error_free(Error); Exit; end; CurrentCRC := CRC32(CurrentCRC, Buffer, Count); @@ -1640,7 +1655,6 @@ var FD: TEngineFileDes; if MergeHasInitialCRC then UpdateProgress2(SizeDone, Format('%d %%', [Trunc(SizeDone / FProgress2Max * 100)])); CommitGUIUpdate; until (Count < MergeBlockSize) or FCancelled; - // * TODO: set real error, also free it AEngine.CloseFile(FDR, nil); Result := True; end; @@ -1649,7 +1663,9 @@ var FD: TEngineFileDes; var CurrFile, SourcePath, TargetFinalName: string; HasFinalCRC, b: boolean; Stat: PDataItem; + Error: PGError; begin + Error := nil; HasFinalCRC := MergeHasInitialCRC; TargetFinalName := MergeTargetFinalName; if (Length(MergeSourceFile) > 4) and (WideUpperCase(RightStr(MergeSourceFile, 4)) = '.CRC') @@ -1661,16 +1677,14 @@ begin if AEngine.FileExists(TargetName, False) then if ShowMessageBox(Format(LANGTheTargetFileSAlreadyExistsDoYouWantToOverwriteIt, [StrToUTF8(TargetName)]), [mbYes, mbNo], mbQuestion, mbNone, mbNo) = mbYes then begin - // * TODO: check error -{ Error := Ord(Engine.Remove(TargetName, nil)); - if Error <> 0 then begin - FCancelMessage := Format(LANGTheTargetFileSCannotBeRemovedS, [StrToUTF8(ExtractFileName(TargetName)), GetErrorString(Error)]); + if not AEngine.Remove(TargetName, @Error) then begin + FCancelMessage := Format(LANGTheTargetFileSCannotBeRemovedS, [StrToUTF8(ExtractFileName(TargetName)), Error^.message]); FShowCancelMessage := True; + g_error_free(Error); Exit; - end; } + end; end else Exit; - // * TODO: check error Stat := AEngine.GetFileInfo(MergeSourceFile, True, True, nil); if Assigned(Stat) then MergeBlockSize := ComputeBlockSize(Stat^.Size) else MergeBlockSize := 65536*4; @@ -1683,12 +1697,12 @@ begin FShowCancelMessage := True; Exit; end; - // * TODO: check error FD := AEngine.OpenFile(TargetName, omWrite, @Error); if Error <> nil then begin FCancelMessage := Format(LANGAnErrorOccuredWhileOpeningFileSS, [StrToUTF8(TargetName), Error^.message]); FShowCancelMessage := True; libc_free(Buffer); + g_error_free(Error); Exit; end; @@ -1723,8 +1737,16 @@ begin CurrFile := ''; end; until (SizeDone = MergeTargetSize) or FCancelled or PrivateCancel {or ((not b) and (not HasInitialCRC))} or (CurrFile = ''); - // * TODO: check error - if (not MergeHasInitialCRC) and HasFinalCRC then AEngine.RenameFile(TargetName, IncludeTrailingPathDelimiter(ExtractFilePath(TargetName)) + TargetFinalName, nil); + libc_free(Buffer); + if (not MergeHasInitialCRC) and HasFinalCRC then begin + if not AEngine.RenameFile(TargetName, IncludeTrailingPathDelimiter(ExtractFilePath(TargetName)) + TargetFinalName, @Error) then begin + FCancelMessage := Format(LANGAnErrorOccuredWhileWritingFileSS, [StrToUTF8(ExtractFileName(TargetName)), Error^.message]); + FShowCancelMessage := True; + g_error_free(Error); + AEngine.CloseFile(FD, nil); + Exit; + end; + end; if FCancelled and (not PrivateCancel) then begin FCancelMessage := LANGUserCancelled; FShowCancelMessage := True; @@ -1735,9 +1757,11 @@ begin then ShowMessageBox(Format(LANGMergeOfSSucceeded, [StrToUTF8(ExtractFileName(TargetFinalName))]), [mbOK], mbInfo, mbNone, mbOK) else ShowMessageBox(LANGWarningCreatedFileFailsCRCCheck, [mbOK], mbWarning, mbNone, mbOK); end else ShowMessageBox(Format(LANGMergeOfSSucceeded_NoCRCFileAvailable, [StrToUTF8(ExtractFileName(TargetFinalName))]), [mbOK], mbInfo, mbNone, mbOK); - // * TODO: set real error, also free it - AEngine.CloseFile(FD, nil); - libc_free(Buffer); + if not AEngine.CloseFile(FD, @Error) then begin + FCancelMessage := Format(LANGAnErrorOccuredWhileWritingFileSS, [StrToUTF8(ExtractFileName(TargetName)), Error^.message]); + FShowCancelMessage := True; + g_error_free(Error); + end; end; @@ -1748,12 +1772,12 @@ end; procedure TWorkerThread.SplitFilesWorker; const SplitBlockSize = 65536*4; var FD: TEngineFileDes; - Error: PGError; FileCRC: LongWord; Buffer: Pointer; PrivateCancel: boolean; FilePath: string; SizeDone, TDF, FileSize, CurrSize: Int64; + Error: PGError; function WriteSplitPart(TargetFile: string; PartSize: Int64; var Written: Int64): boolean; @@ -1762,9 +1786,8 @@ var FD: TEngineFileDes; begin Result := False; Written := 0; - // * TODO: check error - FDW := AEngine.OpenFile(TargetFile, omWrite, @Error); DebugMsg(['-- Opening file ', ExtractFileName(TargetFile), ', PartSize = ', PartSize]); + FDW := AEngine.OpenFile(TargetFile, omWrite, @Error); if Error <> nil then Exit; if SplitMaxSize > 0 then begin UpdateCaption2(Format(LANGToS, [StrToUTF8(TargetFile)])); @@ -1773,37 +1796,34 @@ var FD: TEngineFileDes; end else UpdateCaption1(Format(LANGToS, [StrToUTF8(TargetFile)])); CommitGUIUpdate; repeat - // * TODO: check error DebugMsg(['Seek to ', AEngine.FileSeek(FD, SizeDone + Written, @Error), ', Written = ', Written]); + if Error <> nil then begin + AEngine.CloseFile(FDW, nil); + Exit; + end; if Written + SplitBlockSize > PartSize then bl := PartSize - Written else bl := SplitBlockSize; - // * TODO: check error Count := AEngine.ReadFile(FD, Buffer, bl, @Error); if (Error <> nil) or (Count <> bl) then begin - // * TODO: set real error, also free it AEngine.CloseFile(FDW, nil); - DebugMsg(['Read Error: ', Error^.message, ', Count = ', Count, ', bl = ', bl]); -// if (Count <> bl) and (Error = 0) then Error := EIO; + if Error <> nil then + DebugMsg(['Read Error: ', Error^.message, ', Count = ', Count, ', bl = ', bl]); Exit; end; - // * TODO: check error wCount := AEngine.WriteFile(FDW, Buffer, Count, @Error); Written := Written + wCount; FileCRC := CRC32(FileCRC, Buffer, wCount); if (Error <> nil) or (Count <> wCount) then begin - // * TODO: set real error, also free it AEngine.CloseFile(FDW, nil); - // * TODO: check error - DebugMsg(['Write Error: ', Error^.message, ', Count = ', Count, ', wCount = ', wCount]); -// if (wCount <> Count) and (Error = 0) then Error := ENOSPC; + if Error <> nil then + DebugMsg(['Write Error: ', Error^.message, ', Count = ', Count, ', wCount = ', wCount]); Exit; end; UpdateProgress1(FProgress1Pos + wCount, Format('%d %%', [Trunc((FProgress1Pos + wCount) / FProgress1Max * 100)])); if SplitMaxSize > 0 then UpdateProgress2(FProgress2Pos + wCount, Format('%d %%', [Trunc((FProgress2Pos + wCount) / FProgress2Max * 100)])); CommitGUIUpdate; until (Written = PartSize) or FCancelled or PrivateCancel; - // * TODO: set real error, also free it - AEngine.CloseFile(FDW, nil); + if not AEngine.CloseFile(FDW, @Error) then Exit; DebugMsg(['-- Closing file ', ExtractFileName(TargetFile), ', PartSize = ', PartSize, ', Written = ', Written]); Result := True; end; @@ -1842,11 +1862,12 @@ var i: integer; x: Int64; xx: string; begin - // * TODO: check error - Stat := AEngine.GetFileInfo(SplitSourceFile, True, True, nil); + Error := nil; + Stat := AEngine.GetFileInfo(SplitSourceFile, True, True, @Error); if not Assigned(Stat) then begin - FCancelMessage := Format(LANGCannotOpenFileS, [StrToUTF8(SplitSourceFile)]); + FCancelMessage := Format('Cannot open file ''%s'': %s', [StrToUTF8(SplitSourceFile), Error^.message]); FShowCancelMessage := True; + g_error_free(Error); Exit; end; if (SplitMaxSize > 0) and (Stat^.Size > SplitMaxSize * 999) then begin @@ -1868,11 +1889,11 @@ begin FShowCancelMessage := True; Exit; end; - // * TODO: check error FD := AEngine.OpenFile(SplitSourceFile, omRead, @Error); if Error <> nil then begin FCancelMessage := Format(LANGAnErrorOccuredWhileOpeningFileSS, [StrToUTF8(SplitSourceFile), Error^.message]); libc_free(Buffer); + g_error_free(Error); Exit; end; FilePath := IncludeTrailingPathDelimiter(ProcessPattern(AEngine, SplitTargetPath, AEngine.Path, '', True)); @@ -1900,9 +1921,8 @@ begin for i := List.Count - 1 downto 0 do FreeDataItem(PDataItem(List[i])); List.Clear; - // * TODO: check error -{ Error := Engine.GetListing(List, FilePath, ConfShowDotFiles, False, False, nil); - if (Error = 0) and (List.Count > 0) then begin + b := AEngine.GetListing(List, FilePath, ConfShowDotFiles, False, False, nil); + if b and (List.Count > 0) then begin st := ''; if List.Count < 6 then begin for i := 0 to List.Count - 1 do @@ -1911,23 +1931,26 @@ begin end else b := ShowMessageBox(Format(LANGThereAreDFilesInTheTargetDirectoryDoYouWantToDeleteThem, [List.Count]), [mbYes, mbNo], mbQuestion, mbNone, mbNo) = mbYes; if b then for i := 0 to List.Count - 1 do begin - Error := Engine.Remove(IncludeTrailingPathDelimiter(FilePath) + string(PDataItem(List[i])^.FName)); - if Error <> 0 then ShowMessageBox(Format(LANGTheTargetFileSCannotBeRemovedS, [StrToUTF8(IncludeTrailingPathDelimiter(FilePath)) + string(PDataItem(List[i])^.FDisplayName), GetErrorString(Error)]), [mbOK], mbError, mbNone, mbOK); + if not AEngine.Remove(IncludeTrailingPathDelimiter(FilePath) + string(PDataItem(List[i])^.FName), @Error) then begin + ShowMessageBox(Format(LANGTheTargetFileSCannotBeRemovedS, [StrToUTF8(IncludeTrailingPathDelimiter(FilePath)) + string(PDataItem(List[i])^.FDisplayName), Error^.message]), [mbOK], mbError, mbNone, mbOK); + g_error_free(Error); + Error := nil; + end; end; - end; } + end; except end; // Test for target file existence if AEngine.FileExists(IncludeTrailingPathDelimiter(FilePath) + FileName, False) then begin b := ShowMessageBox(Format(LANGTheTargetFileSAlreadyExistsDoYouWantToOverwriteIt, [StrToUTF8(IncludeTrailingPathDelimiter(FilePath) + FileName)]), [mbYes, mbNo], mbQuestion, mbNone, mbNo) = mbYes; if b then begin - // * TODO: check error -{ Error := Engine.Remove(IncludeTrailingPathDelimiter(FilePath) + FileName); - if Error <> 0 then begin - FCancelMessage := Format(LANGTheTargetFileSCannotBeRemovedS, [StrToUTF8(IncludeTrailingPathDelimiter(FilePath) + FileName), GetErrorString(Error)]); + if not AEngine.Remove(IncludeTrailingPathDelimiter(FilePath) + FileName, @Error) then begin + FCancelMessage := Format(LANGTheTargetFileSCannotBeRemovedS, [StrToUTF8(IncludeTrailingPathDelimiter(FilePath) + FileName), Error^.message]); FShowCancelMessage := True; PrivateCancel := True; + g_error_free(Error); + Error := nil; Break; - end; } + end; end else begin PrivateCancel := True; Break; @@ -1940,7 +1963,12 @@ begin if (CurrSize >= 512) and (TDF >= CurrSize) then begin b := WriteSplitPart(IncludeTrailingPathDelimiter(FilePath) + FileName, CurrSize, ws); if (not b) and (SplitMaxSize > 0) then begin - FCancelMessage := Format(LANGAnErrorOccuredWhileOperationS, [Error^.message]); + if Error <> nil then begin + FCancelMessage := Format(LANGAnErrorOccuredWhileOperationS, [Error^.message]); + g_error_free(Error); + Error := nil; + end else + FCancelMessage := Format(LANGAnErrorOccuredWhileOperationS, ['(unknown)']); FShowCancelMessage := True; PrivateCancel := True; Break; @@ -1977,7 +2005,6 @@ begin FShowCancelMessage := True; end; end; - // * TODO: set real error, also free it AEngine.CloseFile(FD, nil); if List.Count > 0 then for i := List.Count - 1 downto 0 do @@ -1999,31 +2026,25 @@ var SkipAll: boolean; Error: PGError; begin Result := True; + Error := nil; // DebugMsg(['Chmod Debug: IsDir: ', AFileRec^.IsDir, ', Stage1: ', AFileRec^.Stage1, ', IsLnk: ', AFileRec^.IsLnk, '; Result = ', AFileRec^.IsDir and AFileRec^.Stage1 and (not AFileRec^.IsLnk)]); if AFileRec^.DataItem^.IsDir and (ChmodRecurseType >= 0) and AFileRec^.Stage1 and (not AFileRec^.DataItem^.IsLnk) then Exit; if (not AFileRec^.DataItem^.IsDir) and (ChmodRecurseType >= 0) and (ChmodRecurseType = 1) then Exit; // Directories only if AFileRec^.DataItem^.IsDir and (ChmodRecurseType >= 0) and (ChmodRecurseType = 2) then Exit; // Files only - // * TODO: check error - Error := nil; Res := AEngine.Chmod(String(AFileRec^.DataItem^.FName), ChmodMode, @Error); -// DebugMsg(['Result : ', Res]); - if not Res then - if SkipAll then Result := True else - begin - // * TODO: check error - Response := ShowDirDeleteDialog(DIR_DELETE_SET_DELETE_ERROR, 'Error changing permissions', String(AFileRec^.DataItem^.FDisplayName), Error); - if Error <> nil then - g_error_free(Error); + if not Res then begin + if SkipAll then Result := True + else begin + Response := ShowDirDeleteDialog(DIR_DELETE_SET_DELETE_ERROR, 'Error changing permissions', AFileRec^.DataItem^.FDisplayName, Error^.message); + g_error_free(Error); case Response of DIR_DELETE_SKIP : Result := True; - DIR_DELETE_SKIP_ALL : begin - SkipAll := True; - Result := True; - end; + DIR_DELETE_SKIP_ALL : begin SkipAll := True; Result := True; end; DIR_DELETE_RETRY : Result := HandleChmod(AFileRec); else Result := False; end; end; + end; end; var i: longint; @@ -2086,30 +2107,24 @@ var SkipAll: boolean; Error: PGError; begin Result := True; + Error := nil; // DebugMsg(['Chown Debug: IsDir: ', AFileRec^.IsDir, ', Stage1: ', AFileRec^.Stage1, ', IsLnk: ', AFileRec^.IsLnk, '; Result = ', AFileRec^.IsDir and AFileRec^.Stage1 and (not AFileRec^.IsLnk)]); if (AFileRec^.DataItem^.IsDir and ChownRecursive and AFileRec^.Stage1 and (not AFileRec^.DataItem^.IsLnk)) or ((not AFileRec^.DataItem^.IsDir) and ChownRecursive) then Exit; - // * TODO: check error - Error := nil; Res := AEngine.Chown(String(AFileRec^.DataItem^.FName), ChownUID, ChownGID, @Error); -// DebugMsg(['Result : ', Res]); - if not Res then - if SkipAll then Result := True else - begin - // * TODO: check error - Response := ShowDirDeleteDialog(DIR_DELETE_SET_DELETE_ERROR, 'Error changing owner', String(AFileRec^.DataItem^.FDisplayName), Error); - if Error <> nil then - g_error_free(Error); + if not Res then begin + if SkipAll then Result := True + else begin + Response := ShowDirDeleteDialog(DIR_DELETE_SET_DELETE_ERROR, 'Error changing owner', AFileRec^.DataItem^.FDisplayName, Error^.message); + g_error_free(Error); case Response of DIR_DELETE_SKIP : Result := True; - DIR_DELETE_SKIP_ALL : begin - SkipAll := True; - Result := True; - end; + DIR_DELETE_SKIP_ALL : begin SkipAll := True; Result := True; end; DIR_DELETE_RETRY : Result := HandleChown(AFileRec); else Result := False; end; end; + end; end; var i: longint; @@ -2246,11 +2261,17 @@ begin // AutoFallback loop repeat + if ChDirError <> nil then begin + g_error_free(ChDirError); + ChDirError := nil; + end; if Engine is TVFSEngine then Result := (Engine as TVFSEngine).ChangeDirEx(APath, @vfs_ask_question_callback, @vfs_ask_password_callback, nil, Self, @ChDirError) else Result := Engine.ChangeDir(APath, @ChDirError); - if not Result then - GoUp(APath); + if not Result then begin + PrefixTuxcmdError(@ChDirError, ExcludeTrailingPathDelimiter(APath)); + GoUp(APath); + end; until Result or (not AutoFallback) or (Length(APath) <= 1); if Result then Engine.Path := APath; @@ -2282,8 +2303,10 @@ begin if VFSOpenResult and (not FCancelled) then begin ChDirResult := ChangeDir(AEngine, APath, ASelItem, AAutoFallBack); - if ChDirResult and (not FCancelled) then + if ChDirResult and (not FCancelled) then begin ListingResult := AEngine.GetListing(ADirList, AEngine.GetPath, ConfShowDotFiles, True, False, @ListingError); + if not ListingResult then PrefixTuxcmdError(@ListingError, AEngine.GetPath); + end; end; except on E: Exception do DebugMsg(['*** Exception raised in TOpenDirThread.Execute (', E.ClassName, '): ', E.Message]); |
