From 7d757b8452daed2575e3a9cc300459d7e8674345 Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Fri, 11 Oct 2024 23:38:44 +0200 Subject: Full port to GError 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. --- UConfig.pas | 6 +- UCore.pas | 18 +- UCoreWorkers.pas | 781 ++++++++++++++++++++++++++++--------------------------- UDirDelete.pas | 6 +- UEngines.pas | 235 +++++++---------- UError.pas | 236 ++++++++++++++--- UGnome.pas | 26 +- vfs/UVFSCore.pas | 216 ++++++++------- 8 files changed, 851 insertions(+), 673 deletions(-) diff --git a/UConfig.pas b/UConfig.pas index a51591f..3c54cc5 100644 --- a/UConfig.pas +++ b/UConfig.pas @@ -19,14 +19,14 @@ *) unit UConfig; -interface +interface uses Classes, ULocale; resourcestring ConstAppTitle = 'Tux Commander'; - ConstAboutVersion = '0.6.80-dev'; - ConstAboutBuildDate = '2024-10-08'; + ConstAboutVersion = '0.6.81-dev'; + ConstAboutBuildDate = '2024-10-23'; {$IFDEF FPC} {$INCLUDE fpcver.inc} diff --git a/UCore.pas b/UCore.pas index bf5c012..ed89fd0 100644 --- a/UCore.pas +++ b/UCore.pas @@ -28,7 +28,7 @@ function FillPanel(List: TList; ListView: TGTKListView; Engine: TPanelEngine; Le procedure FindNextSelected(ListView: TGTKListView; DataList: TList; var Item1, Item2: string); procedure UnselectAll(ListView: TGTKListView; DataList: TList); -procedure FillDirFiles(Engine: TPanelEngine; DestList: TList; InputFiles: TStringList; DoNotRecurse, SortForStream: boolean; InaccessiblePaths: TStringList); +procedure FillDirFiles(Engine: TPanelEngine; DestList: TList; InputFiles: TStringList; DoNotRecurse, SortForStream: boolean; InaccessiblePaths: TStringList; CancelFlag: Pboolean); function GetFileInfoSL(Engine: TPanelEngine; const APath: string): PDataItemSL; procedure DebugWriteListSL(List: TList); @@ -348,7 +348,7 @@ end; (********************************************************************************************************************************) (********************************************************************************************************************************) -procedure FillDirFiles(Engine: TPanelEngine; DestList: TList; InputFiles: TStringList; DoNotRecurse, SortForStream: boolean; InaccessiblePaths: TStringList); +procedure FillDirFiles(Engine: TPanelEngine; DestList: TList; InputFiles: TStringList; DoNotRecurse, SortForStream: boolean; InaccessiblePaths: TStringList; CancelFlag: Pboolean); var DirStage1List, FilesList, DirStage2List: TList; function FillDirFiles_compare_func(Item1, Item2: Pointer): integer; @@ -418,6 +418,7 @@ var DirStage1List, FilesList, DirStage2List: TList; ParentDir: string; Error: PGError; begin + if (CancelFlag <> nil) and CancelFlag^ then Exit; LocalList := TList.Create; Error := nil; if Engine.GetListing(LocalList, LocalPath, True, False, True, @Error) then begin @@ -603,11 +604,14 @@ end; (********************************************************************************************************************************) function MakeDirectory(ListView: TGTKListView; Engine: TPanelEngine; LeftPanel: boolean; NewDir: string): boolean; var Error: PGError; + Dir: string; begin Error := nil; - Result := Engine.MakeDir(IncludeTrailingPathDelimiter(Engine.Path) + NewDir, @Error); + Dir := IncludeTrailingPathDelimiter(Engine.Path) + NewDir; + Result := Engine.MakeDir(Dir, @Error); if not Result then begin - ShowError(FMain, Format('Error creating new directory ''%s'' in %s panel', [StrToUTF8(NewDir), LANGPanelStrings[LeftPanel]]), Error); + PrefixTuxcmdError(@Error, StrToUTF8(Dir)); + ShowError(FMain, 'Error creating new directory', Error); g_error_free(Error); end; end; @@ -632,8 +636,8 @@ var AFSymLink: TFSymlink; FDirDelete.Label3.Caption := Format(LANGCouldNotBeCreatedS, [Error^.message]); FDirDelete.Label3.Visible := True; Response := Integer(FDirDelete.Run); - g_error_free(Error); finally + g_error_free(Error); FDirDelete.Free; end; case Response of @@ -684,8 +688,8 @@ var Data: PDataItem; FDirDelete.Label3.Caption := Format(LANGCouldNotBeDeletedS, [Error^.message]); FDirDelete.Label3.Visible := True; Response := Integer(FDirDelete.Run); - g_error_free(Error); finally + g_error_free(Error); FDirDelete.Free; end; case Response of @@ -706,8 +710,8 @@ var Data: PDataItem; FDirDelete.Label3.Caption := Format(LANGCouldNotBeCreatedS, [Error^.message]); FDirDelete.Label3.Visible := True; Response := Integer(FDirDelete.Run); - g_error_free(Error); finally + g_error_free(Error); FDirDelete.Free; end; case Response of 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]); diff --git a/UDirDelete.pas b/UDirDelete.pas index 8d22d42..3a3b4c5 100644 --- a/UDirDelete.pas +++ b/UDirDelete.pas @@ -22,7 +22,7 @@ unit UDirDelete; interface uses - SysUtils, Types, Classes, Variants, GTKControls, GTKForms, GTKStdCtrls, GTKExtCtrls, GTKConsts; + SysUtils, Classes, Variants, GTKControls, GTKForms, GTKStdCtrls, GTKConsts; const DIR_DELETE_CANCEL = 0; DIR_DELETE_DELETE = 1; @@ -60,10 +60,10 @@ begin WindowPosition := wpCenter; Caption := ''; Label1 := TGTKLabel.Create(Self); - Label1.Caption := 'The directory /tmp is not empty!'; + Label1.UseMarkup := True; Label2 := TGTKLabel.Create(Self); - Label2.Caption := LANGDoYouWantToDeleteItWithAllItsFilesAndSubdirectories; Label3 := TGTKLabel.Create(Self); + Label3.UseMarkup := True; Label3.Visible := False; ClientArea.AddControlEx(Label1, True, True, 0); ClientArea.AddControlEx(Label2, True, True, 0); diff --git a/UEngines.pas b/UEngines.pas index 4891314..8a0e9e6 100644 --- a/UEngines.pas +++ b/UEngines.pas @@ -29,8 +29,8 @@ const omRead = 0; omAppend = 2; ConfDefaultDirCreationMask = 755; - - + + type PDataItem = ^TDataItem; TDataItem = record @@ -103,8 +103,8 @@ type // Copy-related routines function GetBlockSize: guint32; virtual; abstract; procedure SetBlockSize(Value: guint32); virtual; abstract; - function CopyFileIn(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer): boolean; virtual; abstract; // returns True if file is successfully copied - function CopyFileOut(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer): boolean; virtual; abstract; // returns True if file is successfully copied + function CopyFileIn(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer; Error: PPGError): boolean; virtual; abstract; // returns True if file is successfully copied + function CopyFileOut(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer; Error: PPGError): boolean; virtual; abstract; // returns True if file is successfully copied function IsOnSameFS(const Path1, Path2: string; FollowSymlinks: boolean): boolean; virtual; abstract; function TwoSameFiles(const Path1, Path2: string; FollowSymlinks: boolean): boolean; virtual; abstract; @@ -153,8 +153,8 @@ type function GetBlockSize: guint32; override; procedure SetBlockSize(Value: guint32); override; - function CopyFileIn(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer): boolean; override; - function CopyFileOut(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer): boolean; override; + function CopyFileIn(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer; Error: PPGError): boolean; override; + function CopyFileOut(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer; Error: PPGError): boolean; override; function IsOnSameFS(const Path1, Path2: string; FollowSymlinks: boolean): boolean; override; function TwoSameFiles(const Path1, Path2: string; FollowSymlinks: boolean): boolean; override; @@ -235,26 +235,21 @@ var Item: PDataItem; Handle: PDIR; DirEnt: PDirent64; Buf: PChar; - saved_errno: integer; - FError: PGError; begin Result := False; try if libc_chdir(PChar(APath)) <> 0 then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error changing directory to ''''%s'''': %s', StrToUTF8(PChar(APath)), g_strerror(saved_errno)); - DebugMsg(['*** TLocalTreeEngine.GetListing(APath=', APath, '): chdir error: ', strerror(saved_errno)]); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_CHDIR), '%m'); + DebugMsg(['*** TLocalTreeEngine.GetListing(APath=', APath, '): chdir error: ', strerror(errno)]); Exit; end; Handle := opendir(PChar(APath)); if Handle = nil then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error opening directory ''''%s'''': %s', StrToUTF8(PChar(APath)), g_strerror(saved_errno)); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_OPENDIR), '%m'); DebugMsg(['*** TLocalTreeEngine.GetListing(APath=', APath, '): opendir() handle == NULL: ', strerror(errno)]); Exit; end; - FError := nil; repeat DirEnt := readdir64(Handle); if (DirEnt <> nil) and (DirEnt^.d_name[0] <> #0) then begin @@ -269,14 +264,13 @@ begin List.Add(Item); end; end; - until (DirEnt = nil) or (FError <> nil); + until (DirEnt = nil); closedir(Handle); - - if FError <> nil then g_propagate_error(Error, FError) - else Result := True; + Result := True; except on E: Exception do begin Result := False; + tuxcmd_set_error_from_exception(Error, E); DebugMsg(['*** TLocalTreeEngine.GetListing(APath=', APath, ') -Exception: ', E.Message]); Exit; end; @@ -288,14 +282,12 @@ var Item: PDataItem; StatBuf: Pstat64; LnkBuf: array[0..65535] of char; i: integer; - saved_errno: integer; begin StatBuf := malloc(sizeof(Tstat64)); memset(StatBuf, 0, sizeof(Tstat64)); if lstat64(PChar(APath), StatBuf) <> 0 then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error getting file info for ''''%s'''': %s', StrToUTF8(PChar(APath)), g_strerror(saved_errno)); - DebugMsg(['*** TLocalTreeEngine.GetFileInfo(APath=', APath, '): Error reading file via lstat64: ', strerror(saved_errno)]); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_STAT), '%m'); + DebugMsg(['*** TLocalTreeEngine.GetFileInfo(APath=', APath, '): Error reading file via lstat64: ', strerror(errno)]); libc_free(StatBuf); Result := nil; Exit; @@ -388,20 +380,17 @@ end; function TLocalTreeEngine.ChangeDir(const NewPath: string; Error: PPGError): boolean; var APath: string; Handle : PDIR; - saved_errno: integer; begin Result := False; try APath := IncludeTrailingPathDelimiter(NewPath); if libc_chdir(PChar(APath)) <> 0 then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error changing directory to ''''%s'''': %s', StrToUTF8(PChar(NewPath)), g_strerror(saved_errno)); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_CHDIR), '%m'); Exit; end; Handle := opendir(PChar(APath)); if Handle = nil then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error changing directory to ''''%s'''': %s', StrToUTF8(PChar(NewPath)), g_strerror(saved_errno)); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_OPENDIR), '%m'); Exit; end; { if not Assigned(readdir(Handle)) then begin @@ -409,14 +398,13 @@ begin Exit; end; } if closedir(Handle) <> 0 then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error changing directory to ''''%s'''': %s', StrToUTF8(PChar(NewPath)), g_strerror(saved_errno)); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_OPENDIR), '%m'); Exit; end; Result := True; except on E: Exception do begin - Result := False; + tuxcmd_set_error_from_exception(Error, E); DebugMsg(['*** TLocalTreeEngine.ChangeDir(APath=', APath, ') -Exception: ', E.Message]); Exit; end; @@ -476,76 +464,63 @@ end; (********************************************************************************************************************************) function TLocalTreeEngine.MakeDir(const NewDir: string; Error: PPGError): boolean; -var saved_errno: integer; begin Result := False; if __mkdir(PChar(NewDir), OctalToAttr(ConfDefaultDirCreationMask)) <> 0 then begin - if Self.DirectoryExists(NewDir, False) or (@g_mkdir_with_parents = nil) or (g_mkdir_with_parents(PChar(NewDir), OctalToAttr(ConfDefaultDirCreationMask)) <> 0) then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error creating directory ''''%s'''': %s', StrToUTF8(PChar(NewDir)), g_strerror(saved_errno)); - end else Result := True; + if Self.DirectoryExists(NewDir, False) or (@g_mkdir_with_parents = nil) or (g_mkdir_with_parents(PChar(NewDir), OctalToAttr(ConfDefaultDirCreationMask)) <> 0) + then g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_MKDIR), '%m') + else Result := True; end else Result := True; end; (********************************************************************************************************************************) function TLocalTreeEngine.Remove(const APath: string; Error: PPGError): boolean; -var saved_errno: integer; begin Result := False; if libc_remove(PChar(ExcludeTrailingPathDelimiter(APath))) <> 0 then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error deleting ''''%s'''': %s', StrToUTF8(PChar(APath)), g_strerror(saved_errno)); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_REMOVE), '%m'); end else Result := True; end; (********************************************************************************************************************************) function TLocalTreeEngine.MakeSymLink(const NewFileName, PointTo: string; Error: PPGError): boolean; -var saved_errno: integer; begin Result := False; if symlink(PChar(PointTo), PChar(NewFileName)) <> 0 then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error creating symlink ''''%s'''': %s', StrToUTF8(PChar(NewFileName)), g_strerror(saved_errno)); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_SYMLINK), '%m'); end else Result := True; end; (********************************************************************************************************************************) function TLocalTreeEngine.Chmod(const FileName: string; Mode: cuLong; Error: PPGError): boolean; -var saved_errno: integer; begin Result := False; if libc_chmod(PChar(FileName), Mode) <> 0 then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error during chmod of ''''%s'''': %s', StrToUTF8(PChar(FileName)), g_strerror(saved_errno)); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_CHMOD), '%m'); end else Result := True; end; (********************************************************************************************************************************) function TLocalTreeEngine.Chown(const FileName: string; UID, GID: cuLong; Error: PPGError): boolean; -var saved_errno: integer; begin Result := False; if libc_chown(PChar(FileName), UID, GID) <> 0 then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error during chown of ''''%s'''': %s', StrToUTF8(PChar(FileName)), g_strerror(saved_errno)); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_CHOWN), '%m'); end else Result := True; end; (********************************************************************************************************************************) function TLocalTreeEngine.RenameFile(const SourceFile, DestFile: string; Error: PPGError): boolean; -var saved_errno: integer; begin Result := False; if libc_rename(PChar(SourceFile), PChar(DestFile)) <> 0 then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error renaming file ''''%s'''': %s', StrToUTF8(PChar(SourceFile)), g_strerror(saved_errno)); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_RENAME), '%m'); end else Result := True; end; (********************************************************************************************************************************) function TLocalTreeEngine.ChangeTimes(const APath: string; mtime, atime: time_t; Error: PPGError): boolean; var timebuf: Putimbuf; - saved_errno: integer; begin Result := False; try @@ -554,87 +529,85 @@ begin timebuf^.actime := atime; timebuf^.modtime := mtime; if utime(PChar(APath), timebuf) <> 0 then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error while changing timestamps of ''''%s'''': %s', StrToUTF8(PChar(APath)), g_strerror(saved_errno)); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_TIMESTAMPS), '%m'); end else Result := True; libc_free(timebuf); except - on E: Exception do DebugMsg(['*** Exception raised in TLocalTreeEngine.ChangeTimes(APath=', APath, '): (', E.ClassName, '): ', E.Message]); + on E: Exception do begin + tuxcmd_set_error_from_exception(Error, E); + DebugMsg(['*** Exception raised in TLocalTreeEngine.ChangeTimes(APath=', APath, '): (', E.ClassName, '): ', E.Message]); + Result := False; + end; end; end; (********************************************************************************************************************************) -function TLocalTreeEngine.CopyFileIn(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer): boolean; +function TLocalTreeEngine.CopyFileIn(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer; Error: PPGError): boolean; begin - Result := CopyFileOut(SourceFile, DestFile, Append, ProgressFunc, Sender); + Result := CopyFileOut(SourceFile, DestFile, Append, ProgressFunc, Sender, Error); end; -function TLocalTreeEngine.CopyFileOut(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer): boolean; +function TLocalTreeEngine.CopyFileOut(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer; Error: PPGError): boolean; var fsrc, fdest: PFILE; Buffer: Pointer; BytesDone, BytesRead, BytesWritten: Int64; - Ignore: boolean; - saved_errno: integer; - Error: PGError; begin Result := False; - Error := nil; + BytesDone := 0; try // Open source file for reading fsrc := fopen64(PChar(SourceFile), 'r'); if fsrc = nil then begin - if @ProgressFunc <> nil then begin - saved_errno := errno; - g_set_error(@Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error opening source file ''''%s'''': %s', StrToUTF8(PChar(SourceFile)), g_strerror(saved_errno)); - ProgressFunc(Sender, 0, Error); - g_error_free(Error); - end; + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_SOURCE_OPEN), '%m'); + if @ProgressFunc <> nil then + ProgressFunc(Sender, 0, Error^); Exit; end; // Open target file for writing/appending if Append then fdest := fopen64(PChar(DestFile), 'a') else fdest := fopen64(PChar(DestFile), 'w'); - if fsrc = nil then begin - if @ProgressFunc <> nil then begin - saved_errno := errno; - g_set_error(@Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error opening target file ''''%s'''': %s', StrToUTF8(PChar(DestFile)), g_strerror(saved_errno)); - ProgressFunc(Sender, 0, Error); - g_error_free(Error); - end; + if fdest = nil then begin + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_TARGET_OPEN), '%m'); fclose(fsrc); + if @ProgressFunc <> nil then + ProgressFunc(Sender, 0, Error^); Exit; end; - BytesDone := 0; Buffer := malloc(FBlockSize); + if Buffer = nil then begin + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_ALLOC_FAILED), '%m'); + fclose(fdest); + fclose(fsrc); + if @ProgressFunc <> nil then + ProgressFunc(Sender, 0, Error^); + Exit; + end; while feof(fsrc) = 0 do begin - Error := nil; - // Read block BytesRead := fread(Buffer, 1, FBlockSize, fsrc); if (BytesRead < FBlockSize) and (feof(fsrc) = 0) then begin - Ignore := False; - if @ProgressFunc <> nil then begin - saved_errno := errno; - g_set_error(@Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error reading from source file ''''%s'''': %s', StrToUTF8(PChar(SourceFile)), g_strerror(saved_errno)); - Ignore := ProgressFunc(Sender, BytesDone + BytesRead, Error); - g_error_free(Error); - end; - if Ignore then Continue - else Break; + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_SOURCE_READ), '%m'); + Result := False; + if @ProgressFunc <> nil then + Result := ProgressFunc(Sender, BytesDone + BytesRead, Error^); + if Result then begin + // user has chosen to ignore the error + g_error_free(Error^); + Error^ := nil; + Continue; + end else Break; end; // Write block BytesWritten := fwrite(Buffer, 1, BytesRead, fdest); if BytesWritten < BytesRead then begin - if @ProgressFunc <> nil then begin - saved_errno := ferror(fdest); - g_set_error(@Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error writing to target file ''''%s'''': %s', StrToUTF8(PChar(DestFile)), g_strerror(saved_errno)); - ProgressFunc(Sender, BytesDone + BytesWritten, Error); - g_error_free(Error); - end; + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_TARGET_WRITE), '%m'); + Result := False; + if @ProgressFunc <> nil then + ProgressFunc(Sender, BytesDone + BytesRead, Error^); Break; // We cannot ignore write errors end; @@ -642,34 +615,41 @@ begin if (@ProgressFunc <> nil) and (not ProgressFunc(Sender, BytesDone, nil)) then Break; end; - Result := feof(fsrc) <> 0; + Result := (feof(fsrc) <> 0) and (Error^ = nil); libc_free(Buffer); if fclose(fdest) <> 0 then begin - fclose(fsrc); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_TARGET_CLOSE), '%m'); Result := False; - if @ProgressFunc <> nil then begin - Error := nil; - saved_errno := errno; - g_set_error(@Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error closing target file ''''%s'''': %s', StrToUTF8(PChar(DestFile)), g_strerror(saved_errno)); - Result := ProgressFunc(Sender, BytesDone, Error); - g_error_free(Error); - end; + if @ProgressFunc <> nil then + ProgressFunc(Sender, BytesDone, Error^); + fclose(fsrc); Exit; end; if fclose(fsrc) <> 0 then begin + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_SOURCE_CLOSE), '%m'); Result := False; - if @ProgressFunc <> nil then begin - Error := nil; - saved_errno := errno; - g_set_error(@Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error closing source file ''''%s'''': %s', StrToUTF8(PChar(SourceFile)), g_strerror(saved_errno)); - Result := ProgressFunc(Sender, BytesDone, Error); - g_error_free(Error); - end; - Exit; + if @ProgressFunc <> nil then + Result := ProgressFunc(Sender, BytesDone, Error^); + if Result then begin + // user has chosen to ignore the error + g_error_free(Error^); + Error^ := nil; + end else Exit; end; except - on E: Exception do DebugMsg(['*** Exception raised in TLocalTreeEngine.CopyFile(Sender=', Sender, ', SourceFile=', SourceFile, ', DestFile=', DestFile, '): (', E.ClassName, '): ', E.Message]); + on E: Exception do begin + Result := False; + tuxcmd_set_error_from_exception(@Error, E); + DebugMsg(['*** Exception raised in TLocalTreeEngine.CopyFile(Sender=', Sender, ', SourceFile=', SourceFile, ', DestFile=', DestFile, '): (', E.ClassName, '): ', E.Message]); + if @ProgressFunc <> nil then + Result := ProgressFunc(Sender, BytesDone, Error^); + if Result then begin + // user has chosen to ignore the error + g_error_free(Error^); + Error^ := nil; + end; + end; end; end; @@ -802,7 +782,6 @@ end; (********************************************************************************************************************************) function TLocalTreeEngine.OpenFile(const APath: string; Mode: integer; Error: PPGError): TEngineFileDes; var m: PChar; - saved_errno: integer; begin case Mode of omRead: m := 'r'; @@ -811,54 +790,40 @@ begin else m := 'r'; end; Result := fopen64(PChar(APath), m); - if Result = nil then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error opening file ''''%s'''': %s', StrToUTF8(PChar(APath)), g_strerror(saved_errno)); - end; + if Result = nil then + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_OPEN_FILE), '%m'); end; (********************************************************************************************************************************) function TLocalTreeEngine.ReadFile(const FileDescriptor: TEngineFileDes; Buffer: Pointer; ABlockSize: integer; Error: PPGError): integer; -var saved_errno: integer; begin Result := fread(Buffer, 1, ABlockSize, FileDescriptor); - if (Result = 0) and (feof(FileDescriptor) = 0) then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error reading from file: %s', g_strerror(saved_errno)); - end; + if (Result = 0) and (feof(FileDescriptor) = 0) then + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_READ_FILE), '%m'); end; (********************************************************************************************************************************) function TLocalTreeEngine.WriteFile(const FileDescriptor: TEngineFileDes; Buffer: Pointer; BytesCount: integer; Error: PPGError): integer; -var saved_errno: integer; begin Result := fwrite(Buffer, 1, BytesCount, FileDescriptor); - if Result < BytesCount then begin - saved_errno := ferror(FileDescriptor); - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error writing to file: %s', g_strerror(saved_errno)); - end; + if Result < BytesCount then + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_WRITE_FILE), '%m'); end; (********************************************************************************************************************************) function TLocalTreeEngine.CloseFile(const FileDescriptor: TEngineFileDes; Error: PPGError): boolean; -var saved_errno: integer; begin Result := fclose(FileDescriptor) = 0; - if not Result then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error closing file: %s', g_strerror(saved_errno)); - end; + if not Result then + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_CLOSE_FILE), '%m'); end; (********************************************************************************************************************************) function TLocalTreeEngine.FileSeek(const FileDescriptor: TEngineFileDes; const AbsoluteOffset: Int64; Error: PPGError): Int64; -var saved_errno: integer; begin Result := fseeko64(FileDescriptor, AbsoluteOffset, SEEK_SET); - if Result = -1 then begin - saved_errno := errno; - g_set_error(Error, G_IO_ERROR, gint(g_io_error_from_errno(saved_errno)), 'Error seeking in file: %s', g_strerror(saved_errno)); - end; + if Result = -1 then + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_SEEK), '%m'); end; (********************************************************************************************************************************) diff --git a/UError.pas b/UError.pas index 6734367..d557189 100644 --- a/UError.pas +++ b/UError.pas @@ -23,54 +23,107 @@ interface uses glib2, gtk2, Classes, SysUtils, ULibc, GTKForms; +type TuxcmdErrorEnum = ( + TUXCMD_ERROR_CANCELLED, + TUXCMD_ERROR_EXCEPTION, + TUXCMD_ERROR_NOT_SUPPORTED, + // Basic IO + TUXCMD_ERROR_CHDIR, + TUXCMD_ERROR_OPENDIR, + TUXCMD_ERROR_STAT, + TUXCMD_ERROR_MKDIR, + TUXCMD_ERROR_REMOVE, + TUXCMD_ERROR_SYMLINK, + TUXCMD_ERROR_CHMOD, + TUXCMD_ERROR_CHOWN, + TUXCMD_ERROR_RENAME, + TUXCMD_ERROR_TIMESTAMPS, + // File IO + TUXCMD_ERROR_OPEN_FILE, + TUXCMD_ERROR_READ_FILE, + TUXCMD_ERROR_WRITE_FILE, + TUXCMD_ERROR_CLOSE_FILE, + TUXCMD_ERROR_SEEK, + // File copy + TUXCMD_ERROR_ALLOC_FAILED, + TUXCMD_ERROR_SOURCE_OPEN, + TUXCMD_ERROR_TARGET_OPEN, + TUXCMD_ERROR_SOURCE_READ, + TUXCMD_ERROR_TARGET_WRITE, + TUXCMD_ERROR_SOURCE_CLOSE, + TUXCMD_ERROR_TARGET_CLOSE +); -// Ported from gioerror.h - -type GIOErrorEnum = (G_IO_ERROR_FAILED, - G_IO_ERROR_NOT_FOUND, - G_IO_ERROR_EXISTS, - G_IO_ERROR_IS_DIRECTORY, - G_IO_ERROR_NOT_DIRECTORY, - G_IO_ERROR_NOT_EMPTY, - G_IO_ERROR_NOT_REGULAR_FILE, - G_IO_ERROR_NOT_SYMBOLIC_LINK, - G_IO_ERROR_NOT_MOUNTABLE_FILE, - G_IO_ERROR_FILENAME_TOO_LONG, - G_IO_ERROR_INVALID_FILENAME, - G_IO_ERROR_TOO_MANY_LINKS, - G_IO_ERROR_NO_SPACE, - G_IO_ERROR_INVALID_ARGUMENT, - G_IO_ERROR_PERMISSION_DENIED, - G_IO_ERROR_NOT_SUPPORTED, - G_IO_ERROR_NOT_MOUNTED, - G_IO_ERROR_ALREADY_MOUNTED, - G_IO_ERROR_CLOSED, - G_IO_ERROR_CANCELLED, - G_IO_ERROR_PENDING, - G_IO_ERROR_READ_ONLY, - G_IO_ERROR_CANT_CREATE_BACKUP, - G_IO_ERROR_WRONG_ETAG, - G_IO_ERROR_TIMED_OUT, - G_IO_ERROR_WOULD_RECURSE, - G_IO_ERROR_BUSY, - G_IO_ERROR_WOULD_BLOCK, - G_IO_ERROR_HOST_NOT_FOUND, - G_IO_ERROR_WOULD_MERGE, - G_IO_ERROR_FAILED_HANDLED, - G_IO_ERROR_TOO_MANY_OPEN_FILES, - G_IO_ERROR_NOT_INITIALIZED, - G_IO_ERROR_ADDRESS_IN_USE, - G_IO_ERROR_PARTIAL_INPUT, - G_IO_ERROR_INVALID_DATA); +// Ported from gioerror.h, glib-2.83.0 +type GIOErrorEnum = ( + G_IO_ERROR_FAILED, + G_IO_ERROR_NOT_FOUND, + G_IO_ERROR_EXISTS, + G_IO_ERROR_IS_DIRECTORY, + G_IO_ERROR_NOT_DIRECTORY, + G_IO_ERROR_NOT_EMPTY, + G_IO_ERROR_NOT_REGULAR_FILE, + G_IO_ERROR_NOT_SYMBOLIC_LINK, + G_IO_ERROR_NOT_MOUNTABLE_FILE, + G_IO_ERROR_FILENAME_TOO_LONG, + G_IO_ERROR_INVALID_FILENAME, + G_IO_ERROR_TOO_MANY_LINKS, + G_IO_ERROR_NO_SPACE, + G_IO_ERROR_INVALID_ARGUMENT, + G_IO_ERROR_PERMISSION_DENIED, + G_IO_ERROR_NOT_SUPPORTED, + G_IO_ERROR_NOT_MOUNTED, + G_IO_ERROR_ALREADY_MOUNTED, + G_IO_ERROR_CLOSED, + G_IO_ERROR_CANCELLED, + G_IO_ERROR_PENDING, + G_IO_ERROR_READ_ONLY, + G_IO_ERROR_CANT_CREATE_BACKUP, + G_IO_ERROR_WRONG_ETAG, + G_IO_ERROR_TIMED_OUT, + G_IO_ERROR_WOULD_RECURSE, + G_IO_ERROR_BUSY, + G_IO_ERROR_WOULD_BLOCK, + G_IO_ERROR_HOST_NOT_FOUND, + G_IO_ERROR_WOULD_MERGE, + G_IO_ERROR_FAILED_HANDLED, + G_IO_ERROR_TOO_MANY_OPEN_FILES, + G_IO_ERROR_NOT_INITIALIZED, + G_IO_ERROR_ADDRESS_IN_USE, + G_IO_ERROR_PARTIAL_INPUT, + G_IO_ERROR_INVALID_DATA, + G_IO_ERROR_DBUS_ERROR, + G_IO_ERROR_HOST_UNREACHABLE, + G_IO_ERROR_NETWORK_UNREACHABLE, + G_IO_ERROR_CONNECTION_REFUSED, + G_IO_ERROR_PROXY_FAILED, + G_IO_ERROR_PROXY_AUTH_FAILED, + G_IO_ERROR_PROXY_NEED_AUTH, + G_IO_ERROR_PROXY_NOT_ALLOWED, + G_IO_ERROR_BROKEN_PIPE, +// G_IO_ERROR_CONNECTION_CLOSED = G_IO_ERROR_BROKEN_PIPE, + G_IO_ERROR_NOT_CONNECTED, + G_IO_ERROR_MESSAGE_TOO_LARGE, + G_IO_ERROR_NO_SUCH_DEVICE, + G_IO_ERROR_DESTINATION_UNSET +); + +const G_IO_ERROR_CONNECTION_CLOSED = G_IO_ERROR_BROKEN_PIPE; + + +function tuxcmd_error_quark: TGQuark; +function TUXCMD_ERROR: TGQuark; function g_io_error_quark: TGQuark; function G_IO_ERROR: TGQuark; function g_io_error_from_errno(err_no: gint): GIOErrorEnum; -procedure g_set_error_from_exception(Error: PPGError; E: Exception); +procedure tuxcmd_set_error_from_exception(Error: PPGError; E: Exception); procedure ShowError(Parent: TCustomGTKForm; const Text: string; Error: PGError); +procedure PrefixTuxcmdError(Error: PPGError; const FileName: string); +procedure VFSToTuxcmdError(Error: PPGError; Operation: TuxcmdErrorEnum); implementation @@ -78,6 +131,16 @@ uses UCoreUtils, UGnome; (********************************************************************************************************************************) +function tuxcmd_error_quark: TGQuark; +begin + Result := g_quark_from_static_string('tuxcmd-error-quark'); +end; + +function TUXCMD_ERROR: TGQuark; +begin + Result := tuxcmd_error_quark; +end; + function g_io_error_quark: TGQuark; begin Result := g_quark_from_static_string('g-io-error-quark'); @@ -90,6 +153,11 @@ end; function g_io_error_from_errno(err_no: gint): GIOErrorEnum; begin + // Prefer native library call + if @__g_io_error_from_errno <> nil then begin + Result := __g_io_error_from_errno(err_no); + Exit; + end; case err_no of EEXIST: Result := G_IO_ERROR_EXISTS; EISDIR: Result := G_IO_ERROR_IS_DIRECTORY; @@ -103,23 +171,40 @@ begin ENOMEM: Result := G_IO_ERROR_NO_SPACE; EINVAL: Result := G_IO_ERROR_INVALID_ARGUMENT; EPERM: Result := G_IO_ERROR_PERMISSION_DENIED; + EMLINK: Result := G_IO_ERROR_TOO_MANY_LINKS; + ENOMSG: Result := G_IO_ERROR_INVALID_DATA; + ENODATA: Result := G_IO_ERROR_INVALID_DATA; + EBADMSG: Result := G_IO_ERROR_INVALID_DATA; ECANCELED: Result := G_IO_ERROR_CANCELLED; ENOTEMPTY: Result := G_IO_ERROR_NOT_EMPTY; ENOTSUP: Result := G_IO_ERROR_NOT_SUPPORTED; + EPROTONOSUPPORT: Result := G_IO_ERROR_NOT_SUPPORTED; + ESOCKTNOSUPPORT: Result := G_IO_ERROR_NOT_SUPPORTED; + EPFNOSUPPORT: Result := G_IO_ERROR_NOT_SUPPORTED; + EAFNOSUPPORT: Result := G_IO_ERROR_NOT_SUPPORTED; ETIMEDOUT: Result := G_IO_ERROR_TIMED_OUT; EBUSY: Result := G_IO_ERROR_BUSY; EAGAIN: Result := G_IO_ERROR_WOULD_BLOCK; EMFILE: Result := G_IO_ERROR_TOO_MANY_OPEN_FILES; EADDRINUSE: Result := G_IO_ERROR_ADDRESS_IN_USE; + EHOSTUNREACH: Result := G_IO_ERROR_HOST_UNREACHABLE; + ENETUNREACH: Result := G_IO_ERROR_NETWORK_UNREACHABLE; + ENETDOWN: Result := G_IO_ERROR_NETWORK_UNREACHABLE; + ECONNREFUSED: Result := G_IO_ERROR_CONNECTION_REFUSED; + EADDRNOTAVAIL: Result := G_IO_ERROR_CONNECTION_REFUSED; + ECONNRESET: Result := G_IO_ERROR_CONNECTION_CLOSED; + ENOTCONN: Result := G_IO_ERROR_NOT_CONNECTED; + EDESTADDRREQ: Result := G_IO_ERROR_DESTINATION_UNSET; + EMSGSIZE: Result := G_IO_ERROR_MESSAGE_TOO_LARGE; + ENOTSOCK: Result := G_IO_ERROR_INVALID_ARGUMENT; else Result := G_IO_ERROR_FAILED; end; end; - (********************************************************************************************************************************) -procedure g_set_error_from_exception(Error: PPGError; E: Exception); +procedure tuxcmd_set_error_from_exception(Error: PPGError; E: Exception); begin - g_set_error(Error, G_IO_ERROR, integer(G_IO_ERROR_FAILED), 'Exception raised: %s', PChar(E.Message)); + g_set_error(Error, TUXCMD_ERROR, integer(TUXCMD_ERROR_EXCEPTION), 'Exception raised: %s', PChar(E.Message)); end; (********************************************************************************************************************************) @@ -142,5 +227,72 @@ begin gtk_widget_destroy (PGtkWidget(Dialog)); end; +procedure PrefixTuxcmdError(Error: PPGError; const FileName: string); +begin + if (Error = nil) or (Error^ = nil) then Exit; + if (Error^.domain <> TUXCMD_ERROR) then Exit; + case TuxcmdErrorEnum(Error^.code) of + TUXCMD_ERROR_CHDIR: g_prefix_error(Error, 'Error changing directory to ''%s'': ', StrToUTF8(PChar(FileName))); + TUXCMD_ERROR_OPENDIR: g_prefix_error(Error, 'Error opening directory ''%s'': ', StrToUTF8(PChar(FileName))); + TUXCMD_ERROR_STAT: g_prefix_error(Error, 'Error getting file info for ''%s'': ', StrToUTF8(PChar(FileName))); + TUXCMD_ERROR_MKDIR: g_prefix_error(Error, 'Error creating directory ''%s'': ', StrToUTF8(PChar(FileName))); + TUXCMD_ERROR_REMOVE: g_prefix_error(Error, 'Error deleting ''%s'': ', StrToUTF8(PChar(FileName))); + TUXCMD_ERROR_SYMLINK: g_prefix_error(Error, 'Error creating symlink ''%s'': ', StrToUTF8(PChar(FileName))); + TUXCMD_ERROR_CHMOD: g_prefix_error(Error, 'Error changing permissions of ''%s'': ', StrToUTF8(PChar(FileName))); + TUXCMD_ERROR_CHOWN: g_prefix_error(Error, 'Error changing owner/group of ''%s'': ', StrToUTF8(PChar(FileName))); + TUXCMD_ERROR_RENAME: g_prefix_error(Error, 'Error renaming file ''%s'': ', StrToUTF8(PChar(FileName))); + TUXCMD_ERROR_TIMESTAMPS: g_prefix_error(Error, 'Error changing timestamps of ''%s'': ', StrToUTF8(PChar(FileName))); + TUXCMD_ERROR_OPEN_FILE: g_prefix_error(Error, 'Error opening file ''%s'': ', StrToUTF8(PChar(FileName))); + TUXCMD_ERROR_READ_FILE: g_prefix_error(Error, 'Error reading file ''%s'': ', StrToUTF8(PChar(FileName))); + TUXCMD_ERROR_WRITE_FILE: g_prefix_error(Error, 'Error writing file ''%s'': ', StrToUTF8(PChar(FileName))); + TUXCMD_ERROR_CLOSE_FILE: g_prefix_error(Error, 'Error closing file ''%s'': ', StrToUTF8(PChar(FileName))); + TUXCMD_ERROR_SEEK: g_prefix_error(Error, 'Error seeking in file ''%s'': ', StrToUTF8(PChar(FileName))); + end; +end; + +procedure VFSToTuxcmdError(Error: PPGError; Operation: TuxcmdErrorEnum); +var OldError: PGError; +begin + if (Error = nil) or (Error^ = nil) or (Error^.domain <> G_IO_ERROR) then Exit; + case GIOErrorEnum(Error^.code) of + G_IO_ERROR_FAILED, + G_IO_ERROR_NOT_FOUND, + G_IO_ERROR_EXISTS, + G_IO_ERROR_IS_DIRECTORY, + G_IO_ERROR_NOT_DIRECTORY, + G_IO_ERROR_NOT_EMPTY, + G_IO_ERROR_NOT_REGULAR_FILE, + G_IO_ERROR_NOT_SYMBOLIC_LINK, + G_IO_ERROR_FILENAME_TOO_LONG, + G_IO_ERROR_INVALID_FILENAME, + G_IO_ERROR_TOO_MANY_LINKS, + G_IO_ERROR_NO_SPACE, + G_IO_ERROR_INVALID_ARGUMENT, + G_IO_ERROR_PERMISSION_DENIED, + G_IO_ERROR_NOT_SUPPORTED, +// G_IO_ERROR_CLOSED, +// G_IO_ERROR_CANCELLED, +// G_IO_ERROR_PENDING, + G_IO_ERROR_READ_ONLY, + G_IO_ERROR_CANT_CREATE_BACKUP, + G_IO_ERROR_WOULD_RECURSE, + G_IO_ERROR_BUSY, + G_IO_ERROR_WOULD_BLOCK, + G_IO_ERROR_WOULD_MERGE, +// G_IO_ERROR_FAILED_HANDLED, + G_IO_ERROR_TOO_MANY_OPEN_FILES, + G_IO_ERROR_NOT_INITIALIZED, + G_IO_ERROR_PARTIAL_INPUT, + G_IO_ERROR_INVALID_DATA, + G_IO_ERROR_BROKEN_PIPE, + G_IO_ERROR_NO_SUCH_DEVICE: begin + OldError := Error^; + Error^ := nil; + g_set_error(Error, TUXCMD_ERROR, gint(Operation), '%s', OldError^.message); + g_free (OldError); + end; + end; +end; + end. diff --git a/UGnome.pas b/UGnome.pas index 2955142..5edab31 100644 --- a/UGnome.pas +++ b/UGnome.pas @@ -21,7 +21,7 @@ unit UGnome; interface -uses glib2, gdk2, gdk2pixbuf, gtk2, Classes, ULibc, +uses glib2, gdk2, gdk2pixbuf, gtk2, Classes, ULibc, UError, GTKForms, GTKControls, GTKStdCtrls, GTKExtCtrls, GTKClasses, GTKDialogs, GTKUtils, GTKConsts, uVFSprototypes; @@ -176,7 +176,7 @@ type PGnomeColorPicker = PGtkWidget; const AFTER_ALL_TABS = -1; NOT_IN_APP_WINDOWS = -2; -var libGlib2Handle, libGtk2Handle, libGnome2Handle, libGnomeUI2Handle: Pointer; +var libGlib2Handle, libGio2Handle, libGtk2Handle, libGnome2Handle, libGnomeUI2Handle: Pointer; gnome_about_new: function (const name, version, copyright, comments: Pchar; const authors, documenters: PPchar; const translator_credits: Pchar; logo_pixbuf: PGdkPixbuf): PGtkWidget; cdecl; {$IFDEF KYLIX} @@ -212,9 +212,11 @@ var libGlib2Handle, libGtk2Handle, libGnome2Handle, libGnomeUI2Handle: Pointer; message_format:Pgchar):PGtkWidget; varargs; cdecl; {$ENDIF} g_mkdir_with_parents: function (const pathname: PChar; mode: integer): integer; cdecl; + __g_io_error_from_errno: function (err_no: gint): GIOErrorEnum; cdecl; function _gtk_notebook_insert_page(notebook:PGtkNotebook; child:PGtkWidget;tab_label:PGtkWidget; position:gint):gint; cdecl; external gtklib name 'gtk_notebook_insert_page'; +procedure g_prefix_error(err: PPGError; format: Pgchar); varargs; cdecl; external gliblib name 'g_prefix_error'; @@ -1637,17 +1639,26 @@ procedure LoadGnomeLibs; begin // Set default values @gnome_about_new := nil; + @gnome_program_init := nil; + @libgnome_module_info_get := nil; + @libgnomeui_module_info_get := nil; @gnome_color_picker_new := nil; @gnome_color_picker_get_i16 := nil; @gnome_color_picker_set_i16 := nil; - @libgnomeui_module_info_get := nil; - @gnome_program_init := nil; - @libgnome_module_info_get := nil; @gnome_icon_entry_new := nil; @gnome_icon_entry_set_pixmap_subdir := nil; @gnome_icon_entry_get_filename := nil; @gnome_icon_entry_set_filename := nil; + @gnome_date_edit_new := nil; + @gnome_date_edit_set_time := nil; + @gnome_date_edit_get_time := nil; @gtk_event_box_set_visible_window := nil; + @gtk_icon_size_lookup_for_settings := nil; + @gtk_window_set_icon_name := nil; + @g_filename_display_name := nil; + @gtk_message_dialog_new_with_markup := nil; + @g_mkdir_with_parents := nil; + @__g_io_error_from_errno := nil; // Dynamic loading libGlib2Handle := dlopen('libglib-2.0.so.0', RTLD_LAZY); @@ -1657,6 +1668,11 @@ begin @g_mkdir_with_parents := dlsym(libGlib2Handle, 'g_mkdir_with_parents'); DebugMsg(['libglib-2.0.so loaded, @g_filename_display_name = ', @g_filename_display_name]); end; + libGio2Handle := dlopen('libgio-2.0.so.0', RTLD_LAZY); + if libGio2Handle = nil then libGio2Handle := dlopen('libgio-2.0.so', RTLD_LAZY); + if libGio2Handle <> nil then begin + @__g_io_error_from_errno := dlsym(libGio2Handle, 'g_io_error_from_errno'); + end; libGtk2Handle := dlopen('libgtk-x11-2.0.so.0', RTLD_LAZY); if libGtk2Handle = nil then libGtk2Handle := dlopen('libgtk-x11-2.0.so', RTLD_LAZY); if libGtk2Handle <> nil then begin diff --git a/vfs/UVFSCore.pas b/vfs/UVFSCore.pas index e7d63a2..5bf185e 100644 --- a/vfs/UVFSCore.pas +++ b/vfs/UVFSCore.pas @@ -136,8 +136,8 @@ type function GetBlockSize: guint32; override; procedure SetBlockSize(Value: guint32); override; - function CopyFileIn(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer): boolean; override; - function CopyFileOut(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer): boolean; override; + function CopyFileIn(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer; Error: PPGError): boolean; override; + function CopyFileOut(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer; Error: PPGError): boolean; override; function IsOnSameFS(const Path1, Path2: string; FollowSymlinks: boolean): boolean; override; function TwoSameFiles(const Path1, Path2: string; FollowSymlinks: boolean): boolean; override; @@ -161,8 +161,8 @@ type // callbacks here are used for next volume prompts, password prompts (encrypted archives) function StartCopyOperation(AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; function StopCopyOperation(ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; - function CopyFileOutEx(const SourceFile, DestFile: string; Append: boolean): boolean; - function CopyFileInEx(const SourceFile, DestFile: string; Append: boolean): boolean; + function CopyFileOutEx(const SourceFile, DestFile: string; Append: boolean; Error: PPGError): boolean; + function CopyFileInEx(const SourceFile, DestFile: string; Append: boolean; Error: PPGError): boolean; published property PluginID: string read GetPluginID; property ArchiveMode: boolean read FArchiveMode; @@ -394,7 +394,7 @@ begin end; except on E: Exception do begin - g_set_error_from_exception(Error, E); + tuxcmd_set_error_from_exception(Error, E); Result := False; end; end; @@ -416,7 +416,7 @@ begin end; except on E: Exception do begin - g_set_error_from_exception(Error, E); + tuxcmd_set_error_from_exception(Error, E); Result := False; end; end; @@ -465,17 +465,16 @@ end; function TVFSEngine.GetListing(List: TList; const APath: string; AddDotFiles, FollowSymlinks, AddFullPath: boolean; Error: PPGError): boolean; var P: PVFSItem; Item: PDataItem; - FError: PGError; begin DebugMsg(['^^VFS (II): GetListing begin']); Result := False; try if (@FSourcePlugin.FVFSListFirst = nil) or (@FSourcePlugin.FVFSListNext = nil) or (@FSourcePlugin.FVFSListClose = nil) then begin - g_set_error(Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'File listing not supported by the VFS module.'); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'File listing not supported by the VFS module.'); Exit; end; - FError := nil; - P := FSourcePlugin.FVFSListFirst(FGlobs, PChar(APath), FollowSymlinks, AddFullPath, @FError); + P := FSourcePlugin.FVFSListFirst(FGlobs, PChar(APath), FollowSymlinks, AddFullPath, Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_OPENDIR); while (P <> nil) and (BreakProcessingType <> 2) do begin if (strlen(P^.FName) > 0) and (AddDotFiles or (P^.FName[0] <> '.')) then begin Item := GetDataItemFromVFSItem(P); @@ -485,25 +484,24 @@ begin if P^.FDisplayName <> nil then real_libc_free(P^.FDisplayName); if P^.sLinkTo <> nil then real_libc_free(P^.sLinkTo); real_libc_free(P); - P := FSourcePlugin.FVFSListNext(FGlobs, @FError); + P := FSourcePlugin.FVFSListNext(FGlobs, Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_OPENDIR); end; - Result := FError = nil; + Result := Error^ = nil; if BreakProcessingType <> 0 then begin DebugMsg(['^^VFS (WW): GetListing: stopped by BreakProcessing']); - g_set_error(Error, G_IO_ERROR, gint(G_IO_ERROR_CANCELLED), '%s', 'The operation has been cancelled.'); + if Error^ <> nil then + g_error_free(Error^); + Error^ := nil; + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_CANCELLED), '%s', 'The operation has been cancelled.'); Result := False; - end else begin - if (Error <> nil) and (FError <> nil) then - Error^ := g_error_copy(FError); end; - if FError <> nil then - g_error_free(FError); real_libc_free(P); FSourcePlugin.FVFSListClose(FGlobs, nil); except on E: Exception do begin + tuxcmd_set_error_from_exception(Error, E); DebugMsg(['^^VFS (EE): GetListing: Exception: ', E.Message]); - g_set_error_from_exception(Error, E); Result := False; end; end; @@ -518,11 +516,12 @@ begin Result := nil; if @FSourcePlugin.FVFSFileInfo = nil then begin - g_set_error(Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'Querying file info is not supported by the VFS module.'); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'Querying file info is not supported by the VFS module.'); Exit; end; try P := FSourcePlugin.FVFSFileInfo(FGlobs, PChar(APath), FollowSymlinks, AddFullPath, Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_STAT); if P <> nil then begin Result := GetDataItemFromVFSItem(P); if P^.FName <> nil then real_libc_free(P^.FName); @@ -533,7 +532,7 @@ begin except on E: Exception do begin DebugMsg(['^^VFS (EE): GetFileInfo: Exception: ', E.Message]); - g_set_error_from_exception(Error, E); + tuxcmd_set_error_from_exception(Error, E); Result := nil; end; end; @@ -606,10 +605,11 @@ begin Result := False; try Result := FSourcePlugin.FVFSChangeDir(FGlobs, PChar(NewPath), Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_CHDIR); except on E: Exception do begin DebugMsg(['^^VFS (EE): ChangeDir: Exception: ', E.Message]); - g_set_error_from_exception(Error, E); + tuxcmd_set_error_from_exception(Error, E); Result := False; end; end; @@ -623,13 +623,14 @@ begin if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, AskQuestionCallback, AskPasswordCallback, ProgressCallback, CallbackData); Result := ChangeDir(NewPath, Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_CHDIR); if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, nil, nil, nil, nil); end; except on E: Exception do begin DebugMsg(['^^VFS (EE): ChangeDirEx: Exception: ', E.Message]); - g_set_error_from_exception(Error, E); + tuxcmd_set_error_from_exception(Error, E); Result := False; end; end; @@ -674,71 +675,78 @@ end; function TVFSEngine.MakeDir(const NewDir: string; Error: PPGError): boolean; begin - if @FSourcePlugin.FVFSMkDir <> nil then - Result := FSourcePlugin.FVFSMkDir(FGlobs, PChar(NewDir), Error) - else begin + if @FSourcePlugin.FVFSMkDir <> nil then begin + Result := FSourcePlugin.FVFSMkDir(FGlobs, PChar(NewDir), Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_MKDIR); + end else begin Result := False; - g_set_error(Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'Creating directories is not supported by the VFS module.'); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'Creating directories is not supported by the VFS module.'); end; end; function TVFSEngine.Remove(const APath: string; Error: PPGError): boolean; begin - if @FSourcePlugin.FVFSRemove <> nil then - Result := FSourcePlugin.FVFSRemove(FGlobs, PChar(APath), Error) - else begin + if @FSourcePlugin.FVFSRemove <> nil then begin + Result := FSourcePlugin.FVFSRemove(FGlobs, PChar(APath), Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_REMOVE); + end else begin Result := False; - g_set_error(Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'Removing files is not supported by the VFS module.'); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'Removing files is not supported by the VFS module.'); end; end; function TVFSEngine.RenameFile(const SourceFile, DestFile: string; Error: PPGError): boolean; begin - if @FSourcePlugin.FVFSRename <> nil then - Result := FSourcePlugin.FVFSRename(FGlobs, PChar(SourceFile), PChar(DestFile), Error) - else begin + if @FSourcePlugin.FVFSRename <> nil then begin + Result := FSourcePlugin.FVFSRename(FGlobs, PChar(SourceFile), PChar(DestFile), Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_RENAME); + end else begin Result := False; - g_set_error(Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'Renaming files is not supported by the VFS module.'); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'Renaming files is not supported by the VFS module.'); end; end; function TVFSEngine.MakeSymLink(const NewFileName, PointTo: string; Error: PPGError): boolean; begin - if @FSourcePlugin.FVFSMakeSymLink <> nil then - Result := FSourcePlugin.FVFSMakeSymLink(FGlobs, PChar(NewFileName), PChar(PointTo), Error) - else begin + if @FSourcePlugin.FVFSMakeSymLink <> nil then begin + Result := FSourcePlugin.FVFSMakeSymLink(FGlobs, PChar(NewFileName), PChar(PointTo), Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_SYMLINK); + end else begin Result := False; - g_set_error(Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'Creating symbolic links is not supported by the VFS module.'); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'Creating symbolic links is not supported by the VFS module.'); end; end; function TVFSEngine.Chmod(const FileName: string; Mode: cuLong; Error: PPGError): boolean; begin - if @FSourcePlugin.FVFSChmod <> nil then - Result := FSourcePlugin.FVFSChmod(FGlobs, PChar(FileName), Mode, Error) - else begin + if @FSourcePlugin.FVFSChmod <> nil then begin + Result := FSourcePlugin.FVFSChmod(FGlobs, PChar(FileName), Mode, Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_CHMOD); + end else begin Result := False; - g_set_error(Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'Chmod is not supported by the VFS module.'); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'Chmod is not supported by the VFS module.'); end; end; function TVFSEngine.Chown(const FileName: string; UID, GID: cuLong; Error: PPGError): boolean; begin - if @FSourcePlugin.FVFSChown <> nil then - Result := FSourcePlugin.FVFSChown(FGlobs, PChar(FileName), UID, GID, Error) - else begin + if @FSourcePlugin.FVFSChown <> nil then begin + Result := FSourcePlugin.FVFSChown(FGlobs, PChar(FileName), UID, GID, Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_CHOWN); + end else begin Result := False; - g_set_error(Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'Chown is not supported by the VFS module.'); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'Chown is not supported by the VFS module.'); end; end; function TVFSEngine.ChangeTimes(const APath: string; mtime, atime: time_t; Error: PPGError): boolean; begin - if @FSourcePlugin.FVFSChangeTimes <> nil then - Result := FSourcePlugin.FVFSChangeTimes(FGlobs, PChar(APath), mtime, atime, Error) - else begin + if @FSourcePlugin.FVFSChangeTimes <> nil then begin + Result := FSourcePlugin.FVFSChangeTimes(FGlobs, PChar(APath), mtime, atime, Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_TIMESTAMPS); + end else begin Result := False; - g_set_error(Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'Changing timestamps is not supported by the VFS module.'); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'Changing timestamps is not supported by the VFS module.'); end; end; @@ -778,48 +786,56 @@ end; function TVFSEngine.OpenFile(const APath: string; Mode: integer; Error: PPGError): TEngineFileDes; begin - if @FSourcePlugin.FVFSOpenFile <> nil then - Result := FSourcePlugin.FVFSOpenFile(FGlobs, PChar(APath), Mode, Error) - else begin + if @FSourcePlugin.FVFSOpenFile <> nil then begin + Result := FSourcePlugin.FVFSOpenFile(FGlobs, PChar(APath), Mode, Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_OPEN_FILE); + end else begin Result := nil; - g_set_error(Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'Manual opening files is not supported by the VFS module.'); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'Manual opening files is not supported by the VFS module.'); end; end; function TVFSEngine.ReadFile(const FileDescriptor: TEngineFileDes; Buffer: Pointer; ABlockSize: integer; Error: PPGError): integer; begin - if @FSourcePlugin.FVFSReadFile <> nil then - Result := FSourcePlugin.FVFSReadFile(FGlobs, FileDescriptor, Buffer, ABlockSize, Error) - else begin + if @FSourcePlugin.FVFSReadFile <> nil then begin + Result := FSourcePlugin.FVFSReadFile(FGlobs, FileDescriptor, Buffer, ABlockSize, Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_READ_FILE); + end else begin Result := -1; - g_set_error(Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'Manual read is not supported by the VFS module.'); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'Manual read is not supported by the VFS module.'); end; end; function TVFSEngine.WriteFile(const FileDescriptor: TEngineFileDes; Buffer: Pointer; BytesCount: integer; Error: PPGError): integer; begin - if @FSourcePlugin.FVFSWriteFile <> nil then - Result := FSourcePlugin.FVFSWriteFile(FGlobs, FileDescriptor, Buffer, BytesCount, Error) - else begin + if @FSourcePlugin.FVFSWriteFile <> nil then begin + Result := FSourcePlugin.FVFSWriteFile(FGlobs, FileDescriptor, Buffer, BytesCount, Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_WRITE_FILE); + end else begin Result := -1; - g_set_error(Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'Manual write is not supported by the VFS module.'); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'Manual write is not supported by the VFS module.'); end; end; function TVFSEngine.CloseFile(const FileDescriptor: TEngineFileDes; Error: PPGError): boolean; begin - if @FSourcePlugin.FVFSCloseFile <> nil then - Result := FSourcePlugin.FVFSCloseFile(FGlobs, FileDescriptor, nil) - else Result := False; + if @FSourcePlugin.FVFSCloseFile <> nil then begin + Result := FSourcePlugin.FVFSCloseFile(FGlobs, FileDescriptor, Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_CLOSE_FILE); + end else begin + Result := False; + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'Manual closing files is not supported by the VFS module.'); + end; end; function TVFSEngine.FileSeek(const FileDescriptor: TEngineFileDes; const AbsoluteOffset: Int64; Error: PPGError): Int64; begin - if @FSourcePlugin.FVFSFileSeek <> nil then - Result := FSourcePlugin.FVFSFileSeek(FGlobs, FileDescriptor, AbsoluteOffset, Error) - else begin + if @FSourcePlugin.FVFSFileSeek <> nil then begin + Result := FSourcePlugin.FVFSFileSeek(FGlobs, FileDescriptor, AbsoluteOffset, Error); + VFSToTuxcmdError(Error, TUXCMD_ERROR_SEEK); + end else begin Result := -1; - g_set_error(Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'Manual seek is not supported by the VFS module.'); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'Manual seek is not supported by the VFS module.'); end; end; @@ -832,73 +848,75 @@ end; (********************************************************************************************************************************) -function TVFSEngine.CopyFileIn(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer): boolean; +function TVFSEngine.CopyFileIn(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer; Error: PPGError): boolean; begin DebugMsg(['** (WW): deprecated method CopyFileIn called from a TVFSEngine instance!']); Result := StartCopyOperation(nil, nil, nil, nil); - Result := Result and CopyFileInEx(SourceFile, DestFile, Append); + Result := Result and CopyFileInEx(SourceFile, DestFile, Append, Error); Result := Result and StopCopyOperation(nil, nil); end; -function TVFSEngine.CopyFileOut(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer): boolean; +function TVFSEngine.CopyFileOut(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer; Error: PPGError): boolean; begin DebugMsg(['** (WW): deprecated method CopyFileOut called from a TVFSEngine instance!']); Result := StartCopyOperation(nil, nil, nil, nil); - Result := Result and CopyFileOutEx(SourceFile, DestFile, Append); + Result := Result and CopyFileOutEx(SourceFile, DestFile, Append, Error); Result := Result and StopCopyOperation(nil, nil); end; -function TVFSEngine.CopyFileOutEx(const SourceFile, DestFile: string; Append: boolean): boolean; -var Error: PGError; - ACopyProgressCallback: TVFSProgressCallback; +function TVFSEngine.CopyFileOutEx(const SourceFile, DestFile: string; Append: boolean; Error: PPGError): boolean; +var ACopyProgressCallback: TVFSProgressCallback; begin Result := False; try - Error := nil; if @FSourcePlugin.FVFSCopyToLocal <> nil then try - Result := FSourcePlugin.FVFSCopyToLocal(FGlobs, PChar(SourceFile), PChar(DestFile), Append, @Error) + Result := FSourcePlugin.FVFSCopyToLocal(FGlobs, PChar(SourceFile), PChar(DestFile), Append, Error) except - on E: Exception do g_set_error_from_exception(@Error, E); + on E: Exception do tuxcmd_set_error_from_exception(Error, E); end else - g_set_error(@Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'VFSCopyToLocal not supported by the VFS module.'); - if Error <> nil then begin + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'VFSCopyToLocal not supported by the VFS module.'); + if Error^ <> nil then begin if FCopyProgressCallback <> nil then begin @ACopyProgressCallback := FCopyProgressCallback; - Result := ACopyProgressCallback(0, Error, FCopyCallbackData); + Result := ACopyProgressCallback(0, Error^, FCopyCallbackData); end; - g_error_free(Error); end; except - on E: Exception do DebugMsg(['*** Exception raised in TVFSEngine.CopyFileOutEx(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 TVFSEngine.CopyFileOutEx(SourceFile=', SourceFile, ', DestFile=', DestFile, ', Append=', Append,'): (', E.ClassName, '): ', E.Message]); + end; end; end; -function TVFSEngine.CopyFileInEx(const SourceFile, DestFile: string; Append: boolean): boolean; -var Error: PGError; -ACopyProgressCallback: TVFSProgressCallback; +function TVFSEngine.CopyFileInEx(const SourceFile, DestFile: string; Append: boolean; Error: PPGError): boolean; +var ACopyProgressCallback: TVFSProgressCallback; begin Result := False; try - Error := nil; if @FSourcePlugin.FVFSCopyFromLocal <> nil then try - Result := FSourcePlugin.FVFSCopyFromLocal(FGlobs, PChar(SourceFile), PChar(DestFile), Append, @Error) + Result := FSourcePlugin.FVFSCopyFromLocal(FGlobs, PChar(SourceFile), PChar(DestFile), Append, Error) except - on E: Exception do g_set_error_from_exception(@Error, E); + on E: Exception do tuxcmd_set_error_from_exception(Error, E); end else - g_set_error(@Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'VFSCopyFromLocal not supported by the VFS module.'); + g_set_error(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'VFSCopyFromLocal not supported by the VFS module.'); if Error <> nil then begin if FCopyProgressCallback <> nil then begin @ACopyProgressCallback := FCopyProgressCallback; - Result := ACopyProgressCallback(0, Error, FCopyCallbackData); + Result := ACopyProgressCallback(0, Error^, FCopyCallbackData); end; - g_error_free(Error); end; except - on E: Exception do DebugMsg(['*** Exception raised in TVFSEngine.CopyFileInEx(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 TVFSEngine.CopyFileInEx(SourceFile=', SourceFile, ', DestFile=', DestFile, ', Append=', Append,'): (', E.ClassName, '): ', E.Message]); + end; end; end; @@ -917,10 +935,10 @@ begin try Result := FSourcePlugin.FVFSStartCopyOperation(FGlobs, @Error) except - on E: Exception do g_set_error_from_exception(@Error, E); + on E: Exception do tuxcmd_set_error_from_exception(@Error, E); end else - g_set_error(@Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'VFSStartCopyOperation not supported by the VFS module.'); + g_set_error(@Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'VFSStartCopyOperation not supported by the VFS module.'); if Error <> nil then begin if ProgressCallback <> nil then Result := ProgressCallback^(0, Error, CallbackData); @@ -945,10 +963,10 @@ begin try Result := FSourcePlugin.FVFSStopCopyOperation(FGlobs, @Error) except - on E: Exception do g_set_error_from_exception(@Error, E); + on E: Exception do tuxcmd_set_error_from_exception(@Error, E); end else - g_set_error(@Error, G_IO_ERROR, gint(G_IO_ERROR_NOT_SUPPORTED), '%s', 'VFSStopCopyOperation not supported by the VFS module.'); + g_set_error(@Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), '%s', 'VFSStopCopyOperation not supported by the VFS module.'); if Error <> nil then begin if ProgressCallback <> nil then Result := ProgressCallback^(0, Error, CallbackData); -- cgit v1.2.3