summaryrefslogtreecommitdiff
path: root/UCore.pas
diff options
context:
space:
mode:
authorTomas Bzatek <tbzatek@users.sourceforge.net>2008-06-07 20:34:49 +0200
committerTomas Bzatek <tbzatek@users.sourceforge.net>2008-06-07 20:34:49 +0200
commitecde167da74c86bc047aaf84c5e548cf65a5da98 (patch)
treea015dfda84f28a65811e3aa0d369f8f211ec8c60 /UCore.pas
downloadtuxcmd-0.6.36.tar.xz
Diffstat (limited to 'UCore.pas')
-rw-r--r--UCore.pas2785
1 files changed, 2785 insertions, 0 deletions
diff --git a/UCore.pas b/UCore.pas
new file mode 100644
index 0000000..694d249
--- /dev/null
+++ b/UCore.pas
@@ -0,0 +1,2785 @@
+(*
+ Tux Commander - UCore - Some engine-related core functions
+ Copyright (C) 2008 Tomas Bzatek <tbzatek@users.sourceforge.net>
+ Check for updates on tuxcmd.sourceforge.net
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*)
+unit UCore;
+interface
+
+uses glib2, SyncObjs, Classes, GTKForms, GTKView, Libc, UGlibC_compat, UEngines, UCoreUtils, UProgress, UVFSCore;
+
+
+function FillPanel(List: TList; ListView: TGTKListView; Engine: TPanelEngine; LeftPanel: boolean): boolean;
+function ChangeDir(Engine: TPanelEngine; Path: string; var SelItem: string; const AutoFallBack: boolean): integer;
+function MakeDirectory(ListView: TGTKListView; Engine: TPanelEngine; LeftPanel: boolean; NewDir: string): boolean;
+procedure FindNextSelected(ListView: TGTKListView; DataList: TList; var Item1, Item2: string);
+procedure UnselectAll(ListView: TGTKListView; DataList: TList);
+
+type TWorkerThread = class(TThread)
+ private
+ FCancelled: boolean;
+ GUIMutex: TCriticalSection;
+ protected
+ procedure Execute; override;
+ procedure CommitGUIUpdate;
+ public
+ // Data to updating
+ FProgress1Pos, FProgress2Pos, FProgress1Max, FProgress2Max: Int64;
+ FProgress1Text, FProgress2Text, FLabel1Text, FLabel2Text: string;
+ FGUIProgress1Pos, FGUIProgress2Pos, FGUIProgress1Max, FGUIProgress2Max: Int64;
+ FGUIProgress1Text, FGUIProgress2Text, FGUILabel1Text, FGUILabel2Text: string;
+ FGUIChanged: boolean;
+
+ FCancelMessage: string;
+ FDoneThread, FShowCancelMessage,
+ FDialogShowDirDelete, FDialogShowOverwrite, FDialogShowNewDir, FDialogShowMsgBox,
+ FSigDialogDirDelete, FSigDialogOverwrite, FSigDialogNewDir, FSigDialogMsgBox: boolean;
+ FDialogResultDirDelete, FDialogResultOverwrite, FDialogResultNewDir: integer;
+
+ FDirDeleteButtonsType: integer;
+ FDirDeleteLabel1Text, FDirDeleteLabel2Text, FDirDeleteLabel3Text, FDirDeleteCaption: string;
+ FDirDeleteLabel2Visible, FDirDeleteLabel3Visible: boolean;
+
+ FOverwriteButtonsType: integer;
+ FOverwriteFromLabel, FOverwriteFromInfoLabel, FOverwriteToLabel, FOverwriteToInfoLabel,
+ FOverwriteRenameStr, FOverwriteSourceFile, FOverwriteDestFile: string;
+
+ FNewDirCaption, FNewDirLabel, FNewDirEdit: string;
+ FMsgBoxText: string;
+ FMsgBoxButtons: TMessageButtons;
+ FMsgBoxStyle: TMessageStyle;
+ FMsgBoxDefault, FMsgBoxEscape, FDialogResultMsgBox: TMessageButton;
+
+
+ // Parameters
+ ProgressForm: TFProgress;
+ Engine, SrcEngine, DestEngine: TPanelEngine;
+ LeftPanel: boolean;
+ DataList: TList;
+ ParamBool1, ParamBool2, ParamBool3, ParamBool4, ParamBool5: boolean;
+ ParamString1, ParamString2, ParamString3: string;
+ ParamPointer1: Pointer;
+ ParamInt64: Int64;
+ ParamInt1, ParamInt2: integer;
+ ParamLongWord1: LongWord;
+ ParamCardinal1, ParamCardinal2: Cardinal;
+ ParamFloat1, ParamFloat2: Extended;
+ ParamDataItem1: PDataItem;
+ WorkerProcedure: procedure(SenderThread: TWorkerThread);
+ SelectedItem: PDataItem;
+ ExtractFromVFSMode, ExtractFromVFSAll: boolean;
+ ErrorHappened: boolean;
+
+ constructor Create;
+ destructor Destroy; override;
+ procedure CancelIt;
+ function Cancelled: boolean;
+
+ procedure UpdateProgress1(const Progress: Int64; const ProgressText: string);
+ procedure UpdateProgress2(const Progress: Int64; const ProgressText: string);
+ procedure SetProgress1Params(const ProgressMax: Int64);
+ procedure SetProgress2Params(const ProgressMax: Int64);
+ procedure UpdateCaption1(const CaptionText: string);
+ procedure UpdateCaption2(const CaptionText: string);
+
+ function ShowDirDeleteDialog(ButtonsType: integer; const Label1Text: string; const Label2Text: string = '';
+ const Label3Text: string = ''; const DirDeleteCaption: string = ''): integer;
+ function ShowOverwriteDialog(ButtonsType: integer; const FromLabel, FromInfoLabel, ToLabel, ToInfoLabel, RenameStr,
+ SourceFile, DestFile: string): integer;
+ function ShowNewDirDialog(Caption, LabelCaption, Edit: string): integer;
+ function ShowMessageBox(const Text: string; Buttons: TMessageButtons; Style: TMessageStyle;
+ Default, Escape: TMessageButton): TMessageButton;
+ end;
+
+ TGetDirSizeThread = class(TThread)
+ private
+ FCancelled: boolean;
+ protected
+ procedure Execute; override;
+ public
+ Finished: boolean;
+ Engine: TPanelEngine;
+ Path: string;
+ Result: Int64;
+ constructor Create;
+ procedure CancelIt;
+ end;
+
+ TOpenDirThread = class(TThread)
+ private
+ procedure Execute; override;
+ public
+ AEngine: TPanelEngine;
+ xEngine: TVFSEngine;
+ APath: string;
+ ASelItem: string;
+ AAutoFallBack: boolean;
+ ADirList: TList;
+ ChDirResult, ListingResult, VFSOpenResult: integer;
+ Finished, CancelIt: boolean;
+ RunningTime: Int64;
+ APlugin: TVFSPlugin;
+ AFullPath, AHighlightItem: string;
+ Password: string;
+ constructor Create;
+{ destructor Destroy; override; }
+ end;
+
+
+// Thread aware functions (also half-thread-safe) without any piece of GTK code
+procedure DeleteFilesWorker(SenderThread: TWorkerThread);
+procedure CopyFilesWorker(SenderThread: TWorkerThread);
+procedure MergeFilesWorker(SenderThread: TWorkerThread);
+procedure SplitFilesWorker(SenderThread: TWorkerThread);
+procedure ChmodFilesWorker(SenderThread: TWorkerThread);
+procedure ChownFilesWorker(SenderThread: TWorkerThread);
+procedure DummyThreadWorker(SenderThread: TWorkerThread);
+
+// Classic functions - don't need progress window
+function CreateSymlink(const FileName, PossibleNewName: string; Engine: TPanelEngine) : boolean;
+function EditSymlink(const FileName: string; Engine: TPanelEngine) : boolean;
+function HandleLogin(Parent: TComponent; Engine: TPanelEngine; UserName, Password: string): boolean;
+procedure GetDirSize(AListView: TGTKListView; Engine: TPanelEngine; DataList: TList; AllItems: boolean);
+
+
+type TMounterItem = class
+ public
+ DisplayText, MountPath, Device, IconPath, MountCommand, UmountCommand: string;
+ DeviceType: integer;
+ function Mounted: boolean;
+ function IsInFSTab: boolean;
+ function Mount: boolean;
+ function Umount: boolean;
+ function Eject: boolean;
+ end;
+
+ TConnMgrItem = class
+ public
+ ConnectionName: string;
+ URI: string; // generated at runtime
+ ServiceType, Server, Username, Password, TargetDir: string;
+ PluginID: string; // leave blank for default
+ end;
+
+procedure FillDefaultFstabMounterItems;
+
+procedure ProcessProgressThread(SenderThread: TWorkerThread; ProgressForm: TFProgress);
+
+function CRCGetInfo(FileName: string; Engine: TPanelEngine; var TargetName: string; var TargetCRC: LongWord; var Size: Int64): boolean;
+
+function ComputeBlockSize(TotalSize: Int64): longint;
+
+function PurgeDirectory(APath: string): boolean;
+procedure CleanTempDirs;
+
+procedure DebugWriteListSL(List: TList);
+procedure DebugWriteList(List: TList);
+
+var LeftLocalEngine, RightLocalEngine: TPanelEngine;
+ LeftPanelData, RightPanelData, AssocList, MounterList, ConnectionMgrList: TList;
+ CommandLineHistory, Bookmarks: TStringList;
+ LeftPanelTabs, RightPanelTabs: TStringList;
+ LeftTabSortIDs, RightTabSortIDs: TList;
+ LeftTabSortTypes, RightTabSortTypes: TList;
+ FMainEscPressed: boolean;
+ UsedTempPaths: TStringList;
+ SelectHistory, SearchHistory: TStringList;
+
+(********************************************************************************************************************************)
+implementation
+(********************************************************************************************************************************)
+uses SysUtils, DateUtils, StrUtils, UConfig, UDirDelete, UOverwrite, ULocale,
+ UNewDir, UFileAssoc, USymlink, UCoreClasses, ULogin, uVFSprototypes,
+ URemoteWait, UMain;
+
+
+
+(********************************************************************************************************************************)
+procedure ClearListData(List: TList);
+var i: integer;
+begin
+ try
+ if not Assigned(List) then Exit;
+ if List.Count > 0 then
+ for i := 0 to List.Count - 1 do
+ FreeDataItem(PDataItem(List[i]));
+ List.Clear;
+ except
+ on E: Exception do DebugMsg(['*** Exception raised in UCore.ClearListData (', E.ClassName, '): ', E.Message]);
+ end;
+end;
+
+(********************************************************************************************************************************)
+procedure AddUpDirItem(ListView: TGTKListView; DataList: TList);
+var ListItem: TGTKListItem;
+ Data: PDataItem;
+ j: integer;
+ s: string;
+begin
+ if ListView.Items.Count = 0
+ then ListItem := ListView.Items.Add
+ else ListItem := ListView.Items[0];
+ Data := Libc.malloc(SizeOf(TDataItem));
+ Libc.memset(Data, 0, SizeOf(TDataItem));
+ with Data^ do begin
+ UpDir := True;
+ IsDotFile := False;
+ AName := nil;
+ LnkPointTo := nil;
+ Selected := False;
+ IsLnk := False;
+ for j := 0 to Length(ColumnData) - 1 do ColumnData[j] := nil;
+ for j := 1 to ConstNumPanelColumns do
+ if ConfColumnVisible[j] then
+ case ConfColumnIDs[j] of
+ 1, 2: begin
+ if ConfDisableDirectoryBrackets then s := '..'
+ else s := '[..]';
+ ColumnData[ConfColumnIDs[j] - 1] := strdup(PChar(s));
+ end;
+ 4: ColumnData[ConfColumnIDs[j] - 1] := strdup(PChar(LANGDIR));
+ end;
+ Icon := UpDirIcon.FPixbuf;
+ ItemColor := NormalItemGDKColor;
+ if not Application.GTKVersion_2_0_5_Up then ListItem.SetValue(0, Data);
+ end;
+ ListItem.Data := Data;
+ DataList.Add(Data);
+end;
+
+(********************************************************************************************************************************)
+function FillPanel(List: TList; ListView: TGTKListView; Engine: TPanelEngine; LeftPanel: boolean): boolean;
+var DataList: TList;
+ i, j, SortColumnID, ItemCount : integer;
+ ListItem : TGTKListItem;
+ Ext, s, s2 : string;
+ SortOrder: TGTKTreeViewSortOrder;
+ Time1, Time2: TDateTime;
+ IsRoot: boolean;
+ UsrManager: TUserManager;
+begin
+ Result := False;
+ try
+ UsrManager := nil;
+ if LeftPanel then DataList := LeftPanelData
+ else DataList := RightPanelData;
+ IsRoot := (Engine.Path = '/') and (not ((Engine is TVFSEngine) and (Engine as TVFSEngine).ArchiveMode));
+{ Time1 := Now;
+ Time2 := Now;
+ DebugMsg(['Get Listing: ', SecondOf(Time2 - Time1), ':', MillisecondOf(Time2 - Time1)]); }
+
+ Time1 := Now;
+ SortColumnID := ListView.SortColumnID;
+ SortOrder := ListView.SortOrder;
+ ListView.SetSortInfo(-1, soAscending);
+ ClearListData(DataList);
+ if List.Count + Ord(not IsRoot) < ListView.Items.Count then
+ for i := ListView.Items.Count - 1 downto List.Count + Ord(not IsRoot) do
+ ListView.Items.Delete(i);
+ ItemCount := ListView.Items.Count;
+
+ Time2 := Now;
+ DebugMsg(['Items clear: ', SecondOf(Time2 - Time1), ':', MillisecondOf(Time2 - Time1)]);
+
+ Time1 := Now;
+ if ((Engine is TVFSEngine) and (Engine as TVFSEngine).ArchiveMode) or (Engine.Path <> '/') then AddUpDirItem(ListView, DataList);
+
+ if List.Count > 0 then
+ for i := 0 to List.Count - 1 do
+ with PDataItem(List[i])^ do begin
+ if i + Ord(not IsRoot) > ItemCount - 1
+ then ListItem := ListView.Items.Add
+ else ListItem := ListView.Items[i + Ord(not IsRoot)];
+ s := String(AName);
+ Ext := '';
+ if not IsDir then SeparateExt(s, s, Ext);
+ Ext := ANSIToUTF8(Ext);
+
+ // Fill the column data
+ for j := 1 to ConstNumPanelColumns do
+ if ConfColumnVisible[j] then
+ case ConfColumnIDs[j] of
+ 1: begin
+ if IsDir and (not ConfDisableDirectoryBrackets)
+ then s2 := ANSIToUTF8(Format('[%s]', [s]))
+ else s2 := ANSIToUTF8(s);
+ ColumnData[ConfColumnIDs[j] - 1] := strdup(PChar(s2));
+ end;
+ 2: begin
+ if IsDir and (not ConfDisableDirectoryBrackets)
+ then s2 := ANSIToUTF8(Format('[%s]', [AName]))
+ else s2 := ANSIToUTF8(AName);
+ ColumnData[ConfColumnIDs[j] - 1] := strdup(PChar(s2));
+ end;
+ 3: ColumnData[ConfColumnIDs[j] - 1] := strdup(PChar(Ext));
+ 4: begin
+ if IsDir then s2 := LANGDIR
+ else s2 := ANSIToUTF8(FormatSize(Size, 0));
+ ColumnData[ConfColumnIDs[j] - 1] := strdup(PChar(s2));
+ end;
+ 5: begin
+ s2 := FormatDateTime('ddddd tt', ModifyTime);
+ ColumnData[ConfColumnIDs[j] - 1] := strdup(PChar(s2));
+ end;
+ 6: begin
+ s2 := FormatDateTime('ddddd', ModifyTime);
+ ColumnData[ConfColumnIDs[j] - 1] := strdup(PChar(s2));
+ end;
+ 7: begin
+ s2 := FormatDateTime('tt', ModifyTime);
+ ColumnData[ConfColumnIDs[j] - 1] := strdup(PChar(s2));
+ end;
+ 8: begin
+ if ConfShowTextUIDs then begin
+ if not Assigned(UsrManager) then UsrManager := TUserManager.Create;
+ s2 := AnsiToUTF8(UsrManager.GetUserName(UID, False));
+ end else s2 := IntToStr(UID);
+ ColumnData[ConfColumnIDs[j] - 1] := strdup(PChar(s2));
+ end;
+ 9: begin
+ if ConfShowTextUIDs then begin
+ if not Assigned(UsrManager) then UsrManager := TUserManager.Create;
+ s2 := AnsiToUTF8(UsrManager.GetGroupName(GID, False));
+ end else s2 := IntToStr(GID);
+ ColumnData[ConfColumnIDs[j] - 1] := strdup(PChar(s2));
+ end;
+ 10: begin
+ if ConfOctalPerm then s2 := Format('%.4d', [AttrToOctal(Mode mod $1000)])
+ else s2 := AttrToStr(Mode);
+ ColumnData[ConfColumnIDs[j] - 1] := strdup(PChar(s2));
+ end;
+ end;
+
+ ItemColor := nil;
+ AddFileTypeIcon(List[i]);
+ DataList.Add(List[i]);
+ ListItem.Data := DataList[DataList.Count - 1];
+ if not Application.GTKVersion_2_0_5_Up then ListItem.SetValue(0, List[i]);
+ end;
+ Time2 := Now;
+ DebugMsg(['Fill panel: ', SecondOf(Time2 - Time1), ':', MillisecondOf(Time2 - Time1)]);
+
+// DebugWriteList(DataList);
+
+ if Assigned(UsrManager) then UsrManager.Free;
+ Time1 := Now;
+ ListView.SetSortInfo(SortColumnID, SortOrder);
+ Time2 := Now;
+ DebugMsg(['Sorting: ', SecondOf(Time2 - Time1), ':', MillisecondOf(Time2 - Time1)]);
+ DebugMsg(['------------------------------']);
+ Result := True;
+ except
+ on E: Exception do begin
+ Application.MessageBox(Format(LANGErrorGettingListingForSPanelNoPath, [LANGPanelStrings[LeftPanel], E.Message]), [mbOK], mbError, mbNone, mbOK);
+ Exit;
+ end;
+ end;
+end;
+
+(********************************************************************************************************************************)
+function ChangeDir(Engine: TPanelEngine; Path: string; var SelItem: string; const AutoFallBack: boolean): integer;
+
+ procedure GoUp(var NewPath: string);
+ var x: integer;
+ begin
+ if NewPath = PathDelim then Exit;
+ NewPath := ExcludeTrailingPathDelimiter(NewPath);
+ if Length(Trim(NewPath)) < 2 then Exit;
+ x := PosEnd(PathDelim, NewPath);
+ SelItem := Copy(NewPath, x + 1, Length(NewPath) - x);
+ NewPath := Copy(NewPath, 1, x);
+ NewPath := IncludeTrailingPathDelimiter(NewPath);
+ end;
+
+var APath: string;
+ Error : integer;
+begin
+ Result := 1;
+ try
+ APath := Engine.Path;
+ if Path = '..' then GoUp(APath)
+ else begin
+ APath := IncludeTrailingPathDelimiter(APath);
+ Path := IncludeTrailingPathDelimiter(Path);
+ if (Length(Path) > 0) and (Path[1] <> '/')
+ then APath := APath + Path
+ else APath := Path;
+ APath := IncludeTrailingPathDelimiter(APath);
+ end;
+
+ // AutoFallback loop
+ Error := Engine.ChangeDir(APath);
+ while AutoFallback and (Error <> 0) and (APath <> '/') do begin
+ GoUp(APath);
+ Error := Engine.ChangeDir(APath);
+ end;
+ // Going on...
+ if Error <> 0 then begin
+ Result := Error;
+ DebugMsg(['*** UCore.ChangeDir: error during Engine.ChangeDir: ', GetErrorString(Error)]);
+ Exit;
+ end;
+ Engine.Path := APath;
+ Result := 0;
+ except
+ on E: Exception do begin
+ DebugMsg(['*** Exception raised in UCore.ChangeDir (', E.ClassName, '): ', E.Message]);
+ Result := 1;
+ end;
+ end;
+end;
+
+(********************************************************************************************************************************)
+function MakeDirectory(ListView: TGTKListView; Engine: TPanelEngine; LeftPanel: boolean; NewDir: string): boolean;
+var Error: integer;
+begin
+ Result := False;
+ try
+ Error := Engine.MakeDir(IncludeTrailingPathDelimiter(Engine.Path) + NewDir);
+ if Error <> 0 then begin
+ Application.MessageBox(Format(LANGErrorCreatingNewDirectorySInSPanel, [NewDir, LANGPanelStrings[LeftPanel], ANSIToUTF8(GetErrorString(Error))]), [mbOK], mbError, mbNone, mbOK);
+ Exit;
+ end;
+ Result := True;
+ except
+ on E: Exception do begin
+ Application.MessageBox(Format(LANGErrorCreatingNewDirectorySInSPanelNoPath, [LANGPanelStrings[LeftPanel], E.Message]), [mbOK], mbError, mbNone, mbOK);
+ Exit;
+ end;
+ end;
+end;
+
+(********************************************************************************************************************************)
+procedure DeleteFilesWorker(SenderThread: TWorkerThread);
+var SkipAll: boolean;
+
+ function HandleDelete(AFileRec: PDataItemSL): boolean;
+ var Res, Response: integer;
+ begin
+ Result := True;
+// DebugMsg(['Debug: IsDir: ', AFileRec^.IsDir, ', Stage1: ', AFileRec^.Stage1, ', IsLnk: ', AFileRec^.IsLnk, '; Result = ', AFileRec^.IsDir and AFileRec^.Stage1 and (not AFileRec^.IsLnk)]);
+ if AFileRec^.IsDir and AFileRec^.Stage1 and (not AFileRec^.IsLnk) then Exit;
+ Res := SenderThread.Engine.Remove(String(AFileRec^.AName));
+// DebugMsg(['Result : ', Res]);
+ if Res <> 0 then
+ if SkipAll then Result := True else
+ begin
+ Response := SenderThread.ShowDirDeleteDialog(1, LANGTheFileDirectory, ANSIToUTF8(String(AFileRec^.AName)),
+ Format(LANGCouldNotBeDeletedS, [ANSIToUTF8(GetErrorString(Res))]));
+ case Response of
+ 1 : Result := True;
+ 3 : begin
+ SkipAll := True;
+ Result := True;
+ end;
+ 2 : Result := HandleDelete(AFileRec);
+ else Result := False;
+ end;
+ end;
+ end;
+
+var i: longint;
+ AList: TList;
+ CurrPath: string;
+ Fr: Single;
+ Response: integer;
+ DeleteAll, SkipToNext: boolean;
+
+begin
+ SkipAll := False;
+ AList := TList.Create;
+ AList.Clear;
+ with SenderThread do begin
+ CurrPath := IncludeTrailingPathDelimiter(Engine.Path);
+ if DataList.Count > 0 then
+ for i := 0 to DataList.Count - 1 do
+ with PDataItem(DataList[i])^ do
+ if (not UpDir) and Selected then
+ if IsDir and (not IsLnk)
+ then Engine.FillDirFiles(CurrPath + String(AName), AList, 1)
+ else AList.Add(Engine.GetFileInfoSL(CurrPath + String(AName)));
+ if (AList.Count = 0) and Assigned(SelectedItem) and (not SelectedItem^.UpDir) then
+ with SelectedItem^ do
+ if IsDir and (not IsLnk)
+ then Engine.FillDirFiles(CurrPath + String(AName), AList, 1)
+ else AList.Add(Engine.GetFileInfoSL(CurrPath + String(AName)));
+ if Engine.ChangeDir(CurrPath, False) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour might occur.']);
+ Engine.ExplicitChDir('/');
+
+ SetProgress1Params(AList.Count);
+ CommitGUIUpdate;
+ DeleteAll := False;
+ SkipToNext := False;
+
+// DebugWriteListSL(AList);
+
+ if AList.Count = 1 then Fr := 1 else Fr := 100 / (AList.Count - 1);
+ if AList.Count > 0 then
+ for i := 0 to AList.Count - 1 do begin
+ if Cancelled then begin
+ FCancelMessage := LANGUserCancelled;
+ FShowCancelMessage := True;
+ Break;
+ end;
+ if SkipToNext and (PDataItemSL(AList[i])^.Level > 1) then Continue;
+ if SkipToNext and (PDataItemSL(AList[i])^.Level = 1) and (not PDataItemSL(AList[i])^.Stage1) then begin
+ SkipToNext := False;
+ Continue;
+ end;
+ // Check for non-empty directory
+ if (not DeleteAll) and (PDataItemSL(AList[i])^.Level = 1) and PDataItemSL(AList[i])^.Stage1 and PDataItemSL(AList[i])^.IsDir and
+ (not PDataItemSL(AList[i])^.IsLnk) and (i < AList.Count - 2) and (PDataItemSL(AList[i + 1])^.Level = 2) then
+ begin
+ Response := ShowDirDeleteDialog(4, Format(LANGTheDirectorySIsNotEmpty, [ANSIToUTF8(string(PDataItemSL(AList[i])^.AName))]),
+ LANGDoYouWantToDeleteItWithAllItsFilesAndSubdirectories);
+ case Response of
+ 1 : ; // Do nothing in this case - I will not bother with changing the structure; it works :-)
+ 2 : DeleteAll := True;
+ 3 : SkipToNext := True;
+ else Break;
+ end;
+ end;
+ // Process delete
+ if not HandleDelete(AList[i]) then Break;
+ UpdateProgress1(i, Format('%d%%', [Round(Fr * i)]));
+ UpdateCaption1(ANSIToUTF8(PDataItemSL(AList[i])^.AName));
+ CommitGUIUpdate;
+ end;
+
+ // Free the objects
+ if AList.Count > 0 then
+ for i := AList.Count - 1 downto 0 do FreeDataItem(PDataItemSL(AList[i]));
+ AList.Clear;
+ AList.Free;
+ if Engine.ChangeDir(CurrPath, False) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour might occur.']);
+ end;
+ SenderThread.FDoneThread := True;
+end;
+
+
+
+
+
+
+
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+
+ // Return False to break the process
+ function CopyFilesWorker_ProgressFunc(Sender: Pointer; BytesDone: Int64): boolean; cdecl;
+ begin
+// DebugMsg(['*** CopyFilesWorker: ProgressFunc called (Sender=', QWord(Sender), ', BytesDone=', BytesDone, ')']);
+ 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(ParamFloat2 * BytesDone)]));
+ UpdateProgress2(ParamInt64 + BytesDone, Format('%d%%', [Round(ParamFloat1 * (ParamInt64 + BytesDone))]));
+ Result := not Cancelled;
+ CommitGUIUpdate;
+ end else DebugMsg(['*** CopyFilesWorker: Sender is not TWorkerThread']);
+ except
+ on E: Exception do DebugMsg(['*** Exception raised in ProgressFunc(Sender=', QWord(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
+ with TWorkerThread(Sender) do begin
+ if ParamBool2 then begin
+ Result := True;
+ Exit;
+ end;
+ case ErrorType of
+ 1 : s := LANGMemoryAllocationFailed;
+ 2 : s := LANGCannotOpenSourceFile;
+ 3 : s := LANGCannotOpenDestinationFile;
+ 4 : s := LANGCannotCloseDestinationFile;
+ 5 : s := LANGCannotCloseSourceFile;
+ 6 : s := LANGCannotReadFromSourceFile;
+ 7 : s := LANGCannotWriteToDestinationFile;
+ end;
+ if ParamBool1 then s2 := LANGCopyError
+ else s2 := LANGMoveError;
+ if ErrorType <> 1 then s3 := ANSIToUTF8(FileName)
+ else s3 := '';
+ case ShowDirDeleteDialog(3, s, s3, ANSIToUTF8(GetErrorString(ErrorNum)), s2) of
+ 0 : begin // Cancel button
+ Result := False;
+ CancelIt;
+ end;
+ 2 : Result := True; // Ignore
+ 3 : begin // Skip All
+ ParamBool2 := True; { Skip All Err }
+ Result := False; //** True?
+ end;
+ else {1, 124, 255 :} Result := False; // Skip
+ end;
+ end;
+ end;
+
+
+procedure CopyFilesWorker(SenderThread: TWorkerThread);
+// ParamFloat1 = Fr - internal
+// ParamFloat2 = Fr2 - internal
+// ParamInt64 = SizeDone - internal
+// ParamBool1 = ModeCopy - internal
+// ParamBool2 = SkipAllErr - internal
+// ParamBool3 = CopyMode
+// ParamBool4 = QuickRename
+// ParamBool5 = OneFile
+// ParamString1 = NewPath
+// ParamString2 = Filepath
+// ParamDataItem1 = QuickRenameDataItem
+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;
+ var fsrc, fdst: TEngineFileDes;
+ Error, BSize: integer;
+ Buffer: Pointer;
+ BytesDone, BytesRead, BytesWritten: Int64;
+ Res: boolean;
+ begin
+ DebugMsg(['ManualCopyFile: ', SourceFile, ' ---> ', DestFile]);
+ with SenderThread do begin
+ Result := False;
+ Error := 0;
+ fsrc := SrcEngine.OpenFile(SourceFile, omRead, Error);
+ if Error <> 0 then begin
+ CopyFilesWorker_ErrorFunc(SenderThread, 2, Error, SourceFile); // Cannot open source file
+ Exit;
+ end;
+ if Append then fdst := DestEngine.OpenFile(DestFile, omAppend, Error)
+ else fdst := DestEngine.OpenFile(DestFile, omWrite, Error);
+ if Error <> 0 then begin
+ SrcEngine.CloseFile(fsrc);
+ CopyFilesWorker_ErrorFunc(SenderThread, 3, Error, SourceFile); // Cannot open target file
+ Exit;
+ end;
+
+ BytesDone := 0;
+ Res := True;
+
+ BSize := DestEngine.GetBlockSize;
+ Buffer := Libc.malloc(BSize);
+ if Buffer = nil then begin
+ CopyFilesWorker_ErrorFunc(SenderThread, 1, errno, SourceFile); // Memory allocation failed
+ Libc.free(Buffer);
+ Exit;
+ end;
+ Libc.memset(Buffer, 0, BSize);
+
+ BytesWritten := 0;
+ repeat
+ BytesRead := SrcEngine.ReadFile(fsrc, Buffer, BSize, Error);
+ if (BytesRead = 0) and (Error <> 0) then
+ Res := CopyFilesWorker_ErrorFunc(SenderThread, 6, Error, SourceFile); // Cannot read from source file
+ if BytesRead > 0 then begin
+ BytesWritten := DestEngine.WriteFile(fdst, Buffer, BytesRead, Error);
+ if (BytesWritten < BytesRead) then
+ Res := CopyFilesWorker_ErrorFunc(SenderThread, 7, Error, DestFile); // Cannot write to source file
+ end;
+ Inc(BytesDone, BytesRead);
+ if not CopyFilesWorker_ProgressFunc(SenderThread, BytesDone) then begin
+ Res := False;
+ Break;
+ end;
+ until (BytesRead = 0) or (BytesWritten < BytesRead);
+ Libc.free(Buffer);
+
+ if DestEngine.CloseFile(fdst) <> 0 then begin
+ CopyFilesWorker_ErrorFunc(SenderThread, 4, errno, DestFile); // Cannot close target file
+ Exit;
+ end;
+ if SrcEngine.CloseFile(fsrc) <> 0 then begin
+ CopyFilesWorker_ErrorFunc(SenderThread, 5, errno, SourceFile); // Cannot close source file
+ Exit;
+ end;
+ Result := Res;
+ end;
+ end;
+
+ // Returns True if the file was successfully copied and will be deleted on move
+ function LocalCopyFile(SourceFile, DestFile: string; Append: boolean): boolean;
+ var DataSrc, DataDest: PDataItemSL;
+ begin
+ try
+ with SenderThread do begin
+ if ((SrcEngine is TLocalTreeEngine) and (DestEngine is TLocalTreeEngine)) or
+ ((SrcEngine is TLocalTreeEngine) and (not (DestEngine is TLocalTreeEngine)))
+ then Result := DestEngine.CopyFileIn(SenderThread, SourceFile, DestFile, @CopyFilesWorker_ProgressFunc, @CopyFilesWorker_ErrorFunc, Append) else
+// DebugMsg(['2 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$']);
+ if (not (SrcEngine is TLocalTreeEngine)) and (DestEngine is TLocalTreeEngine)
+ then Result := SrcEngine.CopyFileOut(SenderThread, SourceFile, DestFile, @CopyFilesWorker_ProgressFunc, @CopyFilesWorker_ErrorFunc, Append)
+ // both files are on different engines, we will have to handle the copy process ourselves
+ else Result := ManualCopyFile(SourceFile, DestFile, Append);
+// DebugMsg(['3 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$']);
+
+ // If size differs, then delete target file
+ if (not Append) and (not Result) then begin
+// DebugMsg(['4 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$']);
+ DataSrc := SrcEngine.GetFileInfoSL(SourceFile);
+ if DataSrc = nil then Exit;
+ DataDest := DestEngine.GetFileInfoSL(DestFile);
+ if DataDest = nil then Exit;
+ if DataSrc^.Size <> DataDest^.Size then DestEngine.Remove(DestFile);
+ end;
+ end;
+// DebugMsg(['(II) CopyFilesWorker.LocalCopyFile: finished']);
+ except
+ on E: Exception do DebugMsg(['*** Exception raised in LocalCopyFile(SourceFile=', SourceFile, ', DestFile=', DestFile, ', Append=', Append, '): (', E.ClassName, '): ', E.Message]);
+ end;
+ end;
+
+ function IsOnSameFS(SrcPath, DestPath: string): boolean;
+ begin
+ DebugMsg(['### IsOnSameFS: "', SrcPath, '" vs. "', DestPath, '"'#10'## Prefix = "', SenderThread.SrcEngine.GetPrefix, '" vs. "', SenderThread.DestEngine.GetPrefix, '"']);
+ with SenderThread do
+ if SrcEngine.GetPrefix <> DestEngine.GetPrefix
+ then Result := False
+ else Result := DestEngine.IsOnSameFS(SrcPath, DestPath);
+ end;
+
+ function TwoSameFiles(Path1, Path2: string; TestCaseInsensitiveFS: boolean): boolean;
+ begin
+ with SenderThread do begin
+ if SrcEngine.GetPrefix <> DestEngine.GetPrefix then Result := False else
+ if AnsiCompareStr(Path1, Path2) = 0 then Result := True else
+ Result := TestCaseInsensitiveFS and DestEngine.TwoSameFiles(Path1, Path2);
+ end;
+ end;
+
+ function DoOperation(AFileRec: PDataItemSL; const Dst: string; var ErrorKind: integer; const Append: boolean): integer;
+ begin
+ try
+ ErrorKind := 0;
+ Result := 0;
+ with SenderThread do
+ with AFileRec^ do begin
+ if IsLnk then begin
+ // Explicit copy the file
+ if ParamBool3 or (not IsOnSameFS(String(AName), ExtractFileDir(Dst))) then begin
+ ErrorKind := DestEngine.MakeSymLink(Dst, String(LnkPointTo));
+ if ErrorKind <> 0 then Result := ERRCreateLink;
+ if not ParamBool3 then begin
+ ErrorKind := SrcEngine.Remove(String(AName));
+ if ErrorKind <> 0 then Result := ERRRemove;
+ end;
+ end else begin // Move the file
+ ErrorKind := DestEngine.RenameFile(String(AName), Dst);
+ if ErrorKind <> 0 then Result := ERRCopyMove;
+ end;
+ end else // is not link
+ if ParamBool3 then begin // Copy mode
+ if LocalCopyFile(String(AName), Dst, Append) then begin
+ if IsOnRO and ConfClearReadOnlyAttr and (Mode and S_IWUSR = 0) then Mode := Mode or S_IWUSR;
+ DestEngine.Chmod(Dst, Mode);
+ DestEngine.Chown(Dst, UID, GID);
+ DestEngine.ChangeTimes(Dst, mtime, atime);
+ end;
+ end else // Move mode
+ if IsOnSameFS(String(AName), ExtractFileDir(Dst)) then begin
+ if TwoSameFiles(String(AName), Dst, True) and (not TwoSameFiles(String(AName), Dst, False)) then begin
+ DebugMsg(['*** Activating double-rename due to renaming on case-insensitive FS']);
+ ErrorKind := DestEngine.RenameFile(String(AName), Dst + '_tcmd');
+ if ErrorKind = 0 then ErrorKind := DestEngine.RenameFile(Dst + '_tcmd', Dst);
+ end else ErrorKind := DestEngine.RenameFile(String(AName), Dst);
+ if ErrorKind <> 0 then Result := ERRCopyMove;
+ end else begin
+ if LocalCopyFile(String(AName), Dst, Append) then begin
+ if IsOnRO and ConfClearReadOnlyAttr and (Mode and S_IWUSR = 0) then Mode := Mode or S_IWUSR;
+ DestEngine.Chmod(Dst, Mode);
+ DestEngine.Chown(Dst, UID, GID);
+ DestEngine.ChangeTimes(Dst, mtime, atime);
+ if not Cancelled then begin
+ ErrorKind := SrcEngine.Remove(String(AName));
+ if ErrorKind <> 0 then Result := ERRRemove;
+ end;
+ end;
+ end;
+ end;
+// DebugMsg(['(II) CopyFilesWorker.DoOperation: finished']);
+ except
+ on E: Exception do DebugMsg(['*** Exception raised in DoOperation(AFileRec=', QWord(AFileRec), ', Dst=', Dst, ', ErrorKind=', ErrorKind, ', Append=', Append, '): (', E.ClassName, '): ', E.Message]);
+ end;
+ end;
+
+ // Return False to break the processing (Cancel)
+ function HandleCopy(AFileRec: PDataItemSL; NewFilePath: string): boolean;
+ var Res, Response, ErrorKind, r: integer;
+ Item: PDataItemSL;
+ s, s1, s3, cap: string;
+ begin
+ Result := True;
+ try
+ with SenderThread do begin
+
+ // Second stage - change permissions
+ if (not AFileRec^.Stage1) and (ParamBool3 or ((not ParamBool3) and (not AFileRec^.ForceMove))) then
+ with AFileRec^ do begin
+ if IsOnRO and ConfClearReadOnlyAttr and (Mode and S_IWUSR = 0) then Mode := Mode or S_IWUSR;
+ DestEngine.Chmod(NewFilePath, Mode);
+ DestEngine.Chown(NewFilePath, UID, GID);
+ DestEngine.ChangeTimes(NewFilePath, mtime, atime);
+ if not ParamBool3 then SrcEngine.Remove(String(AName)); // Remove directory
+ Exit;
+ end;
+
+ // First stage - copy data
+ if AFileRec^.IsDir then begin
+ Res := 0;
+ if AFileRec^.ForceMove and (not ParamBool3)
+ then begin
+ if TwoSameFiles(ExcludeTrailingPathDelimiter(string(AFileRec^.AName)), ExcludeTrailingPathDelimiter(string(AFileRec^.ADestination)), True) and (not
+ TwoSameFiles(ExcludeTrailingPathDelimiter(string(AFileRec^.AName)), ExcludeTrailingPathDelimiter(string(AFileRec^.ADestination)), False)) then
+ begin
+ DebugMsg(['*** Activating double-rename due to renaming on case-insensitive FS']);
+ ErrorKind := DestEngine.RenameFile(string(AFileRec^.AName), ExcludeTrailingPathDelimiter(string(AFileRec^.ADestination)) + '_tcmd');
+ if ErrorKind = 0 then ErrorKind := DestEngine.RenameFile(ExcludeTrailingPathDelimiter(string(AFileRec^.ADestination)) + '_tcmd', ExcludeTrailingPathDelimiter(string(AFileRec^.ADestination)));
+ end else ErrorKind := DestEngine.RenameFile(string(AFileRec^.AName), string(AFileRec^.ADestination));
+ if ErrorKind <> 0 then Res := ERRCopyMove
+ else Res := 0;
+ end else
+ if not DestEngine.DirectoryExists(NewFilePath, True) then begin
+ ErrorKind := DestEngine.MakeDir(NewFilePath);
+ if ErrorKind <> 0 then Res := ERRMkDir
+ else Res := 0;
+ end;
+ end else begin // not a directory
+ if not DestEngine.DirectoryExists(ExtractFileDir(NewFilePath), True) then DestEngine.MakeDir(ExtractFileDir(NewFilePath));
+ SetProgress1Params(AFileRec^.Size + Ord(AFileRec^.Size = 0));
+ if AFileRec^.Size <= 1 then ParamFloat2 := 1 else ParamFloat2 := 100 / (AFileRec^.Size - 1);
+ CopyFilesWorker_ProgressFunc(SenderThread, 0);
+ Res := 0;
+ if DestEngine.FileExists(NewFilePath, True) and
+ (not (not ParamBool3 and (not TwoSameFiles(NewFilePath, AFileRec^.AName, False)) and TwoSameFiles(NewFilePath, AFileRec^.AName, True)))
+ then begin
+ Response := DefResponse;
+ Item := DestEngine.GetFileInfoSL(NewFilePath);
+ if Response = 0 then begin
+ Response := ShowOverwriteDialog(1 + Ord(ParamBool3), Format(LANGOverwriteS, [ANSIToUTF8(NewFilePath)]),
+ Format(LANGOvewriteSBytesS, [FormatSize(Item^.Size, 0), FormatDateTime('ddddd t', Item^.ModifyTime)]),
+ Format(LANGWithFileS, [ANSIToUTF8(AFileRec^.AName)]),
+ Format(LANGOvewriteSBytesS, [FormatSize(AFileRec^.Size, 0), FormatDateTime('ddddd t', AFileRec^.ModifyTime)]),
+ ANSIToUTF8(ExtractFileName(NewFilePath)), ExtractFileName(AFileRec^.AName), ExtractFileName(NewFilePath));
+ s := FOverwriteRenameStr;
+ case Response of
+ // 1: Overwrite
+ // 3: Skip
+ 2 {Overwrite All}, 5 {Overwrite All Older}, 6 {Skip All}: DefResponse := Response;
+ 4 {Cancel}, 124 {Close Window}, 255: begin
+ Result := False;
+ Exit;
+ end;
+ 7: {Rename} begin
+ NewFilePath := Copy(NewFilePath, 1, LastDelimiter(PathDelim, NewFilePath)) + s;
+ Result := HandleCopy(AFileRec, NewFilePath);
+ Exit;
+ end;
+ 8 {Append}: begin
+ Res := DoOperation(AFileRec, NewFilePath, ErrorKind, True);
+ end;
+ end;
+ end;
+
+ // Remove destination file if exists and should be overwritten
+ if (Response in [1, 2]) or ((Response = 5) and (Item^.ModifyTime < AFileRec^.ModifyTime)) then begin
+ r := DestEngine.Remove(NewFilePath);
+ while r <> 0 do begin
+ Res := ShowDirDeleteDialog(1, LANGTheFile, ANSIToUTF8(String(NewFilePath)),
+ Format(LANGCouldNotBeDeletedS, [ANSIToUTF8(GetErrorString(r))]), LANGCopyError);
+ case Res of
+ 1: begin
+ Result := True;
+ Exit;
+ end;
+ 2: r := DestEngine.Remove(NewFilePath);
+ 0, 124, 255: begin
+ Result := False;
+ Exit;
+ end;
+ end;
+ end;
+ Res := DoOperation(AFileRec, NewFilePath, ErrorKind, False);
+ end;
+ end else Res := DoOperation(AFileRec, NewFilePath, ErrorKind, False);
+ end;
+
+ // Error handling
+ if (Res <> 0) and (not SkipAll) then begin
+ if ParamBool3 then cap := LANGCopy
+ else cap := LANGMove;
+ case Res of
+ ERRCreateLink: begin
+ s1 := LANGTheSymbolicLink;
+ if ErrorKind = 0 then s3 := LANGCouldNotBeCreated else
+ s3 := Format(LANGCouldNotBeCreatedS, [ANSIToUTF8(GetErrorString(ErrorKind))]);
+ end;
+ ERRMkDir: begin
+ s1 := LANGTheDirectory;
+ if ErrorKind = 0 then s3 := LANGCouldNotBeCreated else
+ s3 := Format(LANGCouldNotBeCreatedS, [ANSIToUTF8(GetErrorString(ErrorKind))]);
+ end;
+ ERRRemove: begin
+ if AFileRec^.IsDir then s1 := LANGTheDirectory else
+ if AFileRec^.IsLnk then s1 := LANGTheSymbolicLink else
+ s1 := LANGTheFile;
+ if ErrorKind = 0 then s3 := LANGCouldNotBeDeleted else
+ s3 := Format(LANGCouldNotBeDeletedS, [ANSIToUTF8(GetErrorString(ErrorKind))]);
+ end;
+ ERRCopyMove: begin
+ if ParamBool3 then s1 := LANGCannotCopyFile else
+ s1 := LANGCannotMoveFile;
+ if ErrorKind = 0 then s3 := '' else
+ s3 := ANSIToUTF8(GetErrorString(ErrorKind));
+ end;
+ end;
+ Response := ShowDirDeleteDialog(1, s1, ANSIToUTF8(String(NewFilePath)), s3, cap);
+ case Response of
+ 1 : Result := True; // Skip
+ 2 : Result := HandleCopy(AFileRec, NewFilePath); // Retry
+ 3 : begin // Skip All
+ SkipAll := True;
+ Result := True;
+ end;
+ 0, 124, 255 : Result := False; // Cancel
+ end;
+ end;
+ end;
+// DebugMsg(['(II) CopyFilesWorker.HandleCopy: finished']);
+ except
+ on E: Exception do DebugMsg(['*** Exception raised in HandleCopy(AFileRec=', QWord(AFileRec), ', NewFilePath=', NewFilePath, '): (', E.ClassName, '): ', E.Message]);
+ end;
+ end;
+
+ procedure HandleProcessPattern(AList: TList; CurrPath, FullPath, ParamFileName: string; ParamDir, Ren: boolean);
+ var s, s2: string;
+ b, CaseInsensitiveRename: boolean;
+ Info: PDataItemSL;
+ begin
+ with SenderThread do
+ if not Ren then begin
+ if ParamDir then SrcEngine.FillDirFiles(FullPath, AList, 1)
+ else begin
+ Info := SrcEngine.GetFileInfoSL(FullPath);
+ if Info = nil then DebugMsg(['$$$ Copy: Something went wrong while building the filelist...'])
+ else AList.Add(Info);
+ end;
+ end else begin
+ s := ProcessPattern(DestEngine, ParamString1, CurrPath, ParamFileName, ParamDir);
+ CaseInsensitiveRename := (AnsiCompareStr(ParamString1, ParamFileName) <> 0) and (AnsiCompareText(ParamString1, ParamFileName) = 0) and
+ ParamDir and DestEngine.TwoSameFiles(IncludeTrailingPathDelimiter(CurrPath) + ParamString1, IncludeTrailingPathDelimiter(CurrPath) + ParamFileName);
+// DebugMsg(['HandleProcessPattern: s = ', s]);
+ b := False;
+ if ParamDir then begin
+ b := DestEngine.DirectoryExists(ExcludeTrailingPathDelimiter(s)) and (not CaseInsensitiveRename);
+ if (not b) and (s <> '/') then begin
+ s2 := ExcludeTrailingPathDelimiter(s);
+ s2 := ExcludeTrailingPathDelimiter(Copy(s2, 1, LastDelimiter('/', s2)));
+ b := DestEngine.DirectoryExists(ExcludeTrailingPathDelimiter(s2));
+ end;
+ end;
+ if (not ParamDir) or (ParamDir and b and IsOnSameFS(ExcludeTrailingPathDelimiter(FullPath), s2))
+ then begin
+ Info := SrcEngine.GetFileInfoSL(FullPath);
+ if Info = nil then DebugMsg(['$$$ Copy: Something went wrong while building the filelist...'])
+ else begin
+ Info^.ADestination := strdup(PChar(s));
+ Info^.ForceMove := True;
+ AList.Add(Info);
+ end;
+ end else SrcEngine.FillDirFiles(FullPath, AList, 1);
+ end;
+ end;
+
+var i: longint;
+ List: TList;
+ CurrPath, SaveDestPath, SaveSrcPath, s: string;
+ MaxSize: Int64;
+begin
+ List := TList.Create;
+ List.Clear;
+ with SenderThread do begin
+ ErrorHappened := False;
+ FCancelled := False;
+ SaveSrcPath := '';
+ CurrPath := IncludeTrailingPathDelimiter(SrcEngine.Path);
+ SaveDestPath := DestEngine.Path;
+ ParamString1 := ExcludeTrailingPathDelimiter(ParamString1);
+ if ParamString1 = '' then ParamString1 := PathDelim;
+
+ if ParamBool5 then begin // HandleVFSFromArchive
+ if not ExtractFromVFSAll then HandleProcessPattern(List, CurrPath, ParamString2, ExtractFileName(ParamString2), False, False)
+ else begin
+ SaveSrcPath := IncludeTrailingPathDelimiter(SrcEngine.Path);
+ SrcEngine.SetPath('/');
+ CurrPath := '/';
+ HandleProcessPattern(List, '/', '/', '', True, False);
+ end;
+ end else
+ if ParamBool4 then begin // Quick-Rename
+ with ParamDataItem1^ do
+ HandleProcessPattern(List, CurrPath, CurrPath + String(AName), String(AName), IsDir and (not IsLnk), True);
+ end else begin // Not Quick-Rename
+ if not ExtractFromVFSMode then begin
+ if DataList.Count > 0 then
+ for i := 0 to DataList.Count - 1 do
+ with PDataItem(DataList[i])^ do
+ if (not UpDir) and Selected
+ then HandleProcessPattern(List, CurrPath, CurrPath + String(AName), String(AName), IsDir and (not IsLnk), not ParamBool3);
+ if (List.Count = 0) and Assigned(SelectedItem) and (not SelectedItem^.UpDir) then
+ with SelectedItem^ do
+ HandleProcessPattern(List, CurrPath, CurrPath + String(AName), String(AName), IsDir and (not IsLnk), not ParamBool3);
+ end else begin // Extract from VFS mode
+ DebugMsg(['CopyFilesWorker: Should not be reached']);
+ if (not ExtractFromVFSAll) and Assigned(SelectedItem)
+ then HandleProcessPattern(List, CurrPath, CurrPath + String(SelectedItem^.AName), String(SelectedItem^.AName), SelectedItem^.IsDir and (not SelectedItem^.IsLnk), not ParamBool3)
+ else begin
+ SaveSrcPath := IncludeTrailingPathDelimiter(SrcEngine.Path);
+ SrcEngine.SetPath('/');
+ CurrPath := '/';
+ HandleProcessPattern(List, '/', '/', '', True, False);
+ end;
+ end;
+ end;
+
+{ if DestEngine.ChangeDir(CurrPath, False) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour may occur.']);
+ if SrcEngine.ChangeDir(CurrPath, False) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour may occur.']); }
+// DebugWriteListSL(List);
+
+ __chdir('/');
+ // Compute total size of files to copy
+ MaxSize := 0; ParamInt64 := 0;
+ if List.Count > 0 then
+ for i := 0 to List.Count - 1 do
+ if PDataItemSL(List[i])^.Stage1 and (PDataItemSL(List[i])^.Size > 0) and (not PDataItemSL(List[i])^.IsDir) and (not PDataItemSL(List[i])^.IsLnk)
+ then Inc(MaxSize, PDataItemSL(List[i])^.Size);
+ SrcEngine.BlockSize := ComputeBlockSize(MaxSize);
+ DestEngine.BlockSize := ComputeBlockSize(MaxSize);
+
+ // Prepare the Progress window
+ SetProgress2Params(MaxSize + Ord(MaxSize = 0));
+ UpdateProgress1(0, '0%');
+ UpdateProgress2(0, '0%');
+ CommitGUIUpdate;
+
+ DefResponse := 0;
+ ParamBool1 := ParamBool3;
+ SkipAll := False;
+ ParamBool2 := False;
+
+ if MaxSize < 2 then ParamFloat1 := 1 else ParamFloat1 := 100 / (MaxSize - 1);
+ if List.Count > 0 then
+ for i := 0 to List.Count - 1 do begin
+ if Assigned(PDataItemSL(List[i])^.ADestination)
+ then s := string(PDataItemSL(List[i])^.ADestination)
+ else
+ begin
+ s := ProcessPattern(DestEngine, ParamString1, CurrPath, Copy(PDataItemSL(List[i])^.AName, Length(CurrPath) + 1, Length(PDataItemSL(List[i])^.AName) - Length(CurrPath)),
+ PDataItemSL(List[i])^.IsDir and (not PDataItemSL(List[i])^.IsLnk));
+// DebugMsg(['s2 = ', Copy(PDataItemSL(List[i])^.AName, Length(CurrPath) + 1, Length(PDataItemSL(List[i])^.AName) - Length(CurrPath)), ', s = ', s]);
+ end;
+ UpdateCaption1(Format(LANGFromS, [ANSIToUTF8(string(PDataItemSL(List[i])^.AName))]));
+ UpdateCaption2(Format(LANGToS, [ANSIToUTF8(s)]));
+ CommitGUIUpdate;
+ if TwoSameFiles(s, string(PDataItemSL(List[i])^.AName), ParamBool3) and (not PDataItemSL(List[i])^.IsDir) then begin
+ FCancelMessage := LANGCannotCopyFileToItself;
+ FShowCancelMessage := True;
+ ErrorHappened := True;
+ Break;
+ end;
+ if s <> string(PDataItemSL(List[i])^.AName) then
+ if not HandleCopy(List[i], s) then begin
+ ErrorHappened := True;
+ Break;
+ end;
+ if (not PDataItemSL(List[i])^.IsDir) and (not PDataItemSL(List[i])^.IsLnk)
+ then Inc(ParamInt64, PDataItemSL(List[i])^.Size);
+ if Cancelled then begin
+ FCancelMessage := LANGUserCancelled;
+ FShowCancelMessage := True;
+ ErrorHappened := True;
+ Break;
+ end;
+ end;
+
+ // Free the objects
+ if List.Count > 0 then
+ for i := List.Count - 1 downto 0 do FreeDataItem(PDataItemSL(List[i]));
+ List.Clear;
+ List.Free;
+ if DestEngine.ChangeDir(SaveDestPath, False) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour might occur.']);
+ if SaveSrcPath <> '' then CurrPath := SaveSrcPath;
+ if SrcEngine.ChangeDir(CurrPath, False) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour might occur.']);
+ end;
+ SenderThread.FDoneThread := True;
+ DebugMsg(['(II) CopyFilesWorker: finished']);
+end;
+
+
+(********************************************************************************************************************************)
+function ComputeBlockSize(TotalSize: Int64): longint;
+begin
+ if TotalSize < 512*1024 then Result := 32*1024 else
+ if TotalSize < 1024*1024 then Result := 64*1024 else
+ if TotalSize < 2048*1024 then Result := 96*1024 else
+ if TotalSize < 4096*1024 then Result := 128*1024 else
+ if TotalSize < 8192*1024 then Result := 256*1024 else
+{ if TotalSize < 256*1024*1024 then Result := 512*1024 else
+ if TotalSize < 768*1024*1024 then Result := 2048*1024 else }
+ Result := 4096*1024;
+end;
+
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+procedure DebugWriteListSL(List: TList);
+var i: integer;
+ Item: PDataItemSL;
+begin
+ if not ParamDebug then Exit;
+ if not Assigned(List) then begin
+ WriteLn('List not assigned');
+ Exit;
+ end;
+ WriteLn('********************************************************');
+ WriteLn('** List.Count = ', List.Count, ' base @ ', integer(pointer(List)));
+ if List.Count > 0 then
+ for i := 0 to List.Count - 1 do
+ if not Assigned(List[i]) then WriteLn('**** List Item idx ', i, '; base @ nil') else
+ try
+ WriteLn('**** List Item idx ', i, '; base @ ', integer(List[i]), '; sizeof = ', SizeOf(List[i]));
+ Item := List[i];
+ WriteLn(' Stage1: ', Item^.Stage1, ', Level: ', Item^.Level, ', IsDir: ', Item^.IsDir, ', IsLnk: ', Item^.IsLnk, ', ForceMove: ', Item^.ForceMove{, ', Size: ', Item^.Size});
+ WriteLn(' AName: ', Item^.AName);
+ WriteLn(' LnkPointTo: ', Item^.LnkPointTo);
+ WriteLn(' ADestination: ', Item^.ADestination);
+ except
+ on E: Exception do
+ WriteLn('(EE): Exception ', E.ClassName, ' raised: ', E.Message);
+ end;
+ WriteLn('** End of listing');
+ WriteLn('********************************************************');
+end;
+
+procedure DebugWriteList(List: TList);
+var i: integer;
+ Item: PDataItem;
+begin
+ if not ParamDebug then Exit;
+ if not Assigned(List) then begin
+ WriteLn('List not assigned');
+ Exit;
+ end;
+ WriteLn('********************************************************');
+ WriteLn('** List.Count = ', List.Count, ' base @ ', integer(pointer(List)));
+ if List.Count > 0 then
+ for i := 0 to List.Count - 1 do
+ if not Assigned(List[i]) then WriteLn('**** List Item idx ', i, '; base @ nil') else
+ try
+ WriteLn('**** List Item idx ', i, '; base @ ', integer(List[i]), '; sizeof = ', SizeOf(List[i]));
+ Item := List[i];
+ WriteLn(' IsDir: ', Item^.IsDir, ', IsLnk: ', Item^.IsLnk, ', Size: ', Item^.Size);
+ WriteLn(' AName: ', Item^.AName);
+ WriteLn(' LnkPointTo: ', Item^.LnkPointTo);
+ except
+ on E: Exception do
+ WriteLn('(EE): Exception ', E.ClassName, ' raised: ', E.Message);
+ end;
+ WriteLn('** End of listing');
+ WriteLn('********************************************************');
+end;
+
+(********************************************************************************************************************************)
+procedure FindNextSelected(ListView: TGTKListView; DataList: TList; var Item1, Item2: string);
+var i: integer;
+ SelCount: longint;
+begin
+ SelCount := 0;
+ Item1 := ''; Item2 := '';
+ if (not Assigned(ListView.Selected)) or PDataItem(ListView.Selected.Data)^.UpDir then Exit;
+ if DataList.Count > 0 then
+ for i := 0 to DataList.Count - 1 do
+ with PDataItem(DataList[i])^ do
+ if Selected and (not UpDir) then Inc(SelCount);
+ Item1 := string(PDataItem(ListView.Selected.Data)^.AName);
+ if (PDataItem(ListView.Selected.Data)^.Selected and (SelCount > 0)) or (SelCount = 0) then begin
+ if ListView.ConvertToSorted(ListView.Selected.Index) < ListView.Items.Count then
+ for i := ListView.ConvertToSorted(ListView.Selected.Index) + 1 to DataList.Count - 1 do
+ if not PDataItem(DataList[ListView.ConvertFromSorted(i)])^.Selected then begin
+ Item2 := string(PDataItem(DataList[ListView.ConvertFromSorted(i)])^.AName);
+ Break;
+ end;
+ if (Item2 = '') and (ListView.ConvertToSorted(ListView.Selected.Index) > 0) then
+ for i := ListView.ConvertToSorted(ListView.Selected.Index) - 1 downto 0 do
+ if (not PDataItem(DataList[ListView.ConvertFromSorted(i)])^.Selected) and
+ (not PDataItem(DataList[ListView.ConvertFromSorted(i)])^.UpDir) then
+ begin
+ Item2 := string(PDataItem(DataList[ListView.ConvertFromSorted(i)])^.AName);
+ Break;
+ end;
+ end;
+end;
+
+(********************************************************************************************************************************)
+procedure UnselectAll(ListView: TGTKListView; DataList: TList);
+var i: integer;
+begin
+ if DataList.Count > 0 then
+ for i := 0 to DataList.Count - 1 do
+ with PDataItem(DataList[i])^ do
+ if Selected then begin
+ Selected := False;
+ ListView.Items[i].RedrawRow;
+ end;
+end;
+
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+function CRCGetInfo(FileName: string; Engine: TPanelEngine; var TargetName: string; var TargetCRC: LongWord; var Size: Int64): boolean;
+
+ procedure ProcessLine(Str: string);
+ var UPS: string;
+ begin
+ try
+ TrimCRLFESC(Str);
+ if Length(Str) < 1 then Exit;
+ UPS := AnsiUpperCase(Str);
+ if Pos('FILENAME', UPS) = 1 then TargetName := Trim(Copy(Str, Pos('=', Str) + 1, Length(Str) - Pos('=', Str))) else
+ if Pos('SIZE', UPS) = 1 then Size := StrToInt64Def(Trim(Copy(Str, Pos('=', Str) + 1, Length(Str) - Pos('=', Str))), 0) else
+ if Pos('CRC32', UPS) = 1 then TargetCRC := StrToInt64Def('$' + Trim(Copy(Str, Pos('=', Str) + 1, Length(Str) - Pos('=', Str))), 0);
+ except end;
+ end;
+
+const CRCBlockSize = 32768;
+var i, Error, Count, Start: integer;
+ FD: TEngineFileDes;
+ Buffer: Pointer;
+ s: string;
+begin
+ Result := False;
+ if Pos('.', FileName) > 1 then FileName := ChangeFileExt(FileName, '.crc')
+ else FileName := FileName + '.crc';
+ try
+ Buffer := Libc.malloc(CRCBlockSize);
+ Libc.memset(Buffer, 0, CRCBlockSize);
+ except
+ Application.MessageBox(LANGAnErrorOccuredWhileInitializingMemoryBlock, [mbOK], mbError, mbNone, mbOK);
+ Exit;
+ end;
+ FD := Engine.OpenFile(FileName, omRead, Error);
+ if Error <> 0 then Exit;
+
+ s := '';
+ repeat
+ Count := Engine.ReadFile(FD, Buffer, CRCBlockSize, Error);
+ if Error <> 0 then begin
+ Libc.free(Buffer);
+ Engine.CloseFile(FD);
+ Exit;
+ end;
+ // processing begins
+ Start := 1;
+ if Count > 0 then
+ for i := 0 to Count - 1 do
+ if (PByteArray(Buffer)^[i] in [13, 10]) or (i = Count - 1) then begin
+ s := s + Copy(PChar(Buffer), Start, i - Start + 1 + Ord(i = Count - 1));
+ Start := i + 2;
+ if PByteArray(Buffer)^[i] in [13, 10] then begin
+ ProcessLine(s);
+ s := '';
+ end;
+ end;
+ // processing ends
+ until Count < CRCBlockSize;
+ if Length(s) > 0 then ProcessLine(s);
+
+ Engine.CloseFile(FD);
+ Libc.free(Buffer);
+ Result := True;
+end;
+
+(********************************************************************************************************************************)
+procedure MergeFilesWorker(SenderThread: TWorkerThread);
+// ParamBool1 = HasInitialCRC
+// ParamString1 = NewPath
+// ParamString2 = FileName
+// ParamString3 = TargetName
+// ParamLongWord1 = TargetCRC
+// ParamInt64 = TargetSize
+
+var FD: TEngineFileDes;
+ Error, Count, MergeBlockSize: integer;
+ Buffer: Pointer;
+ CurrentCRC: LongWord;
+ PrivateCancel: boolean;
+ SizeDone: Int64;
+ TargetName: string;
+
+
+ function PasteFile(FName: string): boolean;
+ var FDR: TEngineFileDes;
+ wCount: integer;
+ Stat: PDataItemSL;
+ begin
+ Result := False;
+ with SenderThread do begin
+ if ParamBool1 then UpdateCaption2(Format(LANGToS, [ANSIToUTF8(FName)]))
+ else UpdateCaption1(Format(LANGFromS, [ANSIToUTF8(FName)]));
+ UpdateProgress1(0, '0 %');
+ CommitGUIUpdate;
+ Stat := Engine.GetFileInfoSL(FName);
+ if not Assigned(Stat) then Exit;
+ SetProgress1Params(Stat^.Size);
+ FDR := Engine.OpenFile(FName, omRead, Error);
+ if Error <> 0 then Exit;
+ repeat
+ Count := Engine.ReadFile(FDR, Buffer, MergeBlockSize, Error);
+ if Error <> 0 then begin
+ Engine.CloseFile(FD);
+ Exit;
+ end;
+ wCount := Engine.WriteFile(FD, Buffer, Count, Error);
+ if (Error <> 0) or (Count <> wCount) then begin
+ FCancelMessage := Format(LANGAnErrorOccuredWhileWritingFileSS, [ANSIToUTF8(ExtractFileName(TargetName)), ANSIToUTF8(GetErrorString(Error))]);
+ FShowCancelMessage := True;
+ PrivateCancel := True;
+ Result := True; // Fake this to don't show next disc dialog
+ Exit;
+ end;
+ CurrentCRC := CRC32(CurrentCRC, Buffer, Count);
+ UpdateProgress1(FProgress1Pos + Count, Format('%d %%', [Trunc((FProgress1Pos + Count) / FProgress1Max * 100)]));
+ Inc(SizeDone, Count);
+ if ParamBool1 then UpdateProgress2(SizeDone, Format('%d %%', [Trunc(SizeDone / FProgress2Max * 100)]));
+ CommitGUIUpdate;
+ until (Count < MergeBlockSize) or Cancelled;
+ Engine.CloseFile(FDR);
+ end;
+ Result := True;
+ end;
+
+
+var CurrFile, SourcePath, TargetFinalName: string;
+ HasFinalCRC, b: boolean;
+ Stat: PDataItemSL;
+begin
+ with SenderThread do begin
+ HasFinalCRC := ParamBool1;
+ TargetFinalName := ParamString3;
+ if (Length(ParamString2) > 4) and (ANSIUpperCase(RightStr(ParamString2, 4)) = '.CRC')
+ then CurrFile := ChangeFileExt(ExtractFileName(ParamString2), '.001')
+ else CurrFile := ExtractFileName(ParamString2);
+ SourcePath := ExtractFilePath(ParamString2);
+ if ParamString3 = '' then ParamString3 := ChangeFileExt(ExtractFileName(ParamString2), '.out');
+ TargetName := ProcessPattern(Engine, ParamString1, Engine.Path, ParamString3, False);
+ if Engine.FileExists(TargetName, True) then
+ if ShowMessageBox(Format(LANGTheTargetFileSAlreadyExistsDoYouWantToOverwriteIt, [ANSIToUTF8(TargetName)]), [mbYes, mbNo], mbQuestion, mbNone, mbNo) = mbYes then
+ begin
+ Error := Engine.Remove(TargetName);
+ if Error <> 0 then begin
+ FCancelMessage := Format(LANGTheTargetFileSCannotBeRemovedS, [ANSIToUTF8(ExtractFileName(TargetName)), ANSIToUTF8(GetErrorString(Error))]);
+ FShowCancelMessage := True;
+ Exit;
+ end;
+ end else Exit;
+
+ Stat := Engine.GetFileInfoSL(ParamString2);
+ if Assigned(Stat) then MergeBlockSize := ComputeBlockSize(Stat^.Size)
+ else MergeBlockSize := 65536*4;
+ try
+ Buffer := Libc.malloc(MergeBlockSize);
+ Libc.memset(Buffer, 0, MergeBlockSize);
+ except
+ FCancelMessage := LANGAnErrorOccuredWhileInitializingMemoryBlock;
+ FShowCancelMessage := True;
+ Exit;
+ end;
+ FD := Engine.OpenFile(TargetName, omWrite, Error);
+ if Error <> 0 then begin
+ FCancelMessage := Format(LANGAnErrorOccuredWhileOpeningFileSS, [ANSIToUTF8(TargetName), ANSIToUTF8(GetErrorString(Error))]);
+ FShowCancelMessage := True;
+ Libc.free(Buffer);
+ Exit;
+ end;
+
+ CurrentCRC := $FFFFFFFF;
+ SizeDone := 0;
+ PrivateCancel := False;
+ if ParamBool1 then begin
+ SetProgress2Params(ParamInt64);
+ UpdateProgress2(0, '0 %');
+ UpdateCaption2(Format(LANGFromS, [ANSIToUTF8(TargetName)]));
+ CommitGUIUpdate;
+ end; { else begin
+ Label2.XAlign := 0;
+ Label2.XPadding := 20;
+ end; }
+
+ repeat
+ b := PasteFile(IncludeTrailingPathDelimiter(SourcePath) + CurrFile);
+ if not b then begin
+ PrivateCancel := ShowNewDirDialog(LANGMergeCaption, LANGPleaseInsertNextDiskOrGiveDifferentLocation, ANSIToUTF8(SourcePath)) <> integer(mbOK);
+ if not PrivateCancel then begin
+ SourcePath := UTF8ToANSI(FNewDirEdit);
+ if not HasFinalCRC then
+ HasFinalCRC := CRCGetInfo(IncludeTrailingPathDelimiter(SourcePath) + CurrFile, Engine, TargetFinalName, ParamLongWord1, ParamInt64);
+ Continue;
+ end;
+ end;
+ try
+ CurrFile := Copy(CurrFile, 1, LastDelimiter('.', CurrFile)) + Format('%.3d', [StrToInt(
+ Copy(CurrFile, LastDelimiter('.', CurrFile) + 1, Length(CurrFile) - LastDelimiter('.', CurrFile))) + 1]);
+ except
+ CurrFile := '';
+ end;
+ until (SizeDone = ParamInt64) or Cancelled or PrivateCancel {or ((not b) and (not HasInitialCRC))} or (CurrFile = '');
+ if (not ParamBool1) and HasFinalCRC then Engine.RenameFile(TargetName, IncludeTrailingPathDelimiter(ExtractFilePath(TargetName)) + TargetFinalName);
+ if Cancelled and (not PrivateCancel) then begin
+ FCancelMessage := LANGUserCancelled;
+ FShowCancelMessage := True;
+ end;
+ if not (Cancelled or PrivateCancel) then
+ if HasFinalCRC then begin
+ if not CurrentCRC = ParamLongWord1
+ then ShowMessageBox(Format(LANGMergeOfSSucceeded, [ANSIToUTF8(ExtractFileName(TargetFinalName))]), [mbOK], mbInfo, mbNone, mbOK)
+ else ShowMessageBox(LANGWarningCreatedFileFailsCRCCheck, [mbOK], mbWarning, mbNone, mbOK);
+ end else ShowMessageBox(Format(LANGMergeOfSSucceeded_NoCRCFileAvailable, [ANSIToUTF8(ExtractFileName(TargetFinalName))]), [mbOK], mbInfo, mbNone, mbOK);
+ Engine.CloseFile(FD);
+ end;
+ Libc.free(Buffer);
+ SenderThread.FDoneThread := True;
+end;
+
+(********************************************************************************************************************************)
+function WriteCRCFile(Engine: TPanelEngine; TargetFile, SplitFileName: string; const FileSize: Int64; const FileCRC: Longword): boolean;
+var FD: TEngineFileDes;
+ Error, Count: integer;
+ s: string;
+begin
+ Result := False;
+ if Pos('.', TargetFile) > 1 then TargetFile := ChangeFileExt(TargetFile, '.crc')
+ else TargetFile := TargetFile + '.crc';
+ FD := Engine.OpenFile(TargetFile, omWrite, Error);
+ if Error <> 0 then begin
+ Application.MessageBox(Format(LANGAnErrorOccuredWhileOpeningFileSS, [ANSIToUTF8(TargetFile), ANSIToUTF8(GetErrorString(Error))]), [mbOK], mbError, mbNone, mbOK);
+ Exit;
+ end;
+ s := Format('filename=%s'#13#10'size=%d'#13#10'crc32=%s'#13#10, [SplitFileName, FileSize, ANSIUpperCase(IntToHex(FileCRC, 8))]);
+ Count := Engine.WriteFile(FD, @s[1], Length(s), Error);
+ if (Error <> 0) or (Count <> Length(s)) then begin
+ Application.MessageBox(Format(LANGAnErrorOccuredWhileWritingFileSS, [ANSIToUTF8(TargetFile), ANSIToUTF8(GetErrorString(Error))]), [mbOK], mbError, mbNone, mbOK);
+ Exit;
+ end;
+ Engine.CloseFile(FD);
+ Result := True;
+end;
+
+(********************************************************************************************************************************)
+procedure SplitFilesWorker(SenderThread: TWorkerThread);
+// ParamInt64 = SplitSize
+// ParamString1 = FileName
+// ParamString2 = NewPath
+// ParamBool1 = DeleteTarget
+
+const SplitBlockSize = 65536*4;
+var FD: TEngineFileDes;
+ Error: integer;
+ FileCRC: LongWord;
+ Buffer: Pointer;
+ PrivateCancel: boolean;
+ FilePath: string;
+ SizeDone, TDF, FileSize, CurrSize: Int64;
+
+
+ function WriteSplitPart(TargetFile: string; PartSize: Int64; var Written: Int64): boolean;
+ var FDW: TEngineFileDes;
+ Count, wCount, bl: integer;
+ begin
+ Result := False;
+ Written := 0;
+ with SenderThread do begin
+ FDW := Engine.OpenFile(TargetFile, omWrite, Error);
+ DebugMsg(['-- Opening file ', ExtractFileName(TargetFile), ', PartSize = ', PartSize]);
+ if Error <> 0 then Exit;
+ if ParamInt64 > 0 then begin
+ UpdateCaption2(Format(LANGToS, [ANSIToUTF8(TargetFile)]));
+ SetProgress1Params(PartSize);
+ UpdateProgress1(0, '0 %');
+ end else UpdateCaption1(Format(LANGToS, [ANSIToUTF8(TargetFile)]));
+ CommitGUIUpdate;
+ repeat
+ DebugMsg(['Seek to ', Engine.FileSeek(FD, SizeDone + Written, Error), ', Written = ', Written]);
+ if Written + SplitBlockSize > PartSize then bl := PartSize - Written
+ else bl := SplitBlockSize;
+ Count := Engine.ReadFile(FD, Buffer, bl, Error);
+ if (Error <> 0) or (Count <> bl) then begin
+ Engine.CloseFile(FDW);
+ DebugMsg(['Read Error: ', GetErrorString(Error), ', Count = ', Count, ', bl = ', bl]);
+ if (Count <> bl) and (Error = 0) then Error := EIO;
+ Exit;
+ end;
+ wCount := Engine.WriteFile(FDW, Buffer, Count, Error);
+ Inc(Written, wCount);
+ FileCRC := CRC32(FileCRC, Buffer, wCount);
+ if (Error <> 0) or (Count <> wCount) then begin
+ Engine.CloseFile(FDW);
+ DebugMsg(['Write Error: ', GetErrorString(Error), ', Count = ', Count, ', wCount = ', wCount]);
+ if (wCount <> Count) and (Error = 0) then Error := ENOSPC;
+ Exit;
+ end;
+ UpdateProgress1(FProgress1Pos + wCount, Format('%d %%', [Trunc((FProgress1Pos + wCount) / FProgress1Max * 100)]));
+ if ParamInt64 > 0 then UpdateProgress2(FProgress2Pos + wCount, Format('%d %%', [Trunc((FProgress2Pos + wCount) / FProgress2Max * 100)]));
+ CommitGUIUpdate;
+ until (Written = PartSize) or Cancelled or PrivateCancel;
+ Engine.CloseFile(FDW);
+ end;
+ DebugMsg(['-- Closing file ', ExtractFileName(TargetFile), ', PartSize = ', PartSize, ', Written = ', Written]);
+ Result := True;
+ end;
+
+ // Returns True if it should break the process
+ function NewDiskQuestion: boolean;
+ begin
+ Result := False;
+ with SenderThread do begin
+ TDF := Engine.GetFileSystemFree(FilePath);
+ // Calculate part size
+ if ParamInt64 = 0 then begin
+ if FileSize - SizeDone > TDF then CurrSize := TDF
+ else CurrSize := FileSize - SizeDone;
+ end else
+ if SizeDone + ParamInt64 > FileSize then CurrSize := FileSize - SizeDone
+ else CurrSize := ParamInt64;
+ if (TDF < 512) {or (CurrSize < 512)} or (TDF < CurrSize) then begin
+ DebugMsg(['-- New disk question']);
+ Engine.ExplicitChDir('/');
+ PrivateCancel := ShowNewDirDialog(LANGSplitCaption, LANGPleaseInsertNextDiskOrGiveDifferentLocation,
+ ANSIToUTF8(FilePath)) <> integer(mbOK);
+ if not PrivateCancel then FilePath := UTF8ToANSI(FNewDirEdit);
+ Result := PrivateCancel;
+ end;
+ end;
+ end;
+
+
+var i: integer;
+ OriginalFName, st, FileName: string;
+ ws: Int64;
+ Stat: PDataItemSL;
+ b: boolean;
+ List: TList;
+begin
+ with SenderThread do begin
+ Stat := Engine.GetFileInfoSL(ParamString1);
+ if not Assigned(Stat) then begin
+ FCancelMessage := Format(LANGCannotOpenFileS, [ANSIToUTF8(ParamString1)]);
+ FShowCancelMessage := True;
+ Exit;
+ end;
+ if (ParamInt64 > 0) and (Stat^.Size > ParamInt64 * 999) then begin
+ FCancelMessage := LANGCannotSplitTheFileToMoreThan999Parts;
+ FShowCancelMessage := True;
+ Exit;
+ end;
+ FileSize := Stat^.Size;
+ SizeDone := 0;
+ FileCRC := $FFFFFFFF;
+ List := TList.Create;
+
+ try
+ Buffer := Libc.malloc(SplitBlockSize);
+ Libc.memset(Buffer, 0, SplitBlockSize);
+ except
+ FCancelMessage := LANGAnErrorOccuredWhileInitializingMemoryBlock;
+ FShowCancelMessage := True;
+ Exit;
+ end;
+ FD := Engine.OpenFile(ParamString1, omRead, Error);
+ if Error <> 0 then begin
+ FCancelMessage := Format(LANGAnErrorOccuredWhileOpeningFileSS, [ANSIToUTF8(ParamString1), ANSIToUTF8(GetErrorString(Error))]);
+ Libc.free(Buffer);
+ Exit;
+ end;
+ FilePath := IncludeTrailingPathDelimiter(ProcessPattern(Engine, ParamString2, Engine.Path, '', True));
+ FileName := ExtractFileName(ParamString1);
+ OriginalFName := FileName;
+ if Pos('.', FileName) > 1 then FileName := ChangeFileExt(FileName, '.001')
+ else FileName := FileName + '.001';
+ PrivateCancel := False;
+
+ if ParamInt64 > 0 then begin
+ SetProgress2Params(FileSize);
+ UpdateProgress2(0, '0 %');
+ end else begin
+ SetProgress1Params(FileSize);
+ UpdateProgress1(0, '0 %');
+ end;
+ UpdateCaption1(Format(LANGFromS, [ANSIToUTF8(IncludeTrailingPathDelimiter(FilePath) + OriginalFName)]));
+ CommitGUIUpdate;
+
+ repeat
+ TDF := Engine.GetFileSystemFree(FilePath);
+ // Delete target files if necessary
+ if ParamBool1 and ((TDF < 512) or (TDF < FileSize) or (TDF < ParamInt64)) then try
+ if List.Count > 0 then
+ for i := List.Count - 1 downto 0 do
+ FreeDataItem(PDataItem(List[i]));
+ List.Clear;
+ Error := Engine.GetListing(List, ConfShowDotFiles, FilePath);
+ if (Error = 0) and (List.Count > 0) then begin
+ st := '';
+ if List.Count < 6 then begin
+ for i := 0 to List.Count - 1 do
+ st := st + ' ' + AnsiToUTF8(string(PDataItem(List[i])^.AName)) + #10;
+ b := ShowMessageBox(Format(LANGThereAreSomeFilesInTheTargetDirectorySDoYouWantToDeleteThem, [st]), [mbYes, mbNo], mbQuestion, mbNone, mbNo) = mbYes;
+ 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])^.AName));
+ if Error <> 0 then ShowMessageBox(Format(LANGTheTargetFileSCannotBeRemovedS, [ANSIToUTF8(IncludeTrailingPathDelimiter(FilePath) + string(PDataItem(List[i])^.AName)), ANSIToUTF8(GetErrorString(Error))]), [mbOK], mbError, mbNone, mbOK);
+ end;
+ end;
+ except end;
+ // Test for target file existence
+ if Engine.FileExists(IncludeTrailingPathDelimiter(FilePath) + FileName) then begin
+ b := ShowMessageBox(Format(LANGTheTargetFileSAlreadyExistsDoYouWantToOverwriteIt, [ANSIToUTF8(IncludeTrailingPathDelimiter(FilePath) + FileName)]), [mbYes, mbNo], mbQuestion, mbNone, mbNo) = mbYes;
+ if b then begin
+ Error := Engine.Remove(IncludeTrailingPathDelimiter(FilePath) + FileName);
+ if Error <> 0 then begin
+ FCancelMessage := Format(LANGTheTargetFileSCannotBeRemovedS, [ANSIToUTF8(IncludeTrailingPathDelimiter(FilePath) + FileName), ANSIToUTF8(GetErrorString(Error))]);
+ FShowCancelMessage := True;
+ PrivateCancel := True;
+ Break;
+ end;
+ end else begin
+ PrivateCancel := True;
+ Break;
+ end;
+ end;
+ // Free space check
+ if NewDiskQuestion then Break;
+ // Writing
+ ws := 0;
+ if (CurrSize >= 512) and (TDF >= CurrSize) then begin
+ b := WriteSplitPart(IncludeTrailingPathDelimiter(FilePath) + FileName, CurrSize, ws);
+ if (not b) and (ParamInt64 > 0) then begin
+ FCancelMessage := Format(LANGAnErrorOccuredWhileOperationS, [ANSIToUTF8(GetErrorString(Error))]);
+ FShowCancelMessage := True;
+ PrivateCancel := True;
+ Break;
+ end;
+ Inc(SizeDone, ws);
+ if ParamInt64 > 0 then UpdateProgress2(SizeDone, Format('%d %%', [Trunc(SizeDone / FileSize * 100)]))
+ else UpdateProgress1(SizeDone, Format('%d %%', [Trunc(SizeDone / FileSize * 100)]));
+ CommitGUIUpdate;
+ end;
+ // Free space check - New disk question after operation
+ if NewDiskQuestion then Break;
+ // Change filename
+ if ws > 0 then
+ try FileName := Copy(FileName, 1, LastDelimiter('.', FileName)) +
+ Format('%.3d', [StrToInt(Copy(FileName, LastDelimiter('.', FileName) + 1,
+ Length(FileName) - LastDelimiter('.', FileName))) + 1]);
+ except
+ FileName := '';
+ end;
+ until (SizeDone = FileSize) or Cancelled or PrivateCancel or (FileName = '');
+ if Cancelled and (not PrivateCancel) then begin
+ FCancelMessage := LANGUserCancelled;
+ FShowCancelMessage := True;
+ end;
+ if not (Cancelled or PrivateCancel) then begin
+ repeat
+ TDF := Engine.GetFileSystemFree(FilePath);
+ if (TDF < 512) and (not NewDiskQuestion) then Break;
+ until (TDF >= 512) or PrivateCancel or Cancelled;
+ if WriteCRCFile(Engine, IncludeTrailingPathDelimiter(FilePath) + FileName, OriginalFName, SizeDone, not FileCRC)
+ then ShowMessageBox(Format(LANGSplitOfSSucceeded, [ANSIToUTF8(OriginalFName)]), [mbOK], mbInfo, mbNone, mbOK)
+ else begin
+ FCancelMessage := Format(LANGSplitOfSFailed, [ANSIToUTF8(OriginalFName)]);
+ FShowCancelMessage := True;
+ end;
+ end;
+ Engine.CloseFile(FD);
+ end;
+ List.Free;
+ SenderThread.FDoneThread := True;
+end;
+
+(********************************************************************************************************************************)
+procedure ChmodFilesWorker(SenderThread: TWorkerThread);
+// ParamBool1 = Recursive
+// ParamInt1 = All/Dir/Files
+// ParamCardinal1 = Mode
+
+var SkipAll: boolean;
+
+ function HandleChmod(AFileRec: PDataItemSL): boolean;
+ var Res, Response: integer;
+ begin
+ Result := True;
+ with SenderThread do begin
+// DebugMsg(['Chmod Debug: IsDir: ', AFileRec^.IsDir, ', Stage1: ', AFileRec^.Stage1, ', IsLnk: ', AFileRec^.IsLnk, '; Result = ', AFileRec^.IsDir and AFileRec^.Stage1 and (not AFileRec^.IsLnk)]);
+ if AFileRec^.IsDir and ParamBool1 and AFileRec^.Stage1 and (not AFileRec^.IsLnk) then Exit;
+ if (not AFileRec^.IsDir) and ParamBool1 and (ParamInt1 = 1) then Exit; // Directories only
+ if AFileRec^.IsDir and ParamBool1 and (ParamInt1 = 2) then Exit; // Files only
+ Res := Engine.Chmod(String(AFileRec^.AName), ParamCardinal1);
+// DebugMsg(['Result : ', Res]);
+ if Res <> 0 then
+ if SkipAll then Result := True else
+ begin
+ Response := ShowDirDeleteDialog(1, LANGTheFileDirectory, ANSIToUTF8(String(AFileRec^.AName)), Format(LANGCouldNotBeChmoddedS,
+ [ANSIToUTF8(GetErrorString(Res))]), LANGDialogChangePermissions);
+ case Response of
+ 1 : Result := True;
+ 3 : begin
+ SkipAll := True;
+ Result := True;
+ end;
+ 2 : Result := HandleChmod(AFileRec);
+ else Result := False;
+ end;
+ end;
+ end;
+ end;
+
+var i: longint;
+ AList: TList;
+ CurrPath: string;
+ Fr: Single;
+begin
+ SkipAll := False;
+ with SenderThread do begin
+ AList := TList.Create;
+ AList.Clear;
+ CurrPath := IncludeTrailingPathDelimiter(Engine.Path);
+ if DataList.Count > 0 then
+ for i := 0 to DataList.Count - 1 do
+ with PDataItem(DataList[i])^ do
+ if (not UpDir) and Selected then
+ if IsDir and (not IsLnk) and ParamBool1
+ then Engine.FillDirFiles(CurrPath + String(AName), AList, 1)
+ else AList.Add(Engine.GetFileInfoSL(CurrPath + String(AName)));
+ if (AList.Count = 0) and Assigned(SelectedItem) and (not SelectedItem^.UpDir) then
+ with SelectedItem^ do
+ if IsDir and (not IsLnk) and ParamBool1
+ then Engine.FillDirFiles(CurrPath + String(AName), AList, 1)
+ else AList.Add(Engine.GetFileInfoSL(CurrPath + String(AName)));
+ Engine.ExplicitChDir('/');
+ SetProgress1Params(AList.Count);
+ UpdateProgress1(0, '0 %');
+ CommitGUIUpdate;
+
+// DebugWriteListSL(AList);
+
+ if AList.Count = 1 then Fr := 1 else Fr := 100 / (AList.Count - 1);
+ if AList.Count > 0 then
+ for i := 0 to AList.Count - 1 do begin
+ if Cancelled then begin
+ FCancelMessage := LANGUserCancelled;
+ FShowCancelMessage := True;
+ Break;
+ end;
+ // Process chmod
+ if not HandleChmod(AList[i]) then Break;
+ UpdateProgress1(i, Format('%d%%', [Round(Fr * i)]));
+ UpdateCaption1(ANSIToUTF8(PDataItemSL(AList[i])^.AName));
+ CommitGUIUpdate;
+ end;
+
+ // Free the objects
+ if AList.Count > 0 then
+ for i := AList.Count - 1 downto 0 do FreeDataItem(PDataItemSL(AList[i]));
+ AList.Clear;
+ AList.Free;
+ end;
+ SenderThread.FDoneThread := True;
+end;
+
+
+(********************************************************************************************************************************)
+procedure ChownFilesWorker(SenderThread: TWorkerThread);
+// ParamBool1 = Recursive
+// ParamCardinal1 = UID
+// ParamCardinal2 = GID
+var SkipAll: boolean;
+
+ function HandleChown(AFileRec: PDataItemSL): boolean;
+ var Res, Response: integer;
+ begin
+ Result := True;
+ with SenderThread do begin
+// DebugMsg(['Chown Debug: IsDir: ', AFileRec^.IsDir, ', Stage1: ', AFileRec^.Stage1, ', IsLnk: ', AFileRec^.IsLnk, '; Result = ', AFileRec^.IsDir and AFileRec^.Stage1 and (not AFileRec^.IsLnk)]);
+ if (AFileRec^.IsDir and ParamBool1 and AFileRec^.Stage1 and (not AFileRec^.IsLnk)) or
+ ((not AFileRec^.IsDir) and ParamBool1) then Exit;
+ Res := Engine.Chown(String(AFileRec^.AName), ParamCardinal1, ParamCardinal2);
+// DebugMsg(['Result : ', Res]);
+ if Res <> 0 then
+ if SkipAll then Result := True else
+ begin
+ Response := ShowDirDeleteDialog(1, LANGTheFileDirectory, ANSIToUTF8(String(AFileRec^.AName)), Format(LANGCouldNotBeChownedS,
+ [ANSIToUTF8(GetErrorString(Res))]), LANGDialogChangeOwner);
+ case Response of
+ 1 : Result := True;
+ 3 : begin
+ SkipAll := True;
+ Result := True;
+ end;
+ 2 : Result := HandleChown(AFileRec);
+ else Result := False;
+ end;
+ end;
+ end;
+ end;
+
+var i: longint;
+ AList: TList;
+ CurrPath: string;
+ Fr: Single;
+begin
+ SkipAll := False;
+ with SenderThread do begin
+ AList := TList.Create;
+ AList.Clear;
+ CurrPath := IncludeTrailingPathDelimiter(Engine.Path);
+ if DataList.Count > 0 then
+ for i := 0 to DataList.Count - 1 do
+ with PDataItem(DataList[i])^ do
+ if (not UpDir) and Selected then
+ if IsDir and (not IsLnk) and ParamBool1
+ then Engine.FillDirFiles(CurrPath + String(AName), AList, 1)
+ else AList.Add(Engine.GetFileInfoSL(CurrPath + String(AName)));
+ if (AList.Count = 0) and Assigned(SelectedItem) and (not SelectedItem^.UpDir) then
+ with SelectedItem^ do
+ if IsDir and (not IsLnk) and ParamBool1
+ then Engine.FillDirFiles(CurrPath + String(AName), AList, 1)
+ else AList.Add(Engine.GetFileInfoSL(CurrPath + String(AName)));
+ Engine.ExplicitChDir('/');
+ SetProgress1Params(AList.Count);
+ UpdateProgress1(0, '0 %');
+ CommitGUIUpdate;
+
+// DebugWriteListSL(AList);
+
+ if AList.Count = 1 then Fr := 1 else Fr := 100 / (AList.Count - 1);
+ if AList.Count > 0 then
+ for i := 0 to AList.Count - 1 do begin
+ if Cancelled then begin
+ FCancelMessage := LANGUserCancelled;
+ FShowCancelMessage := True;
+ Break;
+ end;
+ // Process chmod
+ if not HandleChown(AList[i]) then Break;
+ UpdateProgress1(i, Format('%d%%', [Round(Fr * i)]));
+ UpdateCaption1(ANSIToUTF8(PDataItemSL(AList[i])^.AName));
+ CommitGUIUpdate;
+ end;
+
+ // Free the objects
+ if AList.Count > 0 then
+ for i := AList.Count - 1 downto 0 do FreeDataItem(PDataItemSL(AList[i]));
+ AList.Clear;
+ AList.Free;
+ end;
+ SenderThread.FDoneThread := True;
+end;
+
+
+(********************************************************************************************************************************)
+procedure DummyThreadWorker(SenderThread: TWorkerThread);
+var i: integer;
+begin
+ DebugMsg(['(II) DummyThreadWorker: begin']);
+ with SenderThread do begin
+ SetProgress1Params(100);
+ SetProgress2Params(100);
+ UpdateProgress1(0, '0 %');
+ UpdateProgress2(100, '100 %');
+ CommitGUIUpdate;
+ for i := 1 to 100 do begin
+ Sleep(100);
+ DebugMsg([' (II) DummyThreadWorker: done ', i, ' / 100']);
+ UpdateProgress1(i, Format('%d%%', [i]));
+ UpdateCaption1(Format('Test %d test', [i]));
+ UpdateProgress2(101-i, Format('%d%%', [101-i]));
+ UpdateCaption2(Format('Test %d test', [101-i]));
+ CommitGUIUpdate;
+ if Cancelled then Break;
+ end;
+ end;
+ DebugMsg(['(II) DummyThreadWorker: finish']);
+ SenderThread.FDoneThread := True;
+end;
+
+(********************************************************************************************************************************)
+function CreateSymlink(const FileName, PossibleNewName: string; Engine: TPanelEngine) : boolean;
+var AFSymLink: TFSymlink;
+
+ function HandleCreateSymlink(const OldName, NewName: string): boolean;
+ var Res, Response: integer;
+ begin
+ Res := Engine.MakeSymLink(NewName, OldName);
+ Result := Res = 0;
+ if not Result then begin
+ try
+ FDirDelete := TFDirDelete.Create(AFSymlink);
+ FDirDelete.Caption := LANGDialogMakeSymlink;
+ FDirDelete.AddButtons(2);
+ FDirDelete.Label1.Caption := LANGTheSymbolicLink;
+ FDirDelete.Label2.Caption := ANSIToUTF8(NewName);
+ FDirDelete.Label3.Caption := Format(LANGCouldNotBeCreatedS, [ANSIToUTF8(GetErrorString(Res))]);
+ FDirDelete.Label3.Visible := True;
+ Response := Integer(FDirDelete.Run);
+ finally
+ FDirDelete.Free;
+ end;
+ case Response of
+ 1 : Result := HandleCreateSymlink(OldName, NewName);
+ else Result := False;
+ end;
+ end;
+ end;
+
+
+begin
+ Result := False;
+ try
+ AFSymlink := TFSymlink.Create(Application.MainForm);
+ AFSymlink.FromEntry.Text := ANSIToUTF8(FileName);
+ AFSymlink.ToEntry.Text := ANSIToUTF8(PossibleNewName);
+ AFSymlink.ToEntry.SetFocus;
+ AFSymlink.ToEntry.SelectAll;
+ if AFSymlink.Run = mbOK then Result := HandleCreateSymlink(UTF8ToANSI(AFSymlink.FromEntry.Text),
+ ProcessPattern(Engine, UTF8ToANSI(AFSymlink.ToEntry.Text), Engine.Path, '', False));
+ finally
+ AFSymlink.Free;
+ end;
+end;
+
+(********************************************************************************************************************************)
+function EditSymlink(const FileName: string; Engine: TPanelEngine) : boolean;
+var Data: PDataItemSL;
+ AFSymLink: TFSymlink;
+
+ function HandleEditSymlink(const ExistingName, PointTo: string): boolean;
+ var Res, Response: integer;
+ begin
+ Res := Engine.Remove(ExistingName);
+ Result := Res = 0;
+ if not Result then begin
+ try
+ FDirDelete := TFDirDelete.Create(AFSymlink);
+ FDirDelete.Caption := LANGDialogEditSymlink;
+ FDirDelete.AddButtons(2);
+ FDirDelete.Label1.Caption := LANGTheSymbolicLink;
+ FDirDelete.Label2.Caption := ANSIToUTF8(ExistingName);
+ FDirDelete.Label3.Caption := Format(LANGCouldNotBeDeletedS, [ANSIToUTF8(GetErrorString(Res))]);
+ FDirDelete.Label3.Visible := True;
+ Response := Integer(FDirDelete.Run);
+ finally
+ FDirDelete.Free;
+ end;
+ case Response of
+ 1 : HandleEditSymlink(ExistingName, PointTo);
+ end;
+ Exit;
+ end;
+ Res := Engine.MakeSymLink(ExistingName, PointTo);
+ Result := Res = 0;
+ if not Result then begin
+ try
+ FDirDelete := TFDirDelete.Create(AFSymlink);
+ FDirDelete.Caption := LANGDialogMakeSymlink;
+ FDirDelete.AddButtons(2);
+ FDirDelete.Label1.Caption := LANGTheSymbolicLink;
+ FDirDelete.Label2.Caption := ANSIToUTF8(ExistingName);
+ FDirDelete.Label3.Caption := Format(LANGCouldNotBeCreatedS, [ANSIToUTF8(GetErrorString(Res))]);
+ FDirDelete.Label3.Visible := True;
+ Response := Integer(FDirDelete.Run);
+ finally
+ FDirDelete.Free;
+ end;
+ case Response of
+ 1 : Result := HandleEditSymlink(ExistingName, PointTo);
+ else Result := False;
+ end;
+ end;
+ end;
+
+begin
+ Result := False;
+ Data := Engine.GetFileInfoSL(FileName);
+ try
+ AFSymlink := TFSymlink.Create(Application);
+ AFSymlink.Caption := LANGFEditSymlink_Caption;
+ AFSymlink.FromEntry.Text := ANSIToUTF8(FileName);
+ AFSymlink.Label1.Caption := LANGFEditSymlink_SymbolicLinkFilename;
+ AFSymlink.Label1.UseUnderline := True;
+ AFSymlink.Label2.Caption := LANGFEditSymlink_SymbolicLinkPointsTo;
+ AFSymlink.Label2.UseUnderline := True;
+ AFSymlink.FromEntry.Enabled := False;
+ AFSymlink.ToEntry.Text := ANSIToUTF8(Data^.LnkPointTo);
+ AFSymlink.ToEntry.SelectAll;
+ if AFSymlink.Run = mbOK then Result := HandleEditSymlink(UTF8ToANSI(AFSymlink.FromEntry.Text), UTF8ToANSI(AFSymlink.ToEntry.Text));
+ finally
+ AFSymlink.Free;
+ end;
+end;
+
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+procedure ProcessProgressThread(SenderThread: TWorkerThread; ProgressForm: TFProgress);
+var AFDirDelete: TFDirDelete;
+ AFOverwrite: TFOverwrite;
+ AFNewDir: TFNewDir;
+begin
+ DebugMsg([' ** ProcessProgressThread --begin']);
+ try
+ while not SenderThread.FDoneThread do begin
+// Write('.');
+ Sleep(ConstInternalProgressTimer);
+// DebugMsg([' ** ProcessProgressThread: updating UI (FProgress1Pos = ', SenderThread.FProgress1Pos, ', FProgress2Pos = ', SenderThread.FProgress2Pos]);
+
+
+// DebugMsg(['ProcessProgressThread - before mutex']);
+ SenderThread.GUIMutex.Acquire;
+// WriteLn('ProcessProgressThread - ted mam lock ja! -- enter');
+
+ try
+ if SenderThread.FGUIChanged then begin
+ if SenderThread.FGUIProgress1Max > 1
+ then ProgressForm.ProgressBar.Fraction := SenderThread.FGUIProgress1Pos / SenderThread.FGUIProgress1Max
+ else ProgressForm.ProgressBar.Fraction := 0;
+// ProgressForm.ProgressBar.Value := SenderThread.FGUIProgress1Pos;
+ ProgressForm.ProgressBar.Text := SenderThread.FGUIProgress1Text;
+ ProgressForm.Label2.Caption := SenderThread.FGUILabel1Text;
+ if ProgressForm.FTwoBars then begin
+ if SenderThread.FGUIProgress2Max > 1
+ then ProgressForm.ProgressBar2.Fraction := SenderThread.FGUIProgress2Pos / SenderThread.FGUIProgress2Max
+ else ProgressForm.ProgressBar2.Fraction := 0;
+// ProgressForm.ProgressBar2.Value := SenderThread.FGUIProgress2Pos;
+ ProgressForm.ProgressBar2.Text := SenderThread.FGUIProgress2Text;
+ ProgressForm.Label3.Caption := SenderThread.FGUILabel2Text;
+ end;
+ ProgressForm.ProgressBar.Max := SenderThread.FGUIProgress1Max;
+ ProgressForm.ProgressBar2.Max := SenderThread.FGUIProgress2Max;
+ SenderThread.FGUIChanged := False;
+ end;
+ except
+ on E: Exception do DebugMsg(['*** Exception raised in UCore.ProcessProgressThread::updating progress bars block (', E.ClassName, '): ', E.Message]);
+ end;
+
+
+// Sleep(1000);
+// WriteLn('ProcessProgressThread - ted mam lock ja! -- leave');
+ SenderThread.GUIMutex.Release;
+
+
+// DebugMsg(['Before refresh']);
+ Application.ProcessMessages;
+// DebugMsg(['After refresh']);
+
+ try
+ if SenderThread.FDialogShowDirDelete then begin
+ AFDirDelete := nil;
+ try
+ AFDirDelete := TFDirDelete.Create(SenderThread.ProgressForm as TComponent);
+ AFDirDelete.Caption := SenderThread.FDirDeleteCaption;
+ AFDirDelete.AddButtons(SenderThread.FDirDeleteButtonsType);
+ AFDirDelete.Label1.Caption := SenderThread.FDirDeleteLabel1Text;
+ AFDirDelete.Label2.Caption := SenderThread.FDirDeleteLabel2Text;
+ AFDirDelete.Label3.Caption := SenderThread.FDirDeleteLabel3Text;
+ AFDirDelete.Label2.Visible := SenderThread.FDirDeleteLabel2Visible;
+ AFDirDelete.Label3.Visible := SenderThread.FDirDeleteLabel3Visible;
+ SenderThread.FDialogResultDirDelete := Integer(AFDirDelete.Run);
+ if (SenderThread.FDirDeleteButtonsType = 3) and (SenderThread.FDialogResultDirDelete = 2) and (not SenderThread.ParamBool3)
+ then case Application.MessageBox(LANGIgnoreError, [mbYes, mbNo{, mbCancel}], mbWarning, mbYes, mbNo) of
+ mbNo: SenderThread.FDialogResultDirDelete := 1;
+ mbCancel: SenderThread.FDialogResultDirDelete := 0;
+ end;
+ finally
+ AFDirDelete.Free;
+ end;
+ SenderThread.FDialogShowDirDelete := False;
+ SenderThread.FSigDialogDirDelete := True;
+ end;
+
+ if SenderThread.FDialogShowOverwrite then begin
+ AFOverwrite := nil;
+ try
+ AFOverwrite := TFOverwrite.Create(SenderThread.ProgressForm as TComponent);
+ AFOverwrite.AddButtons(SenderThread.FOverwriteButtonsType);
+ AFOverwrite.FromLabel.Caption := SenderThread.FOverwriteFromLabel;
+ AFOverwrite.FromInfoLabel.Caption := SenderThread.FOverwriteFromInfoLabel;
+ AFOverwrite.ToLabel.Caption := SenderThread.FOverwriteToLabel;
+ AFOverwrite.ToInfoLabel.Caption := SenderThread.FOverwriteToInfoLabel;
+ AFOverwrite.RenameStr := SenderThread.FOverwriteRenameStr;
+ AFOverwrite.SourceFile := SenderThread.FOverwriteSourceFile;
+ AFOverwrite.DestFile := SenderThread.FOverwriteDestFile;
+ SenderThread.FDialogResultOverwrite := Integer(AFOverwrite.Run);
+ SenderThread.FOverwriteRenameStr := UTF8ToANSI(AFOverwrite.RenameStr);
+ finally
+ AFOverwrite.Free;
+ end;
+ SenderThread.FDialogShowOverwrite := False;
+ SenderThread.FSigDialogOverwrite := True;
+ end;
+
+ if SenderThread.FDialogShowNewDir then begin
+ AFNewDir := nil;
+ try
+ AFNewDir := TFNewDir.Create(SenderThread.ProgressForm as TComponent);
+ AFNewDir.Caption := SenderThread.FNewDirCaption;
+ AFNewDir.Label1.Caption := SenderThread.FNewDirLabel;
+ AFNewDir.Entry.Text := SenderThread.FNewDirEdit;
+ AFNewDir.Entry.SelectAll;
+ SenderThread.FDialogResultNewDir := Integer(AFNewDir.Run);
+ SenderThread.FNewDirEdit := AFNewDir.Entry.Text;
+ finally
+ AFNewDir.Free;
+ end;
+ SenderThread.FDialogShowNewDir := False;
+ SenderThread.FSigDialogNewDir := True;
+ end;
+
+ if SenderThread.FDialogShowMsgBox then begin
+ SenderThread.FDialogResultMsgBox := Application.MessageBox(SenderThread.FMsgBoxText, SenderThread.FMsgBoxButtons,
+ SenderThread.FMsgBoxStyle, SenderThread.FMsgBoxDefault,
+ SenderThread.FMsgBoxEscape);
+ SenderThread.FDialogShowMsgBox := False;
+ SenderThread.FSigDialogMsgBox := True;
+ end;
+ finally end;
+ end;
+ if SenderThread.FShowCancelMessage then
+ if SenderThread.FCancelMessage = LANGUserCancelled
+ then Application.MessageBox(SenderThread.FCancelMessage, [mbOK], mbWarning, mbNone, mbOK)
+ else Application.MessageBox(SenderThread.FCancelMessage, [mbOK], mbError, mbNone, mbOK);
+ except
+ on E: Exception do DebugMsg(['*** Exception raised in UCore.ProcessProgressThread (', E.ClassName, '): ', E.Message]);
+ end;
+ DebugMsg([' ** ProcessProgressThread --end']);
+end;
+
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+procedure TWorkerThread.Execute;
+begin
+ if Assigned(WorkerProcedure) then WorkerProcedure(Self);
+end;
+
+constructor TWorkerThread.Create;
+begin
+ inherited Create(True);
+ FreeOnTerminate := False;
+ GUIMutex := TCriticalSection.Create;
+ FCancelled := False;
+ ProgressForm := nil;
+ Engine := nil;
+ DataList := nil;
+ ParamPointer1 := nil;
+ WorkerProcedure := nil;
+ SelectedItem := nil;
+ FDoneThread := False;
+ FShowCancelMessage := False;
+ FDialogShowDirDelete := False;
+ FDialogShowOverwrite := False;
+ FSigDialogDirDelete := False;
+ FSigDialogOverwrite := False;
+ FDialogShowNewDir := False;
+ FSigDialogNewDir := False;
+ FDialogShowMsgBox := False;
+ FSigDialogMsgBox := False;
+ ExtractFromVFSMode := False;
+ ErrorHappened := False;
+ ParamBool1 := False;
+ ParamBool2 := False;
+ ParamBool3 := False;
+ ParamBool4 := False;
+ ParamBool5 := False;
+ FGUIChanged := False;
+end;
+
+destructor TWorkerThread.Destroy;
+begin
+ GUIMutex.Free;
+ inherited Destroy;
+end;
+
+procedure TWorkerThread.CancelIt;
+begin
+ FCancelled := True;
+end;
+
+function TWorkerThread.Cancelled: boolean;
+begin
+ Result := FCancelled or ProgressForm.Cancelled;
+end;
+
+procedure TWorkerThread.UpdateProgress1(const Progress: Int64; const ProgressText: string);
+begin
+// DebugMsg([' ** TWorkerThread.UpdateProgress1(Progress = ', Progress, ', ProgressText = ', ProgressText]);
+ FProgress1Pos := Progress;
+ FProgress1Text := ProgressText;
+end;
+
+procedure TWorkerThread.UpdateProgress2(const Progress: Int64; const ProgressText: string);
+begin
+// DebugMsg([' ** TWorkerThread.UpdateProgress2(Progress = ', Progress, ', ProgressText = ', ProgressText]);
+ FProgress2Pos := Progress;
+ FProgress2Text := ProgressText;
+end;
+
+procedure TWorkerThread.SetProgress1Params(const ProgressMax: Int64);
+begin
+ FProgress1Max := ProgressMax;
+end;
+
+procedure TWorkerThread.SetProgress2Params(const ProgressMax: Int64);
+begin
+ FProgress2Max := ProgressMax;
+end;
+
+procedure TWorkerThread.UpdateCaption1(const CaptionText: string);
+begin
+ FLabel1Text := CaptionText;
+end;
+
+procedure TWorkerThread.UpdateCaption2(const CaptionText: string);
+begin
+ FLabel2Text := CaptionText;
+end;
+
+procedure TWorkerThread.CommitGUIUpdate;
+begin
+ GUIMutex.Acquire;
+// WriteLn('TWorkerThread.CommitGUIUpdate, ted mam lock ja! -- enter');
+ FGUIProgress1Pos := FProgress1Pos;
+ FGUIProgress2Pos := FProgress2Pos;
+ FGUIProgress1Max := FProgress1Max;
+ FGUIProgress2Max := FProgress2Max;
+ FGUIProgress1Text := FProgress1Text;
+ FGUIProgress2Text := FProgress2Text;
+ FGUILabel1Text := FLabel1Text;
+ FGUILabel2Text := FLabel2Text;
+ FGUIChanged := True;
+// Sleep(1000);
+// WriteLn('TWorkerThread.CommitGUIUpdate, ted mam lock ja! -- leave');
+ GUIMutex.Release;
+end;
+
+function TWorkerThread.ShowDirDeleteDialog(ButtonsType: integer; const Label1Text: string; const Label2Text: string = ''; const Label3Text: string = ''; const DirDeleteCaption: string = ''): integer;
+begin
+ FDialogResultDirDelete := integer(mbCancel);
+ FDirDeleteLabel1Text := Label1Text;
+ FDirDeleteLabel2Text := Label2Text;
+ FDirDeleteLabel3Text := Label3Text;
+ FDirDeleteLabel2Visible := Label2Text <> '';
+ FDirDeleteLabel3Visible := Label3Text <> '';
+ FDirDeleteButtonsType := ButtonsType;
+ if DirDeleteCaption = '' then FDirDeleteCaption := LANGRemoveDirectory
+ else FDirDeleteCaption := DirDeleteCaption;
+ FDialogShowDirDelete := True;
+ repeat
+ Sleep(ConstInternalProgressTimer);
+ until FSigDialogDirDelete;
+ FSigDialogDirDelete := False;
+ Result := FDialogResultDirDelete;
+end;
+
+function TWorkerThread.ShowOverwriteDialog(ButtonsType: integer; const FromLabel, FromInfoLabel, ToLabel, ToInfoLabel, RenameStr, SourceFile, DestFile: string): integer;
+begin
+ FDialogResultOverwrite := integer(mbCancel);
+ FOverwriteButtonsType := ButtonsType;
+ FOverwriteFromLabel := FromLabel;
+ FOverwriteFromInfoLabel := FromInfoLabel;
+ FOverwriteToLabel := ToLabel;
+ FOverwriteToInfoLabel := ToInfoLabel;
+ FOverwriteRenameStr := RenameStr;
+ FOverwriteSourceFile := SourceFile;
+ FOverwriteDestFile := DestFile;
+ FDialogShowOverwrite := True;
+ repeat
+ Sleep(ConstInternalProgressTimer);
+ until FSigDialogOverwrite;
+ FSigDialogOverwrite := False;
+ Result := FDialogResultOverwrite;
+end;
+
+function TWorkerThread.ShowNewDirDialog(Caption, LabelCaption, Edit: string): integer;
+begin
+ FNewDirCaption := Caption;
+ FNewDirLabel := LabelCaption;
+ FNewDirEdit := Edit;
+ FDialogShowNewDir := True;
+ repeat
+ Sleep(ConstInternalProgressTimer);
+ until FSigDialogNewDir;
+ FSigDialogNewDir := False;
+ Result := FDialogResultNewDir;
+end;
+
+function TWorkerThread.ShowMessageBox(const Text: string; Buttons: TMessageButtons; Style: TMessageStyle; Default, Escape: TMessageButton): TMessageButton;
+begin
+ FMsgBoxText := Text;
+ FMsgBoxButtons := Buttons;
+ FMsgBoxStyle := Style;
+ FMsgBoxDefault := Default;
+ FMsgBoxEscape := Escape;
+ FDialogShowMsgBox := True;
+ repeat
+ Sleep(ConstInternalProgressTimer);
+ until FSigDialogMsgBox;
+ FSigDialogMsgBox := False;
+ Result := FDialogResultMsgBox;
+end;
+
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+procedure FillDefaultFstabMounterItems;
+var fd: PGlibc_IOFile;
+ mntent: PGlibc_mntent;
+ MounterItem: TMounterItem;
+begin
+ while MounterList.Count > 0 do begin
+ TMounterItem(MounterList[MounterList.Count - 1]).Free;
+ MounterList.Delete(MounterList.Count - 1);
+ end;
+ MounterList.Clear;
+
+ fd := glibc_setmntent(_PATH_MNTTAB, 'r');
+ if fd = nil then Exit;
+ // Get mount name
+ mntent := glibc_getmntent(fd);
+ while mntent <> nil do begin
+ if (mntent^.mnt_dir <> '/') and (mntent^.mnt_dir <> '/boot') and (Pos('/proc', mntent^.mnt_dir) <> 1) and
+ (Pos('/dev', mntent^.mnt_dir) <> 1) and (mntent^.mnt_dir <> 'swap') and (mntent^.mnt_dir <> '') then
+ begin
+ MounterItem := TMounterItem.Create;
+ MounterItem.DisplayText := '';
+ MounterItem.MountPath := mntent^.mnt_dir;
+ MounterItem.Device := mntent^.mnt_fsname;
+ if (Pos('ISO9660', UpperCase(mntent^.mnt_type)) > 0) or (Pos('CDROM', UpperCase(mntent^.mnt_dir)) > 0) or
+ (Pos('CDRW', UpperCase(mntent^.mnt_dir)) > 0) or (Pos('DVD', UpperCase(mntent^.mnt_dir)) > 0)
+ then MounterItem.DeviceType := 2 else
+ if (Pos('FLOPPY', UpperCase(mntent^.mnt_dir)) > 0) then MounterItem.DeviceType := 3 else
+ if (Pos('ZIP', UpperCase(mntent^.mnt_type)) > 0) or (Pos('USB', UpperCase(mntent^.mnt_dir)) > 0) or
+ (Pos('CAMERA', UpperCase(mntent^.mnt_dir)) > 0) then MounterItem.DeviceType := 1 else
+ if (Pos('NFS', UpperCase(mntent^.mnt_type)) > 0) or (Pos('SMB', UpperCase(mntent^.mnt_type)) > 0) or
+ (Pos('NETW', UpperCase(mntent^.mnt_dir)) > 0) then MounterItem.DeviceType := 4 else
+ MounterItem.DeviceType := 0;
+ MounterList.Add(MounterItem);
+ end;
+ mntent := glibc_getmntent(fd);
+ end;
+ glibc_endmntent(fd);
+end;
+
+function TMounterItem.Mounted: boolean;
+var fd: PGlibc_IOFile;
+ mntent: PGlibc_mntent;
+begin
+ Result := False;
+ fd := glibc_setmntent(_PATH_MOUNTED, 'r');
+ if fd = nil then Exit;
+ // Get mount name
+ mntent := glibc_getmntent(fd);
+ while mntent <> nil do begin
+// DebugMsg(['mntent^.mnt_dir = ', Int64(mntent^.mnt_dir)]);
+// DebugMsg(['mntent^.mnt_dir = ', mntent^.mnt_dir]);
+// DebugMsg(['sizeof(mntent^.mnt_dir) = ', sizeof(mntent^.mnt_dir)]);
+// DebugMsg(['sizeof(TGlibc_mntent) = ', sizeof(TGlibc_mntent)]);
+// DebugMsg(['string(mntent^.mnt_dir) = ', string(mntent^.mnt_dir)]);
+// DebugMsg(['MountPath = ', MountPath]);
+ if mntent^.mnt_dir = MountPath then begin
+ Result := True;
+ Break;
+ end;
+ mntent := glibc_getmntent(fd);
+ end;
+ glibc_endmntent(fd);
+end;
+
+function TMounterItem.IsInFSTab: boolean;
+var fd: PGlibc_IOFile;
+ mntent: PGlibc_mntent;
+begin
+ Result := False;
+ fd := glibc_setmntent(_PATH_MNTTAB, 'r');
+ if fd = nil then Exit;
+ // Get mount name
+ mntent := glibc_getmntent(fd);
+ while mntent <> nil do begin
+ if (mntent^.mnt_dir = MountPath) and (mntent^.mnt_fsname = Device) then begin
+ Result := True;
+ Break;
+ end;
+ mntent := glibc_getmntent(fd);
+ end;
+ glibc_endmntent(fd);
+end;
+
+function TMounterItem.Mount: boolean;
+var s: string;
+begin
+ if Length(MountCommand) = 0 then begin
+ if IsInFSTab then s := Format('mount "%s"', [MountPath])
+ else s := Format('mount "%s" "%s"', [Device, MountPath]);
+ end else begin
+ s := ReplaceStr(MountCommand, '%dev', Device);
+ s := ReplaceStr(s, '%dir', MountPath);
+ end;
+ Result := HandleSystemCommand(s, Format(LANGErrorMount, [ANSIToUTF8(MountPath)]));
+end;
+
+function TMounterItem.Umount: boolean;
+var s: string;
+begin
+ if Length(UmountCommand) = 0 then begin
+ if IsInFSTab then s := Format('umount "%s"', [MountPath])
+ else s := Format('umount "%s" "%s"', [Device, MountPath]);
+ end else begin
+ s := ReplaceStr(UmountCommand, '%dev', Device);
+ s := ReplaceStr(s, '%dir', MountPath);
+ end;
+ Result := HandleSystemCommand(s, Format(LANGErrorUmount, [ANSIToUTF8(MountPath)]));
+end;
+
+function TMounterItem.Eject: boolean;
+var s: string;
+begin
+ if Length(UmountCommand) = 0 then begin
+ if IsInFSTab then s := Format('eject "%s"', [MountPath])
+ else s := Format('eject "%s" "%s"', [Device, MountPath]);
+ end else begin
+ s := ReplaceStr(UmountCommand, '%dev', Device);
+ s := ReplaceStr(s, '%dir', MountPath);
+ end;
+ Result := HandleSystemCommand(s, Format(LANGErrorEject, [ANSIToUTF8(MountPath)]));
+end;
+
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+function HandleLogin(Parent: TComponent; Engine: TPanelEngine; UserName, Password: string): boolean;
+var b: boolean;
+begin
+ Result := False;
+ b := Engine.Login(UserName, Password) = cVFS_OK;
+ if not b then
+ repeat
+ try
+ FLogin := TFLogin.Create(Parent);
+ b := FLogin.Run = mbOK;
+ UserName := FLogin.UserEntry.Text;
+ Password := FLogin.PasswordEntry.Text;
+ finally
+ FLogin.Free;
+ end;
+ if not b then Exit;
+ if b then b := Engine.Login(UserName, Password) = cVFS_OK;
+ until b;
+ Result := True;
+end;
+
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+procedure TGetDirSizeThread.Execute;
+begin
+ Result := Engine.GetDirSize(Path);
+ Finished := True;
+end;
+
+constructor TGetDirSizeThread.Create;
+begin
+ inherited Create(True);
+ FreeOnTerminate := False;
+ FCancelled := False;
+ Finished := False;
+ Result := -1;
+end;
+
+procedure TGetDirSizeThread.CancelIt;
+begin
+ FCancelled := True;
+ Engine.BreakProcessing(1);
+end;
+
+procedure GetDirSize(AListView: TGTKListView; Engine: TPanelEngine; DataList: TList; AllItems: boolean);
+var t: __time_t;
+ b: boolean;
+ FRemoteWait: TFRemoteWait;
+
+ function DoGetDirSizeItem(Index: integer): boolean;
+ var Item: TGTKListItem;
+ Data: PDataItem;
+ APath, s: string;
+ ASize: Int64;
+// List: TList;
+ Thread: TGetDirSizeThread;
+ begin
+ Result := True;
+ try
+ Item := AListView.Items[Index];
+ if not Assigned(Item) then Exit;
+ Data := Item.Data;
+ if (not Assigned(Data)) or (not Data^.IsDir) then Exit;
+ APath := IncludeTrailingPathDelimiter(Engine.Path) + string(Data^.AName);
+
+{ List := TList.Create;
+ Engine.FillDirFiles(APath, List, 1);
+ DebugWriteListSL(List); }
+
+ Thread := TGetDirSizeThread.Create;
+ try
+ Thread.Path := APath;
+ Thread.Engine := Engine;
+ Thread.Resume;
+// Thread.Execute;
+ while not Thread.Finished do begin
+ Sleep(ConstInternalProgressTimer);
+ if not b and (__time(nil) >= t + 2) then begin
+ FRemoteWait := TFRemoteWait.Create(Application);
+// FRemoteWait.Label2.Visible := False;
+ FRemoteWait.ParentForm := FMain;
+ FRemoteWait.ShowModal;
+ b := True;
+ end;
+ Application.ProcessMessages;
+ if FMainEscPressed or (Assigned(FRemoteWait) and FRemoteWait.Cancelled) then Thread.CancelIt;
+ end;
+ ASize := Thread.Result;
+ finally
+ Thread.Free;
+ end;
+
+ if (ASize < 0) or FMainEscPressed or (Assigned(FRemoteWait) and FRemoteWait.Cancelled) then Exit;
+ Data^.Size := ASize;
+ s := ANSIToUTF8(FormatSize(ASize, 0));
+ Libc.free(Data^.ColumnData[3]);
+// Data^.ColumnData[3] := Libc.malloc(Length(s) + 1);
+// Libc.memset(Data^.ColumnData[3], 0, Length(s) + 1);
+ Data^.ColumnData[3] := strdup(PChar(s));
+ except end;
+ end;
+
+
+var i, j: integer;
+ Data: PDataItem;
+begin
+ t := __time(nil);
+ b := False;
+ FRemoteWait := nil;
+
+ if not AllItems then DoGetDirSizeItem(AListView.Selected.Index) else
+ if DataList.Count > 0 then
+ for i := 0 to DataList.Count - 1 do begin
+ j := AListView.ConvertFromSorted(i);
+ Data := DataList[j];
+ if Data^.IsDir and (not Data^.UpDir) then begin
+ if not DoGetDirSizeItem(j) then Break;
+ if FMainEscPressed then Break;
+ AListView.Items[j].RedrawRow;
+ end;
+ end;
+ if FRemoteWait <> nil then FRemoteWait.Free;
+ ChDir('/');
+end;
+
+
+
+(********************************************************************************************************************************)
+constructor TOpenDirThread.Create;
+begin
+ inherited Create(True);
+ FreeOnTerminate := False;
+ Finished := False;
+ CancelIt := False;
+ ChDirResult := 0;
+ ListingResult := 0;
+ VFSOpenResult := 0;
+ RunningTime := 0;
+ APlugin := nil;
+ xEngine := nil;
+ Password := '';
+end;
+
+procedure TOpenDirThread.Execute;
+var tt: TDateTime;
+begin
+ try
+// Writeln('execute.');
+ tt := Now;
+ try
+// WriteLn('x1');
+ if APlugin <> nil then begin
+ xEngine := TVFSEngine.Create(APlugin);
+ xEngine.ParentEngine := AEngine;
+ xEngine.ArchiveMode := True;
+ AEngine.LastHighlightItem := AHighlightItem;
+ xEngine.SavePath := AEngine.Path;
+ AEngine := xEngine;
+ if Length(Password) > 0 then (xEngine as TVFSEngine).Password := Password;
+ VFSOpenResult := (xEngine as TVFSEngine).VFSOpenEx(AFullPath);
+ end else VFSOpenResult := 0;
+// WriteLn('x2');
+
+ if (VFSOpenResult = 0) and (not CancelIt) then begin
+// WriteLn('x3');
+ ChDirResult := ChangeDir(AEngine, APath, ASelItem, AAutoFallBack);
+// WriteLn('x4');
+
+ if (ChDirResult = 0) and (not CancelIt) then begin
+// WriteLn('x5');
+ ListingResult := AEngine.GetListing(ADirList, ConfShowDotFiles);
+// WriteLn('x6');
+ end;
+// WriteLn('x7');
+ end;
+ except
+ on E: Exception do DebugMsg(['*** TOpenDirThread.Execute -Exception: ', E.Message]);
+ end;
+ RunningTime := MilliSecondsBetween(tt, Now);
+// WriteLn('x8');
+ finally
+ Finished := True;
+ end;
+{ except
+ on E: Exception do DebugMsg(['*** Exception raised in TOpenDirThread.Execute (', E.ClassName, '): ', E.Message]);
+ end; }
+end;
+
+{ destructor TOpenDirThread.Destroy;
+begin
+ if (APlugin <> nil) and (xEngine <> nil) then
+ try
+ xEngine.Free;
+ except
+ on E: Exception do DebugMsg(['*** TOpenDirThread.Destroy -Exception: ', E.Message]);
+ end;
+ inherited Destroy;
+end; }
+
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+function PurgeDirectory(APath: string): boolean;
+var Handle : PDirectoryStream;
+ DirEnt : PDirent64;
+ StatBuf : PGlibc_stat64;
+ Buf : PChar;
+begin
+ try
+ Result := True;
+ APath := IncludeTrailingPathDelimiter(APath);
+ Handle := Libc.opendir(PChar(APath));
+ if not Assigned(Handle) then begin
+ Result := False;
+ Exit;
+ end;
+ repeat
+ DirEnt := readdir64(Handle);
+ if Assigned(DirEnt) and Assigned(PChar(@DirEnt^.d_name[0])) then begin
+ Buf := Pchar(@DirEnt^.d_name[0]);
+ if (Buf <> '.') and (Buf <> '..') and (DirEnt^.d_name[0] <> #0) then begin
+ StatBuf := Libc.malloc(sizeof(TGlibc_stat64));
+ Libc.memset(StatBuf, 0, sizeof(TGlibc_stat64));
+ if glibc_lstat64(PChar(APath + string(Buf)), StatBuf) = 0 then
+ if __S_ISTYPE(StatBuf.st_mode, __S_IFDIR)
+ then PurgeDirectory(APath + string(Buf))
+ else begin
+// DebugMsg(['Removing ', APath + string(Buf)]);
+ Result := Result and (Libc.remove(PChar(APath + string(Buf))) = 0);
+ end;
+ Libc.free(StatBuf);
+ end;
+ end;
+ until DirEnt = nil;
+ closedir(Handle);
+// DebugMsg(['Removing ', ExcludeTrailingPathDelimiter(APath)]);
+ Result := Result and (Libc.remove(PChar(ExcludeTrailingPathDelimiter(APath))) = 0);
+ except
+ on E: Exception do DebugMsg(['*** Exception raised in UCore.PurgeDirectory(APath = ', APath, '): ', E.ClassName, ': ', E.Message]);
+ end;
+end;
+
+procedure CleanTempDirs;
+var i: integer;
+begin
+ try
+ if Assigned(UsedTempPaths) and (UsedTempPaths.Count > 0) then
+ for i := 0 to UsedTempPaths.Count - 1 do
+ DebugMsg(['(II) PurgeDirectory: Cleaning directory "', UsedTempPaths[i], '", Successfull = ', PurgeDirectory(UsedTempPaths[i])]);
+ UsedTempPaths.Clear;
+ except
+ on E: Exception do DebugMsg(['*** Exception raised in UCore.CleanTempDirs (', E.ClassName, '): ', E.Message]);
+ end;
+end;
+
+
+
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+(********************************************************************************************************************************)
+
+
+
+initialization
+ LeftPanelData := TList.Create;
+ RightPanelData := TList.Create;
+ LeftLocalEngine := TLocalTreeEngine.Create;
+ RightLocalEngine := TLocalTreeEngine.Create;
+ FMainEscPressed := False;
+ LeftPanelTabs := TStringList.Create;
+ RightPanelTabs := TStringList.Create;
+ LeftTabSortIDs := TList.Create;
+ RightTabSortIDs := TList.Create;
+ LeftTabSortTypes := TList.Create;
+ RightTabSortTypes := TList.Create;
+ MounterList := nil;
+ ConnectionMgrList := nil;
+ ConnectionMgrList := TList.Create;
+ UsedTempPaths := TStringList.Create;
+ SelectHistory := TStringList.Create;
+ SearchHistory := TStringList.Create;
+finalization
+ ClearListData(LeftPanelData);
+ ClearListData(RightPanelData);
+ LeftPanelTabs.Free;
+ RightPanelTabs.Free;
+ LeftTabSortIDs.Free;
+ RightTabSortIDs.Free;
+ LeftTabSortTypes.Free;
+ RightTabSortTypes.Free;
+ MounterList.Free;
+ LeftPanelData.Free;
+ RightPanelData.Free;
+ AssocList.Free;
+ ConnectionMgrList.Free;
+ CleanTempDirs;
+ UsedTempPaths.Free;
+ SelectHistory.Free;
+ SearchHistory.Free;
+end.