1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00

Parallel background uploads

Source commit: 1015897f6614be286e8d8fbb2e059fc831350fcf
This commit is contained in:
Martin Prikryl
2016-12-09 17:00:59 +00:00
parent 6f6f56168f
commit f8018d0f98
28 changed files with 1470 additions and 532 deletions

View File

@@ -12,15 +12,20 @@ __fastcall TFileOperationProgressType::TFileOperationProgressType()
{
FOnProgress = NULL;
FOnFinished = NULL;
FParent = NULL;
Init();
Clear();
}
//---------------------------------------------------------------------------
__fastcall TFileOperationProgressType::TFileOperationProgressType(
TFileOperationProgressEvent AOnProgress, TFileOperationFinished AOnFinished)
TFileOperationProgressEvent AOnProgress, TFileOperationFinished AOnFinished,
TFileOperationProgressType * Parent)
{
FOnProgress = AOnProgress;
FOnFinished = AOnFinished;
FParent = Parent;
FReset = false;
Init();
Clear();
}
//---------------------------------------------------------------------------
@@ -28,14 +33,33 @@ __fastcall TFileOperationProgressType::~TFileOperationProgressType()
{
DebugAssert(!InProgress || FReset);
DebugAssert(!Suspended || FReset);
SAFE_DESTROY(FSection);
SAFE_DESTROY(FUserSelectionsSection);
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::Init()
{
FSection = new TCriticalSection();
FUserSelectionsSection = new TCriticalSection();
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::Assign(const TFileOperationProgressType & Other)
{
TValueRestorer<TCriticalSection *> SectionRestorer(FSection);
TValueRestorer<TCriticalSection *> UserSelectionsSectionRestorer(FUserSelectionsSection);
TGuard Guard(FSection);
TGuard OtherGuard(Other.FSection);
*this = Other;
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::AssignButKeepSuspendState(const TFileOperationProgressType & Other)
{
TGuard Guard(FSection);
TValueRestorer<unsigned int> SuspendTimeRestorer(FSuspendTime);
TValueRestorer<bool> SuspendedRestorer(FSuspended);
*this = Other;
Assign(Other);
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::Clear()
@@ -46,10 +70,12 @@ void __fastcall TFileOperationProgressType::Clear()
FAsciiTransfer = false;
FCount = -1;
FFilesFinished = 0;
FFilesFinishedSuccessfully = 0;
FStartTime = Now();
FSuspended = false;
FSuspendTime = 0;
FInProgress = false;
FDone = false;
FFileInProgress = false;
FTotalTransferred = 0;
FTotalSkipped = 0;
@@ -73,8 +99,9 @@ void __fastcall TFileOperationProgressType::ClearTransfer()
{
if ((TransferSize > 0) && (TransferredSize < TransferSize))
{
TGuard Guard(FSection);
__int64 RemainingSize = (TransferSize - TransferredSize);
FTotalSkipped += RemainingSize;
AddSkipped(RemainingSize);
}
FLocalSize = 0;
FTransferSize = 0;
@@ -95,15 +122,20 @@ void __fastcall TFileOperationProgressType::Start(TFileOperation AOperation,
TOperationSide ASide, int ACount, bool ATemp,
const UnicodeString ADirectory, unsigned long ACPSLimit)
{
Clear();
FOperation = AOperation;
FSide = ASide;
FCount = ACount;
FInProgress = true;
FCancel = csContinue;
FDirectory = ADirectory;
FTemp = ATemp;
FCPSLimit = ACPSLimit;
{
TGuard Guard(FSection); // not really needed, just for consistency
Clear();
FOperation = AOperation;
FSide = ASide;
FCount = ACount;
FInProgress = true;
FCancel = csContinue;
FDirectory = ADirectory;
FTemp = ATemp;
FCPSLimit = ACPSLimit;
}
try
{
DoProgress();
@@ -131,27 +163,42 @@ void __fastcall TFileOperationProgressType::Stop()
DoProgress();
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::SetDone()
{
FDone = true;
DoProgress();
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::Suspend()
{
DebugAssert(!Suspended);
FSuspended = true;
FSuspendTime = GetTickCount();
{
TGuard Guard(FSection);
DebugAssert(!Suspended);
FSuspended = true;
FSuspendTime = GetTickCount();
}
DoProgress();
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::Resume()
{
DebugAssert(Suspended);
FSuspended = false;
// shift timestamps for CPS calculation in advance
// by the time the progress was suspended
unsigned long Stopped = (GetTickCount() - FSuspendTime);
size_t i = 0;
while (i < FTicks.size())
{
FTicks[i] += Stopped;
++i;
TGuard Guard(FSection);
DebugAssert(Suspended);
FSuspended = false;
// shift timestamps for CPS calculation in advance
// by the time the progress was suspended
unsigned long Stopped = (GetTickCount() - FSuspendTime);
size_t i = 0;
while (i < FTicks.size())
{
FTicks[i] += Stopped;
++i;
}
}
DoProgress();
@@ -159,8 +206,15 @@ void __fastcall TFileOperationProgressType::Resume()
//---------------------------------------------------------------------------
int __fastcall TFileOperationProgressType::OperationProgress()
{
DebugAssert(FCount > 0);
int Result = (FFilesFinished * 100)/FCount;
int Result;
if (FCount > 0)
{
Result = (FFilesFinished * 100)/FCount;
}
else
{
Result = 0;
}
return Result;
}
//---------------------------------------------------------------------------
@@ -180,8 +234,9 @@ int __fastcall TFileOperationProgressType::TransferProgress()
//---------------------------------------------------------------------------
int __fastcall TFileOperationProgressType::TotalTransferProgress()
{
TGuard Guard(FSection);
DebugAssert(TotalSizeSet);
int Result = TotalSize > 0 ? (int)(((TotalTransferred + TotalSkipped) * 100)/TotalSize) : 0;
int Result = FTotalSize > 0 ? (int)(((FTotalTransferred + FTotalSkipped) * 100)/FTotalSize) : 0;
return Result < 100 ? Result : 100;
}
//---------------------------------------------------------------------------
@@ -214,10 +269,14 @@ void __fastcall TFileOperationProgressType::Finish(UnicodeString FileName,
{
DebugAssert(InProgress);
// Cancel reader is guarded
FOnFinished(Operation, Side, Temp, FileName,
/* TODO : There wasn't 'Success' condition, was it by mistake or by purpose? */
Success && (Cancel == csContinue), OnceDoneOperation);
FFilesFinished++;
if (Success)
{
FFilesFinishedSuccessfully++;
}
DoProgress();
}
//---------------------------------------------------------------------------
@@ -276,6 +335,7 @@ void __fastcall TFileOperationProgressType::SetSpeedCounters()
}
}
//---------------------------------------------------------------------------
// Used in WebDAV protocol
void __fastcall TFileOperationProgressType::ThrottleToCPSLimit(
unsigned long Size)
{
@@ -291,6 +351,7 @@ unsigned long __fastcall TFileOperationProgressType::AdjustToCPSLimit(
{
SetSpeedCounters();
// CPSLimit reader is guarded, we cannot block whole method as it can last long.
if (CPSLimit > 0)
{
// we must not return 0, hence, if we reach zero,
@@ -327,6 +388,7 @@ unsigned long __fastcall TFileOperationProgressType::AdjustToCPSLimit(
return Size;
}
//---------------------------------------------------------------------------
// Use in SCP protocol only
unsigned long __fastcall TFileOperationProgressType::LocalBlockSize()
{
unsigned long Result = TRANSFER_BUF_SIZE;
@@ -340,8 +402,15 @@ unsigned long __fastcall TFileOperationProgressType::LocalBlockSize()
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::SetTotalSize(__int64 ASize)
{
TGuard Guard(FSection); // not really needed, just for consistency
FTotalSize = ASize;
FTotalSizeSet = true;
// parent has its own totals
if (FParent != NULL)
{
DebugAssert(FParent->TotalSizeSet);
}
DoProgress();
}
//---------------------------------------------------------------------------
@@ -356,42 +425,163 @@ void __fastcall TFileOperationProgressType::SetTransferringFile(bool ATransferri
FTransferringFile = ATransferringFile;
}
//---------------------------------------------------------------------------
bool __fastcall TFileOperationProgressType::PassCancelToParent(TCancelStatus ACancel)
{
bool Result;
if (ACancel < csCancel)
{
// do not propagate csCancelFile,
// though it's not supported for queue atm, so we do not expect it here
DebugFail();
Result = false;
}
else if (ACancel == csCancel)
{
Result = true;
}
else
{
// csCancelTransfer and csRemoteAbort are used with SCP only, which does not use parallel transfers
DebugFail();
Result = false;
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::SetCancel(TCancelStatus ACancel)
{
TGuard Guard(FSection);
FCancel = ACancel;
if ((FParent != NULL) && PassCancelToParent(ACancel))
{
FParent->SetCancel(ACancel);
}
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::SetCancelAtLeast(TCancelStatus ACancel)
{
TGuard Guard(FSection);
if (FCancel < ACancel)
{
FCancel = ACancel;
}
if ((FParent != NULL) && PassCancelToParent(ACancel))
{
FParent->SetCancelAtLeast(ACancel);
}
}
//---------------------------------------------------------------------------
TCancelStatus __fastcall TFileOperationProgressType::GetCancel()
{
TCancelStatus Result = FCancel;
if (FParent != NULL)
{
TGuard Guard(FSection);
TCancelStatus ParentCancel = FParent->Cancel;
if (ParentCancel > Result)
{
Result = ParentCancel;
}
}
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall TFileOperationProgressType::ClearCancelFile()
{
TGuard Guard(FSection);
// Not propagated to parent, as this is local flag, see also PassCancelToParent
bool Result = (Cancel == csCancelFile);
if (Result)
{
SetCancel(csContinue);
FCancel = csContinue;
}
return Result;
}
//---------------------------------------------------------------------------
unsigned long __fastcall TFileOperationProgressType::GetCPSLimit()
{
unsigned int Result;
if (FParent != NULL)
{
Result = FParent->CPSLimit;
}
else
{
TGuard Guard(FSection);
Result = FCPSLimit;
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::SetCPSLimit(unsigned long ACPSLimit)
{
FCPSLimit = ACPSLimit;
if (FParent != NULL)
{
FParent->SetCPSLimit(ACPSLimit);
}
else
{
TGuard Guard(FSection);
FCPSLimit = ACPSLimit;
}
}
//---------------------------------------------------------------------------
TBatchOverwrite __fastcall TFileOperationProgressType::GetBatchOverwrite()
{
TBatchOverwrite Result;
if (FParent != NULL)
{
Result = FParent->BatchOverwrite;
}
else
{
TGuard Guard(FSection); // not really needed
Result = FBatchOverwrite;
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::SetBatchOverwrite(TBatchOverwrite ABatchOverwrite)
{
FBatchOverwrite = ABatchOverwrite;
if (FParent != NULL)
{
FParent->SetBatchOverwrite(ABatchOverwrite);
}
else
{
TGuard Guard(FSection); // not really needed
FBatchOverwrite = ABatchOverwrite;;
}
}
//---------------------------------------------------------------------------
bool __fastcall TFileOperationProgressType::GetSkipToAll()
{
bool Result;
if (FParent != NULL)
{
Result = FParent->SkipToAll;
}
else
{
TGuard Guard(FSection); // not really needed
Result = FSkipToAll;
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::SetSkipToAll()
{
FSkipToAll = true;
if (FParent != NULL)
{
FParent->SetSkipToAll();
}
else
{
TGuard Guard(FSection); // not really needed
FSkipToAll = true;
}
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::ChangeTransferSize(__int64 ASize)
@@ -400,27 +590,79 @@ void __fastcall TFileOperationProgressType::ChangeTransferSize(__int64 ASize)
// on total transfer size
if (TotalSizeSet)
{
FTotalSize += (ASize - TransferSize);
AddTotalSize(ASize - TransferSize);
}
FTransferSize = ASize;
DoProgress();
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::RollbackTransfer()
void __fastcall TFileOperationProgressType::RollbackTransferFromTotals(__int64 ATransferredSize, __int64 ASkippedSize)
{
FTransferredSize -= SkippedSize;
DebugAssert(TransferredSize <= TotalTransferred);
FTotalTransferred -= TransferredSize;
DebugAssert(SkippedSize <= TotalSkipped);
TGuard Guard(FSection);
DebugAssert(ATransferredSize <= FTotalTransferred);
DebugAssert(ASkippedSize <= FTotalSkipped);
FTotalTransferred -= ATransferredSize;
FTicks.clear();
FTotalTransferredThen.clear();
FTotalSkipped -= SkippedSize;
FTotalSkipped -= ASkippedSize;
if (FParent != NULL)
{
FParent->RollbackTransferFromTotals(ATransferredSize, ASkippedSize);
}
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::RollbackTransfer()
{
FTransferredSize -= FSkippedSize;
RollbackTransferFromTotals(FTransferredSize, FSkippedSize);
FSkippedSize = 0;
FTransferredSize = 0;
FTransferSize = 0;
FLocallyUsed = 0;
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::AddTransferredToTotals(__int64 ASize)
{
TGuard Guard(FSection);
FTotalTransferred += ASize;
unsigned long Ticks = GetTickCount();
if (FTicks.empty() ||
(FTicks.back() > Ticks) || // ticks wrap after 49.7 days
((Ticks - FTicks.back()) >= MSecsPerSec))
{
FTicks.push_back(Ticks);
FTotalTransferredThen.push_back(FTotalTransferred);
}
if (FTicks.size() > 10)
{
FTicks.erase(FTicks.begin());
FTotalTransferredThen.erase(FTotalTransferredThen.begin());
}
if (FParent != NULL)
{
FParent->AddTransferredToTotals(ASize);
}
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::AddTotalSize(__int64 ASize)
{
if (ASize != 0)
{
TGuard Guard(FSection);
FTotalSize += ASize;
if (FParent != NULL)
{
FParent->AddTotalSize(ASize);
}
}
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::AddTransferred(__int64 ASize,
bool AddToTotals)
{
@@ -432,34 +674,32 @@ void __fastcall TFileOperationProgressType::AddTransferred(__int64 ASize,
if (TotalSizeSet)
{
// we should probably guard this with AddToTotals
FTotalSize += (TransferredSize - TransferSize);
AddTotalSize(TransferredSize - TransferSize);
}
FTransferSize = TransferredSize;
}
if (AddToTotals)
{
FTotalTransferred += ASize;
unsigned long Ticks = GetTickCount();
if (FTicks.empty() ||
(FTicks.back() > Ticks) || // ticks wrap after 49.7 days
((Ticks - FTicks.back()) >= MSecsPerSec))
{
FTicks.push_back(Ticks);
FTotalTransferredThen.push_back(TotalTransferred);
}
if (FTicks.size() > 10)
{
FTicks.erase(FTicks.begin());
FTotalTransferredThen.erase(FTotalTransferredThen.begin());
}
AddTransferredToTotals(ASize);
}
DoProgress();
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::AddSkipped(__int64 ASize)
{
TGuard Guard(FSection);
FTotalSkipped += ASize;
if (FParent != NULL)
{
FParent->AddSkipped(ASize);
}
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::AddResumed(__int64 ASize)
{
FTotalSkipped += ASize;
AddSkipped(ASize);
FSkippedSize += ASize;
AddTransferred(ASize, false);
AddLocallyUsed(ASize);
@@ -467,10 +707,11 @@ void __fastcall TFileOperationProgressType::AddResumed(__int64 ASize)
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::AddSkippedFileSize(__int64 ASize)
{
FTotalSkipped += ASize;
AddSkipped(ASize);
DoProgress();
}
//---------------------------------------------------------------------------
// Use in SCP protocol only
unsigned long __fastcall TFileOperationProgressType::TransferBlockSize()
{
unsigned long Result = TRANSFER_BUF_SIZE;
@@ -505,6 +746,13 @@ TDateTime __fastcall TFileOperationProgressType::TimeElapsed()
}
//---------------------------------------------------------------------------
unsigned int __fastcall TFileOperationProgressType::CPS()
{
TGuard Guard(FSection);
return GetCPS();
}
//---------------------------------------------------------------------------
// Has to be called from a guarded method
unsigned int __fastcall TFileOperationProgressType::GetCPS()
{
unsigned int Result;
if (FTicks.empty())
@@ -531,7 +779,7 @@ unsigned int __fastcall TFileOperationProgressType::CPS()
}
else
{
__int64 Transferred = (TotalTransferred - FTotalTransferredThen.front());
__int64 Transferred = (FTotalTransferred - FTotalTransferredThen.front());
Result = (unsigned int)(Transferred * MSecsPerSec / TimeSpan);
}
}
@@ -553,12 +801,13 @@ TDateTime __fastcall TFileOperationProgressType::TimeExpected()
//---------------------------------------------------------------------------
TDateTime __fastcall TFileOperationProgressType::TotalTimeLeft()
{
DebugAssert(TotalSizeSet);
unsigned int CurCps = CPS();
TGuard Guard(FSection);
DebugAssert(FTotalSizeSet);
unsigned int CurCps = GetCPS();
// sanity check
if ((CurCps > 0) && (TotalSize > TotalSkipped + TotalTransferred))
if ((CurCps > 0) && (FTotalSize > FTotalSkipped + FTotalTransferred))
{
return TDateTime((double)((double)(TotalSize - TotalSkipped - TotalTransferred) / CurCps) /
return TDateTime((double)((double)(FTotalSize - FTotalSkipped - FTotalTransferred) / CurCps) /
SecsPerDay);
}
else
@@ -566,3 +815,39 @@ TDateTime __fastcall TFileOperationProgressType::TotalTimeLeft()
return 0;
}
}
//---------------------------------------------------------------------------
__int64 __fastcall TFileOperationProgressType::GetTotalTransferred()
{
TGuard Guard(FSection);
return FTotalTransferred;
}
//---------------------------------------------------------------------------
__int64 __fastcall TFileOperationProgressType::GetTotalSize()
{
TGuard Guard(FSection);
return FTotalSize;
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::LockUserSelections()
{
if (FParent != NULL)
{
FParent->LockUserSelections();
}
else
{
FUserSelectionsSection->Enter();
}
}
//---------------------------------------------------------------------------
void __fastcall TFileOperationProgressType::UnlockUserSelections()
{
if (FParent != NULL)
{
FParent->UnlockUserSelections();
}
else
{
FUserSelectionsSection->Leave();
}
}

View File

@@ -37,6 +37,7 @@ private:
__int64 FTransferredSize;
__int64 FSkippedSize;
bool FInProgress;
bool FDone;
bool FFileInProgress;
TCancelStatus FCancel;
int FCount;
@@ -49,12 +50,14 @@ private:
unsigned long FCPSLimit;
bool FTotalSizeSet;
bool FSuspended;
TFileOperationProgressType * FParent;
// when it was last time suspended (to calculate suspend time in Resume())
unsigned int FSuspendTime;
// when current file was started being transferred
TDateTime FFileStartTime;
int FFilesFinished;
int FFilesFinishedSuccessfully;
TFileOperationProgressEvent FOnProgress;
TFileOperationFinished FOnFinished;
bool FReset;
@@ -63,11 +66,26 @@ private:
bool FCounterSet;
std::vector<unsigned long> FTicks;
std::vector<__int64> FTotalTransferredThen;
TCriticalSection * FSection;
TCriticalSection * FUserSelectionsSection;
__int64 __fastcall GetTotalTransferred();
__int64 __fastcall GetTotalSize();
unsigned long __fastcall GetCPSLimit();
TBatchOverwrite __fastcall GetBatchOverwrite();
bool __fastcall GetSkipToAll();
protected:
void __fastcall ClearTransfer();
inline void __fastcall DoProgress();
int __fastcall OperationProgress();
void __fastcall AddTransferredToTotals(__int64 ASize);
void __fastcall AddSkipped(__int64 ASize);
void __fastcall AddTotalSize(__int64 ASize);
void __fastcall RollbackTransferFromTotals(__int64 ATransferredSize, __int64 ASkippedSize);
unsigned int __fastcall GetCPS();
void __fastcall Init();
static bool __fastcall PassCancelToParent(TCancelStatus ACancel);
public:
// common data
@@ -89,18 +107,19 @@ public:
__property __int64 TransferredSize = { read = FTransferredSize };
__property __int64 SkippedSize = { read = FSkippedSize };
__property bool InProgress = { read = FInProgress };
__property bool Done = { read = FDone };
__property bool FileInProgress = { read = FFileInProgress };
__property TCancelStatus Cancel = { read = FCancel };
__property TCancelStatus Cancel = { read = GetCancel };
// when operation started
__property TDateTime StartTime = { read = FStartTime };
// bytes transferred
__property __int64 TotalTransferred = { read = FTotalTransferred };
__property __int64 TotalSkipped = { read = FTotalSkipped };
__property __int64 TotalSize = { read = FTotalSize };
__property __int64 TotalTransferred = { read = GetTotalTransferred };
__property __int64 TotalSize = { read = GetTotalSize };
__property int FilesFinishedSuccessfully = { read = FFilesFinishedSuccessfully };
__property TBatchOverwrite BatchOverwrite = { read = FBatchOverwrite };
__property bool SkipToAll = { read = FSkipToAll };
__property unsigned long CPSLimit = { read = FCPSLimit };
__property TBatchOverwrite BatchOverwrite = { read = GetBatchOverwrite };
__property bool SkipToAll = { read = GetSkipToAll };
__property unsigned long CPSLimit = { read = GetCPSLimit };
__property bool TotalSizeSet = { read = FTotalSizeSet };
@@ -108,8 +127,10 @@ public:
__fastcall TFileOperationProgressType();
__fastcall TFileOperationProgressType(
TFileOperationProgressEvent AOnProgress, TFileOperationFinished AOnFinished);
TFileOperationProgressEvent AOnProgress, TFileOperationFinished AOnFinished,
TFileOperationProgressType * Parent = NULL);
__fastcall ~TFileOperationProgressType();
void __fastcall Assign(const TFileOperationProgressType & Other);
void __fastcall AssignButKeepSuspendState(const TFileOperationProgressType & Other);
void __fastcall AddLocallyUsed(__int64 ASize);
void __fastcall AddTransferred(__int64 ASize, bool AddToTotals = true);
@@ -142,7 +163,10 @@ public:
TOperationSide ASide, int ACount, bool ATemp, const UnicodeString ADirectory,
unsigned long ACPSLimit);
void __fastcall Stop();
void __fastcall SetDone();
void __fastcall Suspend();
void __fastcall LockUserSelections();
void __fastcall UnlockUserSelections();
// whole operation
TDateTime __fastcall TimeElapsed();
// only current file
@@ -153,6 +177,7 @@ public:
int __fastcall TotalTransferProgress();
void __fastcall SetSpeedCounters();
void __fastcall SetTransferringFile(bool ATransferringFile);
TCancelStatus __fastcall GetCancel();
void __fastcall SetCancel(TCancelStatus ACancel);
void __fastcall SetCancelAtLeast(TCancelStatus ACancel);
bool __fastcall ClearCancelFile();

View File

@@ -1659,22 +1659,25 @@ void __fastcall TFTPFileSystem::Sink(const UnicodeString FileName,
}
FILE_OPERATION_LOOP_END(FMTLOAD(CREATE_DIR_ERROR, (DestFullName)));
TSinkFileParams SinkFileParams;
SinkFileParams.TargetDir = IncludeTrailingBackslash(DestFullName);
SinkFileParams.CopyParam = CopyParam;
SinkFileParams.Params = Params;
SinkFileParams.OperationProgress = OperationProgress;
SinkFileParams.Skipped = false;
SinkFileParams.Flags = Flags & ~(tfFirstLevel | tfAutoResume);
FTerminal->ProcessDirectory(FileName, SinkFile, &SinkFileParams);
// Do not delete directory if some of its files were skip.
// Throw "skip file" for the directory to avoid attempt to deletion
// of any parent directory
if (FLAGSET(Params, cpDelete) && SinkFileParams.Skipped)
if (FLAGCLEAR(Params, cpNoRecurse))
{
THROW_SKIP_FILE_NULL;
TSinkFileParams SinkFileParams;
SinkFileParams.TargetDir = IncludeTrailingBackslash(DestFullName);
SinkFileParams.CopyParam = CopyParam;
SinkFileParams.Params = Params;
SinkFileParams.OperationProgress = OperationProgress;
SinkFileParams.Skipped = false;
SinkFileParams.Flags = Flags & ~(tfFirstLevel | tfAutoResume);
FTerminal->ProcessDirectory(FileName, SinkFile, &SinkFileParams);
// Do not delete directory if some of its files were skip.
// Throw "skip file" for the directory to avoid attempt to deletion
// of any parent directory
if (FLAGSET(Params, cpDelete) && SinkFileParams.Skipped)
{
THROW_SKIP_FILE_NULL;
}
}
}
else
@@ -1762,6 +1765,7 @@ void __fastcall TFTPFileSystem::Sink(const UnicodeString FileName,
if (FLAGSET(Params, cpDelete))
{
DebugAssert(FLAGCLEAR(Params, cpNoRecurse));
// If file is directory, do not delete it recursively, because it should be
// empty already. If not, it should not be deleted (some files were
// skipped or some new files were copied to it, while we were downloading)
@@ -2067,60 +2071,62 @@ void __fastcall TFTPFileSystem::DirectorySource(const UnicodeString DirectoryNam
OperationProgress->SetFile(DirectoryName);
int FindAttrs = faReadOnly | faHidden | faSysFile | faDirectory | faArchive;
TSearchRecChecked SearchRec;
bool FindOK;
FILE_OPERATION_LOOP_BEGIN
{
FindOK =
(FindFirstChecked(DirectoryName + L"*.*", FindAttrs, SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
bool CreateDir = true;
try
if (FLAGCLEAR(Params, cpNoRecurse))
{
while (FindOK && !OperationProgress->Cancel)
int FindAttrs = faReadOnly | faHidden | faSysFile | faDirectory | faArchive;
TSearchRecChecked SearchRec;
bool FindOK;
FILE_OPERATION_LOOP_BEGIN
{
UnicodeString FileName = DirectoryName + SearchRec.Name;
try
{
if ((SearchRec.Name != L".") && (SearchRec.Name != L".."))
{
SourceRobust(FileName, DestFullName, CopyParam, Params, OperationProgress,
Flags & ~(tfFirstLevel | tfAutoResume));
// if any file got uploaded (i.e. there were any file in the
// directory and at least one was not skipped),
// do not try to create the directory,
// as it should be already created by FZAPI during upload
CreateDir = false;
}
}
catch (EScpSkipFile &E)
{
// If ESkipFile occurs, just log it and continue with next file
TSuspendFileOperationProgress Suspend(OperationProgress);
// here a message to user was displayed, which was not appropriate
// when user refused to overwrite the file in subdirectory.
// hopefully it won't be missing in other situations.
if (!FTerminal->HandleException(&E))
{
throw;
}
}
FILE_OPERATION_LOOP_BEGIN
{
FindOK = (FindNextChecked(SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
FindOK =
(FindFirstChecked(DirectoryName + L"*.*", FindAttrs, SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
try
{
while (FindOK && !OperationProgress->Cancel)
{
UnicodeString FileName = DirectoryName + SearchRec.Name;
try
{
if ((SearchRec.Name != L".") && (SearchRec.Name != L".."))
{
SourceRobust(FileName, DestFullName, CopyParam, Params, OperationProgress,
Flags & ~(tfFirstLevel | tfAutoResume));
// if any file got uploaded (i.e. there were any file in the
// directory and at least one was not skipped),
// do not try to create the directory,
// as it should be already created by FZAPI during upload
CreateDir = false;
}
}
catch (EScpSkipFile &E)
{
// If ESkipFile occurs, just log it and continue with next file
TSuspendFileOperationProgress Suspend(OperationProgress);
// here a message to user was displayed, which was not appropriate
// when user refused to overwrite the file in subdirectory.
// hopefully it won't be missing in other situations.
if (!FTerminal->HandleException(&E))
{
throw;
}
}
FILE_OPERATION_LOOP_BEGIN
{
FindOK = (FindNextChecked(SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
}
}
__finally
{
FindClose(SearchRec);
}
}
__finally
{
FindClose(SearchRec);
}
if (CreateDir)
@@ -2164,6 +2170,7 @@ void __fastcall TFTPFileSystem::DirectorySource(const UnicodeString DirectoryNam
/* TODO : Show error message on failure. */
if (!OperationProgress->Cancel)
{
DebugAssert(FLAGCLEAR(Params, cpNoRecurse));
if (FLAGSET(Params, cpDelete))
{
RemoveDir(ApiPath(DirectoryName));
@@ -2318,6 +2325,7 @@ bool __fastcall TFTPFileSystem::IsCapable(int Capability) const
case fcRemoveBOMUpload:
case fcMoveToQueue:
case fsSkipTransfer:
case fsParallelTransfers:
return true;
case fcPreservingTimestampUpload:

View File

@@ -12,6 +12,18 @@
//---------------------------------------------------------------------------
class TBackgroundTerminal;
//---------------------------------------------------------------------------
class TParallelUploadQueueItem : public TLocatedQueueItem
{
public:
__fastcall TParallelUploadQueueItem(const TUploadQueueItem * ParentItem, TParallelOperation * ParallelOperation);
protected:
virtual void __fastcall DoExecute(TTerminal * Terminal);
private:
TParallelOperation * FParallelOperation;
};
//---------------------------------------------------------------------------
class TUserAction
{
public:
@@ -589,10 +601,9 @@ void __fastcall TTerminalQueue::DeleteItem(TQueueItem * Item, bool CanKeep)
FForcedItems->Remove(Item);
// =0 do not keep
// <0 infinity
if ((FKeepDoneItemsFor != 0) && CanKeep)
if ((FKeepDoneItemsFor != 0) && CanKeep && Item->Complete())
{
DebugAssert(Item->Status == TQueueItem::qsDone);
Item->Complete();
FDoneItems->Add(Item);
}
else
@@ -1126,6 +1137,29 @@ bool __fastcall TTerminalQueue::GetIsEmpty()
return (FItems->Count == 0);
}
//---------------------------------------------------------------------------
bool __fastcall TTerminalQueue::TryAddParallelOperation(TQueueItem * Item, bool Force)
{
TGuard Guard(FItemsSection);
bool Result =
(FFreeTerminals > 0) ||
(Force && (FItemsInProcess < FTransfersLimit));
if (Result)
{
AddItem(DebugNotNull(Item->CreateParallelOperation()));
}
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall TTerminalQueue::ContinueParallelOperation()
{
TGuard Guard(FItemsSection);
return (FItems->Count <= FItemsInProcess);
}
//---------------------------------------------------------------------------
// TBackgroundItem
//---------------------------------------------------------------------------
class TBackgroundTerminal : public TSecondaryTerminal
@@ -1548,6 +1582,8 @@ __fastcall TQueueItem::TQueueItem() :
FSection = new TCriticalSection();
FInfo = new TInfo();
FInfo->SingleFile = false;
FInfo->Primary = true;
FInfo->GroupToken = this;
}
//---------------------------------------------------------------------------
__fastcall TQueueItem::~TQueueItem()
@@ -1561,7 +1597,7 @@ __fastcall TQueueItem::~TQueueItem()
delete FInfo;
}
//---------------------------------------------------------------------------
void __fastcall TQueueItem::Complete()
bool __fastcall TQueueItem::Complete()
{
TGuard Guard(FSection);
@@ -1570,6 +1606,8 @@ void __fastcall TQueueItem::Complete()
SetEvent(FCompleteEvent);
FCompleteEvent = INVALID_HANDLE_VALUE;
}
return FInfo->Primary;
}
//---------------------------------------------------------------------------
bool __fastcall TQueueItem::IsUserActionStatus(TStatus Status)
@@ -1603,6 +1641,11 @@ void __fastcall TQueueItem::SetStatus(TStatus Status)
}
}
//---------------------------------------------------------------------------
void __fastcall TQueueItem::ProgressUpdated()
{
// noop
}
//---------------------------------------------------------------------------
void __fastcall TQueueItem::SetProgress(
TFileOperationProgressType & ProgressData)
{
@@ -1618,9 +1661,10 @@ void __fastcall TQueueItem::SetProgress(
}
DebugAssert(FProgressData != NULL);
*FProgressData = ProgressData;
FProgressData->Assign(ProgressData);
FProgressData->Reset();
}
ProgressUpdated();
FQueue->DoQueueItemUpdate(this);
}
//---------------------------------------------------------------------------
@@ -1631,7 +1675,7 @@ void __fastcall TQueueItem::GetData(TQueueItemProxy * Proxy)
DebugAssert(Proxy->FProgressData != NULL);
if (FProgressData != NULL)
{
*Proxy->FProgressData = *FProgressData;
Proxy->FProgressData->Assign(*FProgressData);
}
else
{
@@ -1683,6 +1727,23 @@ unsigned long __fastcall TQueueItem::GetCPSLimit()
return Result;
}
//---------------------------------------------------------------------------
TQueueItem * __fastcall TQueueItem::CreateParallelOperation()
{
return NULL;
}
//---------------------------------------------------------------------------
void __fastcall TQueueItem::InitParallelOperationInfo(TQueueItem * Item)
{
// deliberately not copying the ModifiedLocal and ModifiedRemote, not to trigger panel refresh, when sub-item completes
FInfo->Operation = Item->FInfo->Operation;
FInfo->Side = Item->FInfo->Side;
FInfo->Source = Item->FInfo->Source;
FInfo->Destination = Item->FInfo->Destination;
FInfo->SingleFile = DebugAlwaysFalse(Item->FInfo->SingleFile);
FInfo->Primary = false;
FInfo->GroupToken = Item->FInfo->GroupToken;
}
//---------------------------------------------------------------------------
// TQueueItemProxy
//---------------------------------------------------------------------------
__fastcall TQueueItemProxy::TQueueItemProxy(TTerminalQueue * Queue,
@@ -1875,7 +1936,22 @@ int __fastcall TTerminalQueueStatus::GetActiveAndPendingCount()
void __fastcall TTerminalQueueStatus::Add(TQueueItemProxy * ItemProxy)
{
ItemProxy->FQueueStatus = this;
FList->Add(ItemProxy);
int Index = FList->Count;
if (!ItemProxy->Info->Primary)
{
for (int I = 0; I < FList->Count; I++)
{
if (Items[I]->Info->GroupToken == ItemProxy->Info->GroupToken)
{
Index = I + 1;
}
}
DebugAssert(Index >= 0);
}
FList->Insert(Index, ItemProxy);
ResetStats();
}
//---------------------------------------------------------------------------
@@ -1920,7 +1996,13 @@ __fastcall TLocatedQueueItem::TLocatedQueueItem(TTerminal * Terminal) :
FCurrentDir = Terminal->CurrentDirectory;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TLocatedQueueItem::StartupDirectory()
__fastcall TLocatedQueueItem::TLocatedQueueItem(const TLocatedQueueItem & Source) :
TQueueItem()
{
FCurrentDir = Source.FCurrentDir;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TLocatedQueueItem::StartupDirectory() const
{
return FCurrentDir;
}
@@ -1979,7 +2061,7 @@ unsigned long __fastcall TTransferQueueItem::DefaultCPSLimit()
//---------------------------------------------------------------------------
__fastcall TUploadQueueItem::TUploadQueueItem(TTerminal * Terminal,
TStrings * FilesToCopy, const UnicodeString & TargetDir,
const TCopyParamType * CopyParam, int Params, bool SingleFile) :
const TCopyParamType * CopyParam, int Params, bool SingleFile, bool Parallel) :
TTransferQueueItem(Terminal, FilesToCopy, TargetDir, CopyParam, Params, osLocal, SingleFile)
{
if (FilesToCopy->Count > 1)
@@ -2017,6 +2099,8 @@ __fastcall TUploadQueueItem::TUploadQueueItem(TTerminal * Terminal,
FInfo->Destination =
UnixIncludeTrailingBackslash(TargetDir) + CopyParam->FileMask;
FInfo->ModifiedRemote = UnixIncludeTrailingBackslash(TargetDir);
FParallel = Parallel;
FLastParallelOperationAdded = GetTickCount();
}
//---------------------------------------------------------------------------
void __fastcall TUploadQueueItem::DoExecute(TTerminal * Terminal)
@@ -2024,7 +2108,114 @@ void __fastcall TUploadQueueItem::DoExecute(TTerminal * Terminal)
TTransferQueueItem::DoExecute(Terminal);
DebugAssert(Terminal != NULL);
Terminal->CopyToRemote(FFilesToCopy, FTargetDir, FCopyParam, FParams);
TParallelOperation ParallelOperation;
FParallelOperation = &ParallelOperation;
try
{
Terminal->CopyToRemote(FFilesToCopy, FTargetDir, FCopyParam, FParams, &ParallelOperation);
}
__finally
{
FParallelOperation = NULL;
}
}
//---------------------------------------------------------------------------
void __fastcall TUploadQueueItem::ProgressUpdated()
{
TTransferQueueItem::ProgressUpdated();
if (FParallel)
{
bool Add = false;
bool Force = false;
DWORD LastParallelOperationAddedPrev = 0;
{
TGuard Guard(FSection);
// Move this to TTransferQueueItem once implemented for downloads too
DebugAssert(FParallelOperation != NULL);
DebugAssert((FProgressData->Operation == foCopy) || (FProgressData->Operation == foCalculateSize));
if (FProgressData->Operation == foCopy)
{
Add = FParallelOperation->ShouldAddClient();
if (Add)
{
DWORD Now = GetTickCount();
Force =
(Now - FLastParallelOperationAdded >= 5*1000) &&
(TimeToSeconds(FProgressData->TotalTimeLeft()) >= 20);
LastParallelOperationAddedPrev = FLastParallelOperationAdded;
// update now already to prevent race condition, but we will have to rollback it back,
// if we actually do not add the parallel operation
FLastParallelOperationAdded = Now;
}
}
}
if (Add)
{
if (!FQueue->TryAddParallelOperation(this, Force))
{
TGuard Guard(FSection);
FLastParallelOperationAdded = LastParallelOperationAddedPrev;
}
}
}
}
//---------------------------------------------------------------------------
TQueueItem * __fastcall TUploadQueueItem::CreateParallelOperation()
{
DebugAssert(FParallelOperation != NULL);
FParallelOperation->AddClient();
return new TParallelUploadQueueItem(this, FParallelOperation);
}
//---------------------------------------------------------------------------
// TDownloadQueueItem
//---------------------------------------------------------------------------
__fastcall TParallelUploadQueueItem::TParallelUploadQueueItem(
const TUploadQueueItem * ParentItem, TParallelOperation * ParallelOperation) :
TLocatedQueueItem(*ParentItem),
FParallelOperation(ParallelOperation)
{
InitParallelOperationInfo(ParentItem);
}
//---------------------------------------------------------------------------
void __fastcall TParallelUploadQueueItem::DoExecute(TTerminal * Terminal)
{
TLocatedQueueItem::DoExecute(Terminal);
TFileOperationProgressType OperationProgress(Terminal->OnProgress, Terminal->OnFinished, FParallelOperation->MainOperationProgress);
TFileOperation Operation = (FLAGSET(FParallelOperation->Params, cpDelete) ? foMove : foCopy);
bool Temp = FLAGSET(FParallelOperation->Params, cpTemporary);
OperationProgress.Start(
// CPS limit inherited from parent OperationProgress.
// Count not known and won't be needed as we will always have TotalSize as we always transfer a single file at a time.
Operation, osLocal, -1, Temp, FParallelOperation->TargetDir, 0);
try
{
bool Continue = true;
do
{
int GotNext = Terminal->CopyToRemoteParallel(FParallelOperation, &OperationProgress);
if (GotNext < 0)
{
Continue = false;
}
else if (!FQueue->ContinueParallelOperation())
{
Continue = false;
}
}
while (Continue);
}
__finally
{
OperationProgress.Stop();
FParallelOperation->RemoveClient();
}
}
//---------------------------------------------------------------------------
// TDownloadQueueItem

View File

@@ -66,6 +66,8 @@ class TTerminalQueue : public TSignalThread
{
friend class TQueueItem;
friend class TQueueItemProxy;
friend class TUploadQueueItem; //???
friend class TParallelUploadQueueItem; //???
public:
__fastcall TTerminalQueue(TTerminal * Terminal, TConfiguration * Configuration);
@@ -146,6 +148,9 @@ protected:
void __fastcall SetKeepDoneItemsFor(int value);
void __fastcall SetEnabled(bool value);
bool __fastcall GetIsEmpty();
bool __fastcall TryAddParallelOperation(TQueueItem * Item, bool Force);
bool __fastcall ContinueParallelOperation();
};
//---------------------------------------------------------------------------
class TQueueItem
@@ -166,6 +171,8 @@ public:
UnicodeString ModifiedLocal;
UnicodeString ModifiedRemote;
bool SingleFile;
bool Primary;
void * GroupToken;
};
static bool __fastcall IsUserActionStatus(TStatus Status);
@@ -196,8 +203,11 @@ protected:
void __fastcall SetCPSLimit(unsigned long CPSLimit);
unsigned long __fastcall GetCPSLimit();
virtual unsigned long __fastcall DefaultCPSLimit();
virtual UnicodeString __fastcall StartupDirectory() = 0;
void __fastcall Complete();
virtual UnicodeString __fastcall StartupDirectory() const = 0;
virtual void __fastcall ProgressUpdated();
virtual TQueueItem * __fastcall CreateParallelOperation();
void __fastcall InitParallelOperationInfo(TQueueItem * Item);
bool __fastcall Complete();
};
//---------------------------------------------------------------------------
class TQueueItemProxy
@@ -284,9 +294,10 @@ class TLocatedQueueItem : public TQueueItem
{
protected:
__fastcall TLocatedQueueItem(TTerminal * Terminal);
__fastcall TLocatedQueueItem(const TLocatedQueueItem & Source);
virtual void __fastcall DoExecute(TTerminal * Terminal);
virtual UnicodeString __fastcall StartupDirectory();
virtual UnicodeString __fastcall StartupDirectory() const;
private:
UnicodeString FCurrentDir;
@@ -315,10 +326,16 @@ class TUploadQueueItem : public TTransferQueueItem
public:
__fastcall TUploadQueueItem(TTerminal * Terminal,
TStrings * FilesToCopy, const UnicodeString & TargetDir,
const TCopyParamType * CopyParam, int Params, bool SingleFile);
const TCopyParamType * CopyParam, int Params, bool SingleFile, bool Parallel);
protected:
bool FParallel;
DWORD FLastParallelOperationAdded;
TParallelOperation * FParallelOperation;
virtual void __fastcall DoExecute(TTerminal * Terminal);
virtual void __fastcall ProgressUpdated();
virtual TQueueItem * __fastcall CreateParallelOperation();
};
//---------------------------------------------------------------------------
class TDownloadQueueItem : public TTransferQueueItem

View File

@@ -63,6 +63,11 @@ UnicodeString __fastcall SimpleUnixExcludeTrailingBackslash(const UnicodeString
return UnixExcludeTrailingBackslash(Path, true);
}
//---------------------------------------------------------------------------
UnicodeString __fastcall UnixCombinePaths(const UnicodeString & Path1, const UnicodeString & Path2)
{
return UnixIncludeTrailingBackslash(Path1) + Path2;
}
//---------------------------------------------------------------------------
Boolean __fastcall UnixSamePath(const UnicodeString Path1, const UnicodeString Path2)
{
return (UnixIncludeTrailingBackslash(Path1) == UnixIncludeTrailingBackslash(Path2));

View File

@@ -443,6 +443,7 @@ bool __fastcall UnixIsAbsolutePath(const UnicodeString & Path);
UnicodeString __fastcall UnixIncludeTrailingBackslash(const UnicodeString Path);
UnicodeString __fastcall UnixExcludeTrailingBackslash(const UnicodeString Path, bool Simple = false);
UnicodeString __fastcall SimpleUnixExcludeTrailingBackslash(const UnicodeString Path);
UnicodeString __fastcall UnixCombinePaths(const UnicodeString & Path1, const UnicodeString & Path2);
UnicodeString __fastcall UnixExtractFileDir(const UnicodeString Path);
UnicodeString __fastcall UnixExtractFilePath(const UnicodeString Path);
UnicodeString __fastcall UnixExtractFileName(const UnicodeString Path);

View File

@@ -463,6 +463,7 @@ bool __fastcall TSCPFileSystem::IsCapable(int Capability) const
case fcPreservingTimestampDirs:
case fcResumeSupport:
case fsSkipTransfer:
case fsParallelTransfers: // does not implement cpNoRecurse
return false;
case fcChangePassword:

View File

@@ -1479,7 +1479,7 @@ void __fastcall TScript::PutProc(TScriptProcParams * Parameters)
CopyParamParams(CopyParam, Parameters);
CheckParams(Parameters);
FTerminal->CopyToRemote(FileList, TargetDirectory, &CopyParam, Params);
FTerminal->CopyToRemote(FileList, TargetDirectory, &CopyParam, Params, NULL);
}
__finally
{

View File

@@ -42,7 +42,7 @@ enum TFSCapability { fcUserGroupListing, fcModeChanging, fcGroupChanging,
fcModeChangingUpload, fcPreservingTimestampUpload, fcShellAnyCommand,
fcSecondaryShell, fcRemoveCtrlZUpload, fcRemoveBOMUpload, fcMoveToQueue,
fcLocking, fcPreservingTimestampDirs, fcResumeSupport,
fcChangePassword, fsSkipTransfer,
fcChangePassword, fsSkipTransfer, fsParallelTransfers,
fcCount };
//---------------------------------------------------------------------------
struct TFileSystemInfo

View File

@@ -2076,6 +2076,7 @@ bool __fastcall TSFTPFileSystem::IsCapable(int Capability) const
case fcPreservingTimestampDirs:
case fcResumeSupport:
case fsSkipTransfer:
case fsParallelTransfers:
return true;
case fcRename:
@@ -4357,57 +4358,65 @@ void __fastcall TSFTPFileSystem::SFTPConfirmOverwrite(
if (CanAppend &&
((Answer == qaRetry) || (Answer == qaSkip)))
{
// duplicated in TTerminal::ConfirmFileOverwrite
bool CanAlternateResume =
(FileParams->DestSize < FileParams->SourceSize) && !OperationProgress->AsciiTransfer;
TBatchOverwrite BatchOverwrite =
FTerminal->EffectiveBatchOverwrite(SourceFullFileName, CopyParam, Params, OperationProgress, true);
// when mode is forced by batch, never query user
if (BatchOverwrite == boAppend)
OperationProgress->LockUserSelections();
try
{
OverwriteMode = omAppend;
}
else if (CanAlternateResume &&
((BatchOverwrite == boResume) || (BatchOverwrite == boAlternateResume)))
{
OverwriteMode = omResume;
}
// no other option, but append
else if (!CanAlternateResume)
{
OverwriteMode = omAppend;
}
else
{
TQueryParams Params(0, HELP_APPEND_OR_RESUME);
// duplicated in TTerminal::ConfirmFileOverwrite
bool CanAlternateResume =
(FileParams->DestSize < FileParams->SourceSize) && !OperationProgress->AsciiTransfer;
TBatchOverwrite BatchOverwrite =
FTerminal->EffectiveBatchOverwrite(SourceFullFileName, CopyParam, Params, OperationProgress, true);
// when mode is forced by batch, never query user
if (BatchOverwrite == boAppend)
{
TSuspendFileOperationProgress Suspend(OperationProgress);
Answer = FTerminal->QueryUser(FORMAT(LoadStr(APPEND_OR_RESUME2), (SourceFullFileName)),
NULL, qaYes | qaNo | qaNoToAll | qaCancel, &Params);
OverwriteMode = omAppend;
}
switch (Answer)
else if (CanAlternateResume &&
((BatchOverwrite == boResume) || (BatchOverwrite == boAlternateResume)))
{
case qaYes:
OverwriteMode = omAppend;
break;
case qaNo:
OverwriteMode = omResume;
break;
case qaNoToAll:
OverwriteMode = omResume;
OperationProgress->SetBatchOverwrite(boAlternateResume);
break;
default: DebugFail(); //fallthru
case qaCancel:
OperationProgress->SetCancelAtLeast(csCancel);
Abort();
break;
OverwriteMode = omResume;
}
// no other option, but append
else if (!CanAlternateResume)
{
OverwriteMode = omAppend;
}
else
{
TQueryParams Params(0, HELP_APPEND_OR_RESUME);
{
TSuspendFileOperationProgress Suspend(OperationProgress);
Answer = FTerminal->QueryUser(FORMAT(LoadStr(APPEND_OR_RESUME2), (SourceFullFileName)),
NULL, qaYes | qaNo | qaNoToAll | qaCancel, &Params);
}
switch (Answer)
{
case qaYes:
OverwriteMode = omAppend;
break;
case qaNo:
OverwriteMode = omResume;
break;
case qaNoToAll:
OverwriteMode = omResume;
OperationProgress->SetBatchOverwrite(boAlternateResume);
break;
default: DebugFail(); //fallthru
case qaCancel:
OperationProgress->SetCancelAtLeast(csCancel);
Abort();
break;
}
}
}
__finally
{
OperationProgress->UnlockUserSelections();
}
}
else if (Answer == qaIgnore)
@@ -5341,82 +5350,85 @@ void __fastcall TSFTPFileSystem::SFTPDirectorySource(const UnicodeString Directo
Flags |= tfNewDirectory;
}
int FindAttrs = faReadOnly | faHidden | faSysFile | faDirectory | faArchive;
TSearchRecChecked SearchRec;
bool FindOK;
FILE_OPERATION_LOOP_BEGIN
if (FLAGCLEAR(Params, cpNoRecurse))
{
FindOK =
(FindFirstChecked(DirectoryName + L"*.*", FindAttrs, SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
int FindAttrs = faReadOnly | faHidden | faSysFile | faDirectory | faArchive;
TSearchRecChecked SearchRec;
bool FindOK;
try
{
while (FindOK && !OperationProgress->Cancel)
FILE_OPERATION_LOOP_BEGIN
{
UnicodeString FileName = DirectoryName + SearchRec.Name;
try
{
if ((SearchRec.Name != L".") && (SearchRec.Name != L".."))
{
SFTPSourceRobust(FileName, DestFullName, CopyParam, Params, OperationProgress,
Flags & ~tfFirstLevel);
}
}
catch (EScpSkipFile &E)
{
// If ESkipFile occurs, just log it and continue with next file
TSuspendFileOperationProgress Suspend(OperationProgress);
// here a message to user was displayed, which was not appropriate
// when user refused to overwrite the file in subdirectory.
// hopefully it won't be missing in other situations.
if (!FTerminal->HandleException(&E))
{
throw;
}
}
FindOK =
(FindFirstChecked(DirectoryName + L"*.*", FindAttrs, SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
FILE_OPERATION_LOOP_BEGIN
{
FindOK = (FindNextChecked(SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
};
}
__finally
{
FindClose(SearchRec);
}
/* TODO : Delete also read-only directories. */
/* TODO : Show error message on failure. */
if (!OperationProgress->Cancel)
{
if (CopyParam->PreserveTime && CopyParam->PreserveTimeDirs)
try
{
TRemoteProperties Properties;
Properties.Valid << vpModification;
while (FindOK && !OperationProgress->Cancel)
{
UnicodeString FileName = DirectoryName + SearchRec.Name;
try
{
if ((SearchRec.Name != L".") && (SearchRec.Name != L".."))
{
SFTPSourceRobust(FileName, DestFullName, CopyParam, Params, OperationProgress,
Flags & ~tfFirstLevel);
}
}
catch (EScpSkipFile &E)
{
// If ESkipFile occurs, just log it and continue with next file
TSuspendFileOperationProgress Suspend(OperationProgress);
// here a message to user was displayed, which was not appropriate
// when user refused to overwrite the file in subdirectory.
// hopefully it won't be missing in other situations.
if (!FTerminal->HandleException(&E))
{
throw;
}
}
FTerminal->OpenLocalFile(
ExcludeTrailingBackslash(DirectoryName), GENERIC_READ, NULL, NULL, NULL,
&Properties.Modification, &Properties.LastAccess, NULL);
FTerminal->ChangeFileProperties(DestFullName, NULL, &Properties);
FILE_OPERATION_LOOP_BEGIN
{
FindOK = (FindNextChecked(SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
};
}
__finally
{
FindClose(SearchRec);
}
if (FLAGSET(Params, cpDelete))
/* TODO : Delete also read-only directories. */
/* TODO : Show error message on failure. */
if (!OperationProgress->Cancel)
{
RemoveDir(ApiPath(DirectoryName));
}
else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
{
FILE_OPERATION_LOOP_BEGIN
if (CopyParam->PreserveTime && CopyParam->PreserveTimeDirs)
{
THROWOSIFFALSE(FileSetAttr(ApiPath(DirectoryName), Attrs & ~faArchive) == 0);
TRemoteProperties Properties;
Properties.Valid << vpModification;
FTerminal->OpenLocalFile(
ExcludeTrailingBackslash(DirectoryName), GENERIC_READ, NULL, NULL, NULL,
&Properties.Modification, &Properties.LastAccess, NULL);
FTerminal->ChangeFileProperties(DestFullName, NULL, &Properties);
}
if (FLAGSET(Params, cpDelete))
{
RemoveDir(ApiPath(DirectoryName));
}
else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
{
FILE_OPERATION_LOOP_BEGIN
{
THROWOSIFFALSE(FileSetAttr(ApiPath(DirectoryName), Attrs & ~faArchive) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(CANT_SET_ATTRS, (DirectoryName)));
}
FILE_OPERATION_LOOP_END(FMTLOAD(CANT_SET_ATTRS, (DirectoryName)));
}
}
}
@@ -5574,53 +5586,56 @@ void __fastcall TSFTPFileSystem::SFTPSink(const UnicodeString FileName,
}
FILE_OPERATION_LOOP_END(FMTLOAD(CREATE_DIR_ERROR, (DestFullName)));
TSinkFileParams SinkFileParams;
SinkFileParams.TargetDir = IncludeTrailingBackslash(DestFullName);
SinkFileParams.CopyParam = CopyParam;
SinkFileParams.Params = Params;
SinkFileParams.OperationProgress = OperationProgress;
SinkFileParams.Skipped = false;
SinkFileParams.Flags = Flags & ~tfFirstLevel;
FTerminal->ProcessDirectory(FileName, SFTPSinkFile, &SinkFileParams);
if (CopyParam->PreserveTime && CopyParam->PreserveTimeDirs)
if (FLAGCLEAR(Params, cpNoRecurse))
{
FTerminal->LogEvent(FORMAT(L"Preserving directory timestamp [%s]",
(StandardTimestamp(File->Modification))));
int SetFileTimeError = ERROR_SUCCESS;
// FILE_FLAG_BACKUP_SEMANTICS is needed to "open" directory
HANDLE LocalHandle = CreateFile(ApiPath(DestFullName).c_str(), GENERIC_WRITE,
FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (LocalHandle == INVALID_HANDLE_VALUE)
TSinkFileParams SinkFileParams;
SinkFileParams.TargetDir = IncludeTrailingBackslash(DestFullName);
SinkFileParams.CopyParam = CopyParam;
SinkFileParams.Params = Params;
SinkFileParams.OperationProgress = OperationProgress;
SinkFileParams.Skipped = false;
SinkFileParams.Flags = Flags & ~tfFirstLevel;
FTerminal->ProcessDirectory(FileName, SFTPSinkFile, &SinkFileParams);
if (CopyParam->PreserveTime && CopyParam->PreserveTimeDirs)
{
SetFileTimeError = GetLastError();
}
else
{
FILETIME AcTime = DateTimeToFileTime(File->LastAccess, FTerminal->SessionData->DSTMode);
FILETIME WrTime = DateTimeToFileTime(File->Modification, FTerminal->SessionData->DSTMode);
if (!SetFileTime(LocalHandle, NULL, &AcTime, &WrTime))
FTerminal->LogEvent(FORMAT(L"Preserving directory timestamp [%s]",
(StandardTimestamp(File->Modification))));
int SetFileTimeError = ERROR_SUCCESS;
// FILE_FLAG_BACKUP_SEMANTICS is needed to "open" directory
HANDLE LocalHandle = CreateFile(ApiPath(DestFullName).c_str(), GENERIC_WRITE,
FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (LocalHandle == INVALID_HANDLE_VALUE)
{
SetFileTimeError = GetLastError();
}
CloseHandle(LocalHandle);
else
{
FILETIME AcTime = DateTimeToFileTime(File->LastAccess, FTerminal->SessionData->DSTMode);
FILETIME WrTime = DateTimeToFileTime(File->Modification, FTerminal->SessionData->DSTMode);
if (!SetFileTime(LocalHandle, NULL, &AcTime, &WrTime))
{
SetFileTimeError = GetLastError();
}
CloseHandle(LocalHandle);
}
if (SetFileTimeError != ERROR_SUCCESS)
{
FTerminal->LogEvent(FORMAT(L"Preserving timestamp failed, ignoring: %s",
(SysErrorMessageForError(SetFileTimeError))));
}
}
if (SetFileTimeError != ERROR_SUCCESS)
// Do not delete directory if some of its files were skip.
// Throw "skip file" for the directory to avoid attempt to deletion
// of any parent directory
if ((Params & cpDelete) && SinkFileParams.Skipped)
{
FTerminal->LogEvent(FORMAT(L"Preserving timestamp failed, ignoring: %s",
(SysErrorMessageForError(SetFileTimeError))));
THROW_SKIP_FILE_NULL;
}
}
// Do not delete directory if some of its files were skip.
// Throw "skip file" for the directory to avoid attempt to deletion
// of any parent directory
if ((Params & cpDelete) && SinkFileParams.Skipped)
{
THROW_SKIP_FILE_NULL;
}
}
else
{
@@ -6084,6 +6099,7 @@ void __fastcall TSFTPFileSystem::SFTPSink(const UnicodeString FileName,
if (Params & cpDelete)
{
DebugAssert(FLAGCLEAR(Params, cpNoRecurse));
ChildError = true;
// If file is directory, do not delete it recursively, because it should be
// empty already. If not, it should not be deleted (some files were

View File

@@ -6,6 +6,7 @@
#include <SysUtils.hpp>
#include <FileCtrl.hpp>
#include <StrUtils.hpp>
#include "Common.h"
#include "PuttyTools.h"
@@ -679,6 +680,215 @@ bool TRetryOperationLoop::Retry()
return Result;
}
//---------------------------------------------------------------------------
TParallelOperation::TParallelOperation()
{
FCopyParam = NULL;
FParams = 0;
FProbablyEmpty = false;
FClients = 0;
FMainOperationProgress = NULL;
}
//---------------------------------------------------------------------------
void TParallelOperation::Init(
TStrings * AFileList, const UnicodeString & TargetDir, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * MainOperationProgress)
{
DebugAssert(FFileList.get() == NULL);
// More lists should really happen in scripting only, which does not support parallel transfers atm.
// But in general the code should work with more lists anyway, it just was not tested for it.
DebugAssert(AFileList->Count == 1);
FFileList.reset(AFileList);
FSection.reset(new TCriticalSection());
FTargetDir = TargetDir;
FCopyParam = CopyParam;
FParams = Params;
FMainOperationProgress = MainOperationProgress;
}
//---------------------------------------------------------------------------
TParallelOperation::~TParallelOperation()
{
WaitFor();
}
//---------------------------------------------------------------------------
bool TParallelOperation::ShouldAddClient()
{
bool Result;
// initialized already?
if (FSection.get() == NULL)
{
Result = false;
}
else
{
TGuard Guard(FSection.get());
Result = !FProbablyEmpty && (FMainOperationProgress->Cancel < csCancel);
}
return Result;
}
//---------------------------------------------------------------------------
void TParallelOperation::AddClient()
{
TGuard Guard(FSection.get());
FClients++;
}
//---------------------------------------------------------------------------
void TParallelOperation::RemoveClient()
{
TGuard Guard(FSection.get());
FClients--;
}
//---------------------------------------------------------------------------
void TParallelOperation::WaitFor()
{
bool Done;
do
{
{
TGuard Guard(FSection.get());
Done = (FClients == 0);
}
if (!Done)
{
// propagate the total progress incremented by the parallel operations
FMainOperationProgress->Progress();
Sleep(200);
}
}
while (!Done);
FProbablyEmpty = true;
}
//---------------------------------------------------------------------------
void TParallelOperation::Done(const UnicodeString & FileName, bool Dir, bool Success)
{
if (Dir)
{
TGuard Guard(FSection.get());
TDirectories::iterator DirectoryIterator = FDirectories.find(FileName);
if (DebugAlwaysTrue(DirectoryIterator != FDirectories.end()))
{
if (Success)
{
DebugAssert(!DirectoryIterator->second.Exists);
DirectoryIterator->second.Exists = true;
}
else
{
// This is actually not useful at the moment, as when creating directory fails and "Skip" is pressed,
// the current code in CopyToRemote/CreateDirectory will behave as, if it succedded, so Successs will be true here.
FDirectories.erase(DirectoryIterator);
if (FFileList->Count > 0)
{
UnicodeString FileNameWithSlash = IncludeTrailingBackslash(FileName);
// It can actually be a different list than the one the directory was taken from,
// but that does not maatter that much. It should not happen anyway, as more lists should be in scripting only.
TStrings * Files = DebugNotNull(dynamic_cast<TStrings *>(FFileList->Objects[0]));
for (int Index = 0; Index < Files->Count; Index++)
{
if (StartsText(Files->Strings[Index], FileNameWithSlash))
{
// We should add the file to "skip" counters in the OperationProgress,
// but an interactive foreground transfer is not doing that either yet.
FFileList->Delete(Index);
}
}
}
}
}
}
}
//---------------------------------------------------------------------------
int TParallelOperation::GetNext(TTerminal * Terminal, UnicodeString & FileName, UnicodeString & TargetDir, bool & Dir)
{
int Result = 1;
TStrings * Files;
do
{
if (FFileList->Count > 0)
{
Files = DebugNotNull(dynamic_cast<TStrings *>(FFileList->Objects[0]));
// can happen if the file was excluded by file mask
if (Files->Count == 0)
{
FFileList->Delete(0);
Files = NULL;
}
}
else
{
Files = NULL;
Result = -1;
}
}
while ((Result == 1) && (Files == NULL));
if (Files != NULL)
{
UnicodeString RootPath = FFileList->Strings[0];
FileName = Files->Strings[0];
Dir = (FileName[FileName.Length()] == L'\\');
if (Dir)
{
FileName.SetLength(FileName.Length() - 1);
}
UnicodeString DirPath = ExtractFileDir(FileName);
bool FirstLevel = SamePaths(DirPath, RootPath);
if (FirstLevel)
{
TargetDir = FTargetDir;
}
else
{
TDirectories::const_iterator DirectoryIterator = FDirectories.find(DirPath);
if (DebugAlwaysFalse(DirectoryIterator == FDirectories.end()))
{
throw EInvalidOperation(L"Parent path not known");
}
const TDirectoryData & DirectoryData = DirectoryIterator->second;
if (!DirectoryData.Exists)
{
Result = 0; // wait for parent directory to be created
}
else
{
TargetDir = DirectoryData.RemotePath;
}
}
if (!TargetDir.IsEmpty())
{
if (Dir)
{
TDirectoryData DirectoryData;
UnicodeString OnlyFileName = ExtractFileName(FileName);
OnlyFileName = Terminal->ChangeFileName(FCopyParam, OnlyFileName, osRemote, FirstLevel);
DirectoryData.RemotePath = UnixCombinePaths(TargetDir, OnlyFileName);
DirectoryData.Exists = false;
FDirectories.insert(std::make_pair(FileName, DirectoryData));
}
Files->Delete(0);
if (Files->Count == 0)
{
FFileList->Delete(0);
}
}
}
FProbablyEmpty = (FFileList->Count == 0);
return Result;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TTerminal::TTerminal(TSessionData * SessionData,
TConfiguration * Configuration)
@@ -2335,7 +2545,17 @@ TBatchOverwrite __fastcall TTerminal::EffectiveBatchOverwrite(
bool __fastcall TTerminal::CheckRemoteFile(
const UnicodeString & FileName, const TCopyParamType * CopyParam, int Params, TFileOperationProgressType * OperationProgress)
{
return (EffectiveBatchOverwrite(FileName, CopyParam, Params, OperationProgress, true) != boAll);
bool Result;
OperationProgress->LockUserSelections();
try
{
Result = (EffectiveBatchOverwrite(FileName, CopyParam, Params, OperationProgress, true) != boAll);
}
__finally
{
OperationProgress->UnlockUserSelections();
}
return Result;
}
//---------------------------------------------------------------------------
unsigned int __fastcall TTerminal::ConfirmFileOverwrite(
@@ -2345,84 +2565,102 @@ unsigned int __fastcall TTerminal::ConfirmFileOverwrite(
UnicodeString Message)
{
unsigned int Result;
// duplicated in TSFTPFileSystem::SFTPConfirmOverwrite
TBatchOverwrite BatchOverwrite;
bool CanAlternateResume =
(FileParams != NULL) &&
(FileParams->DestSize < FileParams->SourceSize) &&
!OperationProgress->AsciiTransfer;
TBatchOverwrite BatchOverwrite = EffectiveBatchOverwrite(SourceFullFileName, CopyParam, Params, OperationProgress, true);
bool Applicable = true;
switch (BatchOverwrite)
OperationProgress->LockUserSelections();
try
{
case boOlder:
Applicable = (FileParams != NULL);
break;
// duplicated in TSFTPFileSystem::SFTPConfirmOverwrite
BatchOverwrite = EffectiveBatchOverwrite(SourceFullFileName, CopyParam, Params, OperationProgress, true);
bool Applicable = true;
switch (BatchOverwrite)
{
case boOlder:
Applicable = (FileParams != NULL);
break;
case boAlternateResume:
Applicable = CanAlternateResume;
break;
case boAlternateResume:
Applicable = CanAlternateResume;
break;
case boResume:
Applicable = CanAlternateResume;
break;
case boResume:
Applicable = CanAlternateResume;
break;
}
if (!Applicable)
{
TBatchOverwrite ABatchOverwrite = EffectiveBatchOverwrite(SourceFullFileName, CopyParam, Params, OperationProgress, false);
DebugAssert(BatchOverwrite != ABatchOverwrite);
BatchOverwrite = ABatchOverwrite;
}
if (BatchOverwrite == boNo)
{
// particularly with parallel transfers, the overal operation can be already cancelled by other parallel operation
if (OperationProgress->Cancel > csContinue)
{
Result = qaCancel;
}
else
{
if (Message.IsEmpty())
{
// Side refers to destination side here
Message = FMTLOAD((Side == osLocal ? LOCAL_FILE_OVERWRITE2 :
REMOTE_FILE_OVERWRITE2), (TargetFileName, TargetFileName));
}
if (FileParams != NULL)
{
Message = FMTLOAD(FILE_OVERWRITE_DETAILS, (Message,
FormatSize(FileParams->SourceSize),
UserModificationStr(FileParams->SourceTimestamp, FileParams->SourcePrecision),
FormatSize(FileParams->DestSize),
UserModificationStr(FileParams->DestTimestamp, FileParams->DestPrecision)));
}
if (DebugAlwaysTrue(QueryParams->HelpKeyword.IsEmpty()))
{
QueryParams->HelpKeyword = HELP_OVERWRITE;
}
Result = QueryUser(Message, NULL, Answers, QueryParams);
switch (Result)
{
case qaNeverAskAgain:
Configuration->ConfirmOverwriting = false;
Result = qaYes;
break;
case qaYesToAll:
BatchOverwrite = boAll;
break;
case qaAll:
BatchOverwrite = boOlder;
break;
case qaNoToAll:
BatchOverwrite = boNone;
break;
}
// we user has not selected another batch overwrite mode,
// keep the current one. note that we may get here even
// when batch overwrite was selected already, but it could not be applied
// to current transfer (see condition above)
if (BatchOverwrite != boNo)
{
OperationProgress->SetBatchOverwrite(BatchOverwrite);
}
}
}
}
if (!Applicable)
__finally
{
TBatchOverwrite ABatchOverwrite = EffectiveBatchOverwrite(SourceFullFileName, CopyParam, Params, OperationProgress, false);
DebugAssert(BatchOverwrite != ABatchOverwrite);
BatchOverwrite = ABatchOverwrite;
}
if (BatchOverwrite == boNo)
{
if (Message.IsEmpty())
{
// Side refers to destination side here
Message = FMTLOAD((Side == osLocal ? LOCAL_FILE_OVERWRITE2 :
REMOTE_FILE_OVERWRITE2), (TargetFileName, TargetFileName));
}
if (FileParams != NULL)
{
Message = FMTLOAD(FILE_OVERWRITE_DETAILS, (Message,
FormatSize(FileParams->SourceSize),
UserModificationStr(FileParams->SourceTimestamp, FileParams->SourcePrecision),
FormatSize(FileParams->DestSize),
UserModificationStr(FileParams->DestTimestamp, FileParams->DestPrecision)));
}
if (DebugAlwaysTrue(QueryParams->HelpKeyword.IsEmpty()))
{
QueryParams->HelpKeyword = HELP_OVERWRITE;
}
Result = QueryUser(Message, NULL, Answers, QueryParams);
switch (Result)
{
case qaNeverAskAgain:
Configuration->ConfirmOverwriting = false;
Result = qaYes;
break;
case qaYesToAll:
BatchOverwrite = boAll;
break;
case qaAll:
BatchOverwrite = boOlder;
break;
case qaNoToAll:
BatchOverwrite = boNone;
break;
}
// we user has not selected another batch overwrite mode,
// keep the current one. note that we may get here even
// when batch overwrite was selected already, but it could not be applied
// to current transfer (see condition above)
if (BatchOverwrite != boNo)
{
OperationProgress->SetBatchOverwrite(BatchOverwrite);
}
OperationProgress->UnlockUserSelections();
}
if (BatchOverwrite != boNo)
@@ -4289,28 +4527,36 @@ bool __fastcall TTerminal::DoCreateLocalFile(const UnicodeString FileName,
{
if (FLAGSET(FileAttr, faReadOnly))
{
if (OperationProgress->BatchOverwrite == boNone)
OperationProgress->LockUserSelections();
try
{
Result = false;
}
else if ((OperationProgress->BatchOverwrite != boAll) && !NoConfirmation)
{
unsigned int Answer;
if (OperationProgress->BatchOverwrite == boNone)
{
TSuspendFileOperationProgress Suspend(OperationProgress);
Answer = QueryUser(
MainInstructions(FMTLOAD(READ_ONLY_OVERWRITE, (FileName))), NULL,
qaYes | qaNo | qaCancel | qaYesToAll | qaNoToAll, 0);
Result = false;
}
else if ((OperationProgress->BatchOverwrite != boAll) && !NoConfirmation)
{
unsigned int Answer;
switch (Answer) {
case qaYesToAll: OperationProgress->SetBatchOverwrite(boAll); break;
case qaCancel: OperationProgress->SetCancel(csCancel); // continue on next case
case qaNoToAll: OperationProgress->SetBatchOverwrite(boNone);
case qaNo: Result = false; break;
{
TSuspendFileOperationProgress Suspend(OperationProgress);
Answer = QueryUser(
MainInstructions(FMTLOAD(READ_ONLY_OVERWRITE, (FileName))), NULL,
qaYes | qaNo | qaCancel | qaYesToAll | qaNoToAll, 0);
}
switch (Answer) {
case qaYesToAll: OperationProgress->SetBatchOverwrite(boAll); break;
case qaCancel: OperationProgress->SetCancel(csCancel); // continue on next case
case qaNoToAll: OperationProgress->SetBatchOverwrite(boNone);
case qaNo: Result = false; break;
}
}
}
__finally
{
OperationProgress->UnlockUserSelections();
}
}
else
{
@@ -4569,6 +4815,17 @@ void __fastcall TTerminal::CalculateLocalFileSize(const UnicodeString FileName,
if (AllowTransfer)
{
if (AParams->Files != NULL)
{
UnicodeString FullFileName = ::ExpandFileName(FileName);
if (Dir)
{
FullFileName += L"\\";
}
AParams->Files->Add(FullFileName);
}
if (!Dir)
{
AParams->Size += Size;
@@ -4587,7 +4844,7 @@ void __fastcall TTerminal::CalculateLocalFileSize(const UnicodeString FileName,
}
//---------------------------------------------------------------------------
bool __fastcall TTerminal::CalculateLocalFilesSize(TStrings * FileList,
__int64 & Size, const TCopyParamType * CopyParam, bool AllowDirs)
__int64 & Size, const TCopyParamType * CopyParam, bool AllowDirs, TStrings * Files)
{
bool Result = false;
TFileOperationProgressType OperationProgress(&DoProgress, &DoFinished);
@@ -4599,23 +4856,39 @@ bool __fastcall TTerminal::CalculateLocalFilesSize(TStrings * FileList,
Params.Size = 0;
Params.Params = 0;
Params.CopyParam = CopyParam;
Params.Files = NULL;
Params.Result = true;
DebugAssert(!FOperationProgress);
FOperationProgress = &OperationProgress;
UnicodeString LastDirPath;
for (int Index = 0; Params.Result && (Index < FileList->Count); Index++)
{
UnicodeString FileName = FileList->Strings[Index];
TSearchRec Rec;
if (FileSearchRec(FileName, Rec))
{
if (FLAGSET(Rec.Attr, faDirectory) && !AllowDirs)
bool Dir = FLAGSET(Rec.Attr, faDirectory);
if (Dir && !AllowDirs)
{
Params.Result = false;
}
else
{
if (Files != NULL)
{
UnicodeString FullFileName = ::ExpandFileName(FileName);
UnicodeString DirPath = ExtractFilePath(FullFileName);
if (DirPath != LastDirPath)
{
Params.Files = new TStringList();
LastDirPath = DirPath;
Files->AddObject(LastDirPath, Params.Files);
}
}
CalculateLocalFileSize(FileName, Rec, &Params);
OperationProgress.Finish(FileName, true, OnceDoneOperation);
}
}
@@ -5335,7 +5608,7 @@ void __fastcall TTerminal::SynchronizeApply(TSynchronizeChecklist * Checklist,
}
if ((UploadList->Count > 0) &&
!CopyToRemote(UploadList, Data.RemoteDirectory, &SyncCopyParam, CopyParams))
!CopyToRemote(UploadList, Data.RemoteDirectory, &SyncCopyParam, CopyParams, NULL))
{
Abort();
}
@@ -5674,8 +5947,46 @@ bool __fastcall TTerminal::GetStoredCredentialsTried()
return Result;
}
//---------------------------------------------------------------------------
int __fastcall TTerminal::CopyToRemoteParallel(TParallelOperation * ParallelOperation, TFileOperationProgressType * OperationProgress)
{
UnicodeString FileName;
UnicodeString TargetDir;
bool Dir;
int Result = ParallelOperation->GetNext(this, FileName, TargetDir, Dir);
if (Result > 0)
{
std::unique_ptr<TStrings> FilesToCopy(new TStringList());
FilesToCopy->Add(FileName);
UnicodeString UnlockedTargetDir = TranslateLockedPath(TargetDir, false);
// OnceDoneOperation is not supported
TOnceDoneOperation OnceDoneOperation = odoIdle;
int Params = ParallelOperation->Params;
if (Dir)
{
Params = Params | cpNoRecurse;
}
int Prev = OperationProgress->FilesFinishedSuccessfully;
try
{
FFileSystem->CopyToRemote(
FilesToCopy.get(), UnlockedTargetDir, ParallelOperation->CopyParam, Params, OperationProgress, OnceDoneOperation);
}
__finally
{
bool Success = (Prev < OperationProgress->FilesFinishedSuccessfully);
ParallelOperation->Done(FileName, Dir, Success);
}
}
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall TTerminal::CopyToRemote(TStrings * FilesToCopy,
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params)
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params, TParallelOperation * ParallelOperation)
{
DebugAssert(FFileSystem);
DebugAssert(FilesToCopy);
@@ -5687,12 +5998,24 @@ bool __fastcall TTerminal::CopyToRemote(TStrings * FilesToCopy,
try
{
__int64 Size;
std::unique_ptr<TStringList> Files;
if ((ParallelOperation != NULL) &&
FFileSystem->IsCapable(fsParallelTransfers) &&
// parallel upload is not implemented for operations needed to be done on a folder
// after all its files are processed
FLAGCLEAR(Params, cpDelete) &&
!CopyParam->ClearArchive &&
(!CopyParam->PreserveTime || !CopyParam->PreserveTimeDirs))
{
Files.reset(new TStringList());
Files->OwnsObjects = true;
}
// dirty trick: when moving, do not pass copy param to avoid exclude mask
bool CalculatedSize =
CalculateLocalFilesSize(
FilesToCopy, Size,
(FLAGCLEAR(Params, cpDelete) ? CopyParam : NULL),
CopyParam->CalculateSize);
CopyParam->CalculateSize, Files.get());
TFileOperationProgressType OperationProgress(&DoProgress, &DoFinished);
OperationProgress.Start((Params & cpDelete ? foMove : foCopy), osLocal,
@@ -5720,15 +6043,49 @@ bool __fastcall TTerminal::CopyToRemote(TStrings * FilesToCopy,
BeginTransaction();
try
{
bool Parallel = CalculatedSize && (Files.get() != NULL);
if (Log->Logging)
{
LogEvent(FORMAT(L"Copying %d files/directories to remote directory "
"\"%s\"", (FilesToCopy->Count, TargetDir)));
"\"%s\"%s", (FilesToCopy->Count, TargetDir, (Parallel ? L" in parallel" : L""))));
LogEvent(CopyParam->LogStr);
}
FFileSystem->CopyToRemote(FilesToCopy, UnlockedTargetDir,
CopyParam, Params, &OperationProgress, OnceDoneOperation);
if (Parallel)
{
// OnceDoneOperation is not supported
ParallelOperation->Init(Files.release(), UnlockedTargetDir, CopyParam, Params, &OperationProgress);
try
{
bool Continue = true;
do
{
int GotNext = CopyToRemoteParallel(ParallelOperation, &OperationProgress);
if (GotNext < 0)
{
Continue = false;
}
else if (GotNext == 0)
{
Sleep(100);
}
}
while (Continue);
Result = true;
}
__finally
{
OperationProgress.SetDone();
ParallelOperation->WaitFor();
}
}
else
{
FFileSystem->CopyToRemote(FilesToCopy, UnlockedTargetDir,
CopyParam, Params, &OperationProgress, OnceDoneOperation);
}
}
__finally
{

View File

@@ -28,6 +28,7 @@ struct TSpaceAvailable;
struct TFilesFindParams;
class TTunnelUI;
class TCallbackGuard;
class TParallelOperation;
//---------------------------------------------------------------------------
typedef void __fastcall (__closure *TQueryUserEvent)
(TObject * Sender, const UnicodeString Query, TStrings * MoreMessages, unsigned int Answers,
@@ -107,6 +108,7 @@ const int cpTemporary = 0x04;
const int cpNoConfirmation = 0x08;
const int cpAppend = 0x20;
const int cpResume = 0x40;
const int cpNoRecurse = 0x80;
//---------------------------------------------------------------------------
const int ccApplyToDirectories = 0x01;
const int ccRecursive = 0x02;
@@ -297,7 +299,7 @@ protected:
void __fastcall CalculateLocalFileSize(const UnicodeString FileName,
const TSearchRec Rec, /*__int64*/ void * Size);
bool __fastcall CalculateLocalFilesSize(TStrings * FileList, __int64 & Size,
const TCopyParamType * CopyParam, bool AllowDirs);
const TCopyParamType * CopyParam, bool AllowDirs, TStrings * Files);
TBatchOverwrite __fastcall EffectiveBatchOverwrite(
const UnicodeString & SourceFullFileName, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, bool Special);
@@ -427,7 +429,8 @@ public:
bool __fastcall CopyToLocal(TStrings * FilesToCopy,
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params);
bool __fastcall CopyToRemote(TStrings * FilesToCopy,
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params);
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params, TParallelOperation * ParallelOperation);
int __fastcall CopyToRemoteParallel(TParallelOperation * ParallelOperation, TFileOperationProgressType * OperationProgress);
void __fastcall CreateDirectory(const UnicodeString DirName,
const TRemoteProperties * Properties = NULL);
void __fastcall CreateLink(const UnicodeString FileName, const UnicodeString PointTo, bool Symbolic);
@@ -621,6 +624,7 @@ struct TCalculateSizeParams
const TCopyParamType * CopyParam;
TCalculateSizeStats * Stats;
bool AllowDirs;
TStrings * Files;
bool Result;
};
//---------------------------------------------------------------------------
@@ -751,4 +755,45 @@ private:
TDateTime FStart;
};
//---------------------------------------------------------------------------
class TParallelOperation
{
public:
TParallelOperation();
~TParallelOperation();
void Init(
TStrings * AFiles, const UnicodeString & TargetDir, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * MainOperationProgress);
void WaitFor();
bool ShouldAddClient();
void AddClient();
void RemoveClient();
int GetNext(TTerminal * Terminal, UnicodeString & FileName, UnicodeString & TargetDir, bool & Dir);
void Done(const UnicodeString & FileName, bool Dir, bool Success);
__property const TCopyParamType * CopyParam = { read = FCopyParam };
__property int Params = { read = FParams };
__property UnicodeString TargetDir = { read = FTargetDir };
__property TFileOperationProgressType * MainOperationProgress = { read = FMainOperationProgress };
private:
struct TDirectoryData
{
UnicodeString RemotePath;
bool Exists;
};
std::unique_ptr<TStrings> FFileList;
typedef std::map<UnicodeString, TDirectoryData> TDirectories;
TDirectories FDirectories;
UnicodeString FTargetDir;
const TCopyParamType * FCopyParam;
int FParams;
bool FProbablyEmpty;
int FClients;
std::unique_ptr<TCriticalSection> FSection;
TFileOperationProgressType * FMainOperationProgress;
};
//---------------------------------------------------------------------------
#endif

View File

@@ -679,6 +679,7 @@ bool __fastcall TWebDAVFileSystem::IsCapable(int Capability) const
// instead of trying to open it as directory
case fcResolveSymlink:
case fsSkipTransfer:
case fsParallelTransfers:
return true;
case fcUserGroupListing:
@@ -1642,70 +1643,73 @@ void __fastcall TWebDAVFileSystem::DirectorySource(const UnicodeString Directory
OperationProgress->SetFile(DirectoryName);
int FindAttrs = faReadOnly | faHidden | faSysFile | faDirectory | faArchive;
TSearchRecChecked SearchRec;
bool FindOK;
FILE_OPERATION_LOOP_BEGIN
if (FLAGCLEAR(Params, cpNoRecurse))
{
FindOK =
(FindFirstChecked(DirectoryName + L"*.*", FindAttrs, SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
int FindAttrs = faReadOnly | faHidden | faSysFile | faDirectory | faArchive;
TSearchRecChecked SearchRec;
bool FindOK;
try
{
while (FindOK && !OperationProgress->Cancel)
FILE_OPERATION_LOOP_BEGIN
{
UnicodeString FileName = DirectoryName + SearchRec.Name;
try
{
if ((SearchRec.Name != L".") && (SearchRec.Name != L".."))
{
SourceRobust(FileName, DestFullName, CopyParam, Params, OperationProgress,
Flags & ~(tfFirstLevel));
}
}
catch (EScpSkipFile & E)
{
// If ESkipFile occurs, just log it and continue with next file
TSuspendFileOperationProgress Suspend(OperationProgress);
// here a message to user was displayed, which was not appropriate
// when user refused to overwrite the file in subdirectory.
// hopefully it won't be missing in other situations.
if (!FTerminal->HandleException(&E))
{
throw;
}
}
FILE_OPERATION_LOOP_BEGIN
{
FindOK = (FindNextChecked(SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
FindOK =
(FindFirstChecked(DirectoryName + L"*.*", FindAttrs, SearchRec) == 0);
}
}
__finally
{
FindClose(SearchRec);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
// TODO : Delete also read-only directories.
// TODO : Show error message on failure.
if (!OperationProgress->Cancel)
{
if (FLAGSET(Params, cpDelete))
try
{
RemoveDir(ApiPath(DirectoryName));
}
else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
{
FILE_OPERATION_LOOP_BEGIN
while (FindOK && !OperationProgress->Cancel)
{
THROWOSIFFALSE(FileSetAttr(ApiPath(DirectoryName), Attrs & ~faArchive) == 0);
UnicodeString FileName = DirectoryName + SearchRec.Name;
try
{
if ((SearchRec.Name != L".") && (SearchRec.Name != L".."))
{
SourceRobust(FileName, DestFullName, CopyParam, Params, OperationProgress,
Flags & ~(tfFirstLevel));
}
}
catch (EScpSkipFile & E)
{
// If ESkipFile occurs, just log it and continue with next file
TSuspendFileOperationProgress Suspend(OperationProgress);
// here a message to user was displayed, which was not appropriate
// when user refused to overwrite the file in subdirectory.
// hopefully it won't be missing in other situations.
if (!FTerminal->HandleException(&E))
{
throw;
}
}
FILE_OPERATION_LOOP_BEGIN
{
FindOK = (FindNextChecked(SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
}
}
__finally
{
FindClose(SearchRec);
}
// TODO : Delete also read-only directories.
// TODO : Show error message on failure.
if (!OperationProgress->Cancel)
{
if (FLAGSET(Params, cpDelete))
{
RemoveDir(ApiPath(DirectoryName));
}
else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
{
FILE_OPERATION_LOOP_BEGIN
{
THROWOSIFFALSE(FileSetAttr(ApiPath(DirectoryName), Attrs & ~faArchive) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(CANT_SET_ATTRS, (DirectoryName)));
}
FILE_OPERATION_LOOP_END(FMTLOAD(CANT_SET_ATTRS, (DirectoryName)));
}
}
}
@@ -2116,22 +2120,25 @@ void __fastcall TWebDAVFileSystem::Sink(const UnicodeString FileName,
}
FILE_OPERATION_LOOP_END(FMTLOAD(CREATE_DIR_ERROR, (DestFullName)));
TSinkFileParams SinkFileParams;
SinkFileParams.TargetDir = IncludeTrailingBackslash(DestFullName);
SinkFileParams.CopyParam = CopyParam;
SinkFileParams.Params = Params;
SinkFileParams.OperationProgress = OperationProgress;
SinkFileParams.Skipped = false;
SinkFileParams.Flags = Flags & ~tfFirstLevel;
FTerminal->ProcessDirectory(FileName, SinkFile, &SinkFileParams);
// Do not delete directory if some of its files were skip.
// Throw "skip file" for the directory to avoid attempt to deletion
// of any parent directory
if (FLAGSET(Params, cpDelete) && SinkFileParams.Skipped)
if (FLAGCLEAR(Params, cpNoRecurse))
{
THROW_SKIP_FILE_NULL;
TSinkFileParams SinkFileParams;
SinkFileParams.TargetDir = IncludeTrailingBackslash(DestFullName);
SinkFileParams.CopyParam = CopyParam;
SinkFileParams.Params = Params;
SinkFileParams.OperationProgress = OperationProgress;
SinkFileParams.Skipped = false;
SinkFileParams.Flags = Flags & ~tfFirstLevel;
FTerminal->ProcessDirectory(FileName, SinkFile, &SinkFileParams);
// Do not delete directory if some of its files were skip.
// Throw "skip file" for the directory to avoid attempt to deletion
// of any parent directory
if (FLAGSET(Params, cpDelete) && SinkFileParams.Skipped)
{
THROW_SKIP_FILE_NULL;
}
}
}
else
@@ -2260,6 +2267,7 @@ void __fastcall TWebDAVFileSystem::Sink(const UnicodeString FileName,
if (FLAGSET(Params, cpDelete))
{
DebugAssert(FLAGCLEAR(Params, cpNoRecurse));
ChildError = true;
// If file is directory, do not delete it recursively, because it should be
// empty already. If not, it should not be deleted (some files were

View File

@@ -268,7 +268,6 @@ void __fastcall TCopyDialog::SetParams(const TGUICopyParamType & value)
FCopyParams = value;
DirectoryEdit->Text = Directory + FParams.FileMask;
QueueCheck2->Checked = FParams.Queue;
QueueIndividuallyCheck->Checked = FParams.QueueIndividually;
UpdateControls();
}
//---------------------------------------------------------------------------
@@ -278,7 +277,6 @@ TGUICopyParamType __fastcall TCopyDialog::GetParams()
FParams = FCopyParams;
FParams.FileMask = GetFileMask();
FParams.Queue = QueueCheck2->Checked;
FParams.QueueIndividually = QueueIndividuallyCheck->Checked;
return FParams;
}
//---------------------------------------------------------------------------
@@ -337,10 +335,6 @@ void __fastcall TCopyDialog::UpdateControls()
bool RemoteTransfer = FLAGSET(FOutputOptions, cooRemoteTransfer);
EnableControl(QueueCheck2,
((FOptions & (coDisableQueue | coTemp)) == 0) && !RemoteTransfer);
QueueIndividuallyCheck->Visible =
FLAGCLEAR(FOptions, coNoQueueIndividually) &&
QueueCheck2->Enabled && QueueCheck2->Checked &&
(FFileList != NULL) && (FFileList->Count > 1);
TransferSettingsButton->Style =
FLAGCLEAR(FOptions, coDoNotUsePresets) ?

View File

@@ -96,15 +96,6 @@ object CopyDialog: TCopyDialog
TabOrder = 4
OnClick = ControlChange
end
object QueueIndividuallyCheck: TCheckBox
Left = 312
Top = 112
Width = 193
Height = 17
Caption = '&Transfer each file individually'
TabOrder = 5
OnClick = ControlChange
end
object HelpButton: TButton
Left = 427
Top = 136

View File

@@ -25,7 +25,6 @@ __published:
TButton *CancelButton;
TButton *LocalDirectoryBrowseButton;
TCheckBox *QueueCheck2;
TCheckBox *QueueIndividuallyCheck;
TButton *HelpButton;
TCheckBox *NeverShowAgainCheck;
TButton *TransferSettingsButton;

View File

@@ -1109,26 +1109,7 @@ bool __fastcall TCustomScpExplorerForm::CopyParamDialog(
Params |=
(CopyParam.QueueNoConfirmation ? cpNoConfirmation : 0);
if (CopyParam.QueueIndividually)
{
for (int Index = 0; Index < FileList->Count; Index++)
{
TStrings * FileList1 = new TStringList();
try
{
FileList1->AddObject(FileList->Strings[Index], FileList->Objects[Index]);
AddQueueItem(Queue, Direction, FileList1, TargetDirectory, CopyParam, Params);
}
__finally
{
delete FileList1;
}
}
}
else
{
AddQueueItem(Queue, Direction, FileList, TargetDirectory, CopyParam, Params);
}
AddQueueItem(Queue, Direction, FileList, TargetDirectory, CopyParam, Params);
Result = false;
ClearTransferSourceSelection(Direction);
@@ -1155,7 +1136,7 @@ void __fastcall TCustomScpExplorerForm::ClearTransferSourceSelection(TTransferDi
//---------------------------------------------------------------------------
void __fastcall TCustomScpExplorerForm::AddQueueItem(
TTerminalQueue * Queue, TTransferDirection Direction, TStrings * FileList,
const UnicodeString TargetDirectory, const TCopyParamType & CopyParam,
const UnicodeString TargetDirectory, const TGUICopyParamType & CopyParam,
int Params)
{
DebugAssert(Queue != NULL);
@@ -1179,7 +1160,7 @@ void __fastcall TCustomScpExplorerForm::AddQueueItem(
if (Direction == tdToRemote)
{
QueueItem = new TUploadQueueItem(Terminal, FileList, TargetDirectory,
&CopyParam, Params, SingleFile);
&CopyParam, Params, SingleFile, CopyParam.QueueParallel);
}
else
{
@@ -1996,7 +1977,7 @@ void __fastcall TCustomScpExplorerForm::CustomCommand(TStrings * FileList,
std::unique_ptr<TStrings> TemporaryFilesList(new TStringList());
TemporaryFilesList->Add(FileName);
FTerminal->CopyToRemote(TemporaryFilesList.get(), RemoteDir, &CopyParam, cpTemporary);
FTerminal->CopyToRemote(TemporaryFilesList.get(), RemoteDir, &CopyParam, cpTemporary, NULL);
}
}
}
@@ -2368,7 +2349,7 @@ bool __fastcall TCustomScpExplorerForm::ExecuteFileOperation(TFileOperation Oper
PermanentFileList = FileList;
Params |= FLAGMASK(Temp, cpTemporary);
Terminal->CopyToRemote(FileList, TargetDirectory, &CopyParam, Params);
Terminal->CopyToRemote(FileList, TargetDirectory, &CopyParam, Params, NULL);
if (Operation == foMove)
{
ReloadLocalDirectory();
@@ -3427,7 +3408,7 @@ void __fastcall TCustomScpExplorerForm::ExecutedFileChanged(const UnicodeString
int Params = cpNoConfirmation | cpTemporary;
TQueueItem * QueueItem = new TUploadQueueItem(Data->Terminal, FileList,
Data->RemoteDirectory, &CopyParam, Params, true);
Data->RemoteDirectory, &CopyParam, Params, true, false);
QueueItem->CompleteEvent = UploadCompleteEvent;
AddQueueItem(Data->Queue, QueueItem, Data->Terminal);
}
@@ -3829,7 +3810,7 @@ bool __fastcall TCustomScpExplorerForm::RemoteTransferFiles(
DebugAssert(!FAutoOperation);
FAutoOperation = true;
FTerminal->CopyToRemote(TemporaryFilesList, Target, &CopyParam, cpTemporary);
FTerminal->CopyToRemote(TemporaryFilesList, Target, &CopyParam, cpTemporary, NULL);
}
}
__finally

View File

@@ -274,7 +274,7 @@ private:
void __fastcall QueueSplitterDblClick(TObject * Sender);
void __fastcall AddQueueItem(TTerminalQueue * Queue, TTransferDirection Direction,
TStrings * FileList, const UnicodeString TargetDirectory,
const TCopyParamType & CopyParam, int Params);
const TGUICopyParamType & CopyParam, int Params);
void __fastcall AddQueueItem(TTerminalQueue * Queue, TQueueItem * QueueItem, TTerminal * Terminal);
void __fastcall ClearTransferSourceSelection(TTransferDirection Direction);
void __fastcall SessionsDDDragOver(int KeyState, const TPoint & Point, int & Effect);

View File

@@ -394,7 +394,7 @@ void __fastcall TPreferencesDialog::LoadConfiguration()
EnableQueueByDefaultCheck->Checked = WinConfiguration->EnableQueueByDefault;
QueueAutoPopupCheck->Checked = GUIConfiguration->QueueAutoPopup;
QueueCheck->Checked = GUIConfiguration->DefaultCopyParam.Queue;
QueueIndividuallyCheck->Checked = GUIConfiguration->DefaultCopyParam.QueueIndividually;
QueueParallelCheck->Checked = GUIConfiguration->DefaultCopyParam.QueueParallel;
QueueNoConfirmationCheck->Checked = GUIConfiguration->DefaultCopyParam.QueueNoConfirmation;
if (!GUIConfiguration->QueueKeepDoneItems)
{
@@ -746,7 +746,7 @@ void __fastcall TPreferencesDialog::SaveConfiguration()
WinConfiguration->EnableQueueByDefault = EnableQueueByDefaultCheck->Checked;
GUIConfiguration->QueueAutoPopup = QueueAutoPopupCheck->Checked;
CopyParam.Queue = QueueCheck->Checked;
CopyParam.QueueIndividually = QueueIndividuallyCheck->Checked;
CopyParam.QueueParallel = QueueParallelCheck->Checked;
CopyParam.QueueNoConfirmation = QueueNoConfirmationCheck->Checked;
GUIConfiguration->QueueKeepDoneItems = (QueueKeepDoneItemsForCombo->ItemIndex != 0);
switch (QueueKeepDoneItemsForCombo->ItemIndex)

View File

@@ -1468,13 +1468,13 @@ object PreferencesDialog: TPreferencesDialog
Caption = '&No confirmations for background transfers'
TabOrder = 4
end
object QueueIndividuallyCheck: TCheckBox
object QueueParallelCheck: TCheckBox
Left = 16
Top = 98
Width = 369
Height = 17
Anchors = [akLeft, akTop, akRight]
Caption = '&Transfer each file individually on background by default'
Caption = '&Use multiple connections for single transfer'
TabOrder = 3
end
object EnableQueueByDefaultCheck: TCheckBox

View File

@@ -192,7 +192,7 @@ __published:
TRadioButton *UpdatesAutoCheck;
TCheckBox *ConfirmTransferringCheck;
TGroupBox *UpdatesOptionsGroup;
TCheckBox *QueueIndividuallyCheck;
TCheckBox *QueueParallelCheck;
TCheckBox *SessionReopenAutoIdleCheck;
TCheckBox *RenameWholeNameCheck;
TCheckBox *TreeOnLeftCheck;

View File

@@ -60,7 +60,7 @@ void __fastcall TGUICopyParamType::GUIAssign(const TGUICopyParamType * Source)
{
Queue = Source->Queue;
QueueNoConfirmation = Source->QueueNoConfirmation;
QueueIndividually = Source->QueueIndividually;
QueueParallel = Source->QueueParallel;
}
//---------------------------------------------------------------------------
void __fastcall TGUICopyParamType::Default()
@@ -74,7 +74,7 @@ void __fastcall TGUICopyParamType::GUIDefault()
{
Queue = false;
QueueNoConfirmation = true;
QueueIndividually = false;
QueueParallel = true;
}
//---------------------------------------------------------------------------
void __fastcall TGUICopyParamType::Load(THierarchicalStorage * Storage)
@@ -83,7 +83,7 @@ void __fastcall TGUICopyParamType::Load(THierarchicalStorage * Storage)
Queue = Storage->ReadBool(L"Queue", Queue);
QueueNoConfirmation = Storage->ReadBool(L"QueueNoConfirmation", QueueNoConfirmation);
QueueIndividually = Storage->ReadBool(L"QueueIndividually", QueueIndividually);
QueueParallel = Storage->ReadBool(L"QueueParallel", QueueParallel);
}
//---------------------------------------------------------------------------
void __fastcall TGUICopyParamType::Save(THierarchicalStorage * Storage)
@@ -92,7 +92,7 @@ void __fastcall TGUICopyParamType::Save(THierarchicalStorage * Storage)
Storage->WriteBool(L"Queue", Queue);
Storage->WriteBool(L"QueueNoConfirmation", QueueNoConfirmation);
Storage->WriteBool(L"QueueIndividually", QueueIndividually);
Storage->WriteBool(L"QueueParallel", QueueParallel);
}
//---------------------------------------------------------------------------
TGUICopyParamType & __fastcall TGUICopyParamType::operator =(const TCopyParamType & rhp)

View File

@@ -39,7 +39,7 @@ public:
__property bool Queue = { read = FQueue, write = FQueue };
__property bool QueueNoConfirmation = { read = FQueueNoConfirmation, write = FQueueNoConfirmation };
__property bool QueueIndividually = { read = FQueueIndividually, write = FQueueIndividually };
__property bool QueueParallel = { read = FQueueParallel, write = FQueueParallel };
protected:
void __fastcall GUIDefault();
@@ -48,7 +48,7 @@ protected:
private:
bool FQueue;
bool FQueueNoConfirmation;
bool FQueueIndividually;
bool FQueueParallel;
};
//---------------------------------------------------------------------------
struct TCopyParamRuleData

View File

@@ -291,7 +291,7 @@ void __fastcall TQueueController::ExecuteOperation(TQueueOperation Operation,
}
//---------------------------------------------------------------------------
void __fastcall TQueueController::FillQueueViewItem(TListItem * Item,
TQueueItemProxy * QueueItem, bool Detail)
TQueueItemProxy * QueueItem, bool Detail, bool OnlyLine)
{
DebugAssert(!Detail || (QueueItem->Status != TQueueItem::qsPending));
@@ -345,7 +345,7 @@ void __fastcall TQueueController::FillQueueViewItem(TListItem * Item,
TFileOperationProgressType * ProgressData = QueueItem->ProgressData;
TQueueItem::TInfo * Info = QueueItem->Info;
if (!Detail)
if (!Detail && Info->Primary)
{
switch (Info->Operation)
{
@@ -358,6 +358,11 @@ void __fastcall TQueueController::FillQueueViewItem(TListItem * Item,
break;
}
if (!OnlyLine)
{
Image = -1;
}
// cannot use ProgressData->Temp as it is set only after the transfer actually starts
Values[0] = Info->Source.IsEmpty() ? LoadStr(PROGRESS_TEMP_DIR) : Info->Source;
Values[1] = Info->Destination.IsEmpty() ? LoadStr(PROGRESS_TEMP_DIR) : Info->Destination;
@@ -401,7 +406,6 @@ void __fastcall TQueueController::FillQueueViewItem(TListItem * Item,
}
else
{
Image = -1;
if (ProgressData != NULL)
{
if ((Info->Side == osRemote) || !ProgressData->Temp)
@@ -491,16 +495,17 @@ void __fastcall TQueueController::UpdateQueueStatus(
}
Item = InsertItemFor(QueueItem, Index);
FillQueueViewItem(Item, QueueItem, false);
bool HasDetailsLine = UseDetailsLine(ItemIndex, QueueItem);
FillQueueViewItem(Item, QueueItem, false, !HasDetailsLine);
Index++;
DebugAssert((QueueItem->Status != TQueueItem::qsPending) ==
(ItemIndex < FQueueStatus->DoneAndActiveCount));
if (UseDetailsLine(ItemIndex, QueueItem))
if (HasDetailsLine)
{
Item = InsertItemFor(QueueItem, Index);
FillQueueViewItem(Item, QueueItem, true);
FillQueueViewItem(Item, QueueItem, true, false);
Index++;
}
}
@@ -523,7 +528,9 @@ bool __fastcall TQueueController::UseDetailsLine(int ItemIndex, TQueueItemProxy
return
(ItemIndex >= FQueueStatus->DoneCount) &&
(ItemIndex < FQueueStatus->DoneAndActiveCount) &&
!QueueItem->Info->SingleFile;
QueueItem->Info->Primary &&
!QueueItem->Info->SingleFile &&
((QueueItem->ProgressData == NULL) || !QueueItem->ProgressData->Done);
}
//---------------------------------------------------------------------------
void __fastcall TQueueController::RefreshQueueItem(TQueueItemProxy * QueueItem)
@@ -544,15 +551,23 @@ void __fastcall TQueueController::RefreshQueueItem(TQueueItemProxy * QueueItem)
}
}
FillQueueViewItem(ListItem, QueueItem, false);
bool HasDetailsLine = UseDetailsLine(QueueItem->Index, QueueItem);
FillQueueViewItem(ListItem, QueueItem, false, !HasDetailsLine);
if (UseDetailsLine(QueueItem->Index, QueueItem))
if (HasDetailsLine)
{
if (NextListItem == NULL)
{
NextListItem = FListView->Items->Insert(Index + 1);
}
FillQueueViewItem(NextListItem, QueueItem, true);
FillQueueViewItem(NextListItem, QueueItem, true, false);
}
else
{
if (NextListItem != NULL)
{
NextListItem->Delete();
}
}
DoChange();

View File

@@ -47,7 +47,7 @@ private:
void __fastcall RememberConfiguration();
static void __fastcall FillQueueViewItem(TListItem * Item,
TQueueItemProxy * QueueItem, bool Detail);
TQueueItemProxy * QueueItem, bool Detail, bool OnlyLine);
TListItem * __fastcall InsertItemFor(TQueueItemProxy * QueueItem, int Index);
bool __fastcall UseDetailsLine(int ItemIndex, TQueueItemProxy * QueueItem);
};

View File

@@ -193,7 +193,6 @@ const coDisableSaveSettings = 0x040; // not used anymore
const coDoNotUsePresets = 0x080;
const coAllowRemoteTransfer = 0x100;
const coNoQueue = 0x200;
const coNoQueueIndividually = 0x400;
const coShortCutHint = 0x800;
const coAllFiles = 0x1000;
const cooDoNotShowAgain = 0x01;

View File

@@ -105,7 +105,7 @@ void __fastcall Upload(TTerminal * Terminal, TStrings * FileList, bool UseDefaul
DoCopyDialog(true, false, FileList, TargetDirectory, &CopyParam, Options,
CopyParamAttrs, NULL, NULL))
{
Terminal->CopyToRemote(FileList, TargetDirectory, &CopyParam, 0);
Terminal->CopyToRemote(FileList, TargetDirectory, &CopyParam, 0, NULL);
}
}
//---------------------------------------------------------------------------