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:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) ?
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -25,7 +25,6 @@ __published:
|
||||
TButton *CancelButton;
|
||||
TButton *LocalDirectoryBrowseButton;
|
||||
TCheckBox *QueueCheck2;
|
||||
TCheckBox *QueueIndividuallyCheck;
|
||||
TButton *HelpButton;
|
||||
TCheckBox *NeverShowAgainCheck;
|
||||
TButton *TransferSettingsButton;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -192,7 +192,7 @@ __published:
|
||||
TRadioButton *UpdatesAutoCheck;
|
||||
TCheckBox *ConfirmTransferringCheck;
|
||||
TGroupBox *UpdatesOptionsGroup;
|
||||
TCheckBox *QueueIndividuallyCheck;
|
||||
TCheckBox *QueueParallelCheck;
|
||||
TCheckBox *SessionReopenAutoIdleCheck;
|
||||
TCheckBox *RenameWholeNameCheck;
|
||||
TCheckBox *TreeOnLeftCheck;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user