Index: src/ch/FeedbackHandler.cpp
===================================================================
diff -u -rd12e49decb8a3df3e28f6786d38542390484ac07 -r4d20d0e58f37f06ac91287015b960308db54d47e
--- src/ch/FeedbackHandler.cpp (.../FeedbackHandler.cpp) (revision d12e49decb8a3df3e28f6786d38542390484ac07)
+++ src/ch/FeedbackHandler.cpp (.../FeedbackHandler.cpp) (revision 4d20d0e58f37f06ac91287015b960308db54d47e)
@@ -57,7 +57,7 @@
if(!pFeedbackParam)
return eResult_Unknown;
- FEEDBACK_ALREADYEXISTS* pData = (FEEDBACK_ALREADYEXISTS*)pFeedbackParam;
+ chcore::FEEDBACK_ALREADYEXISTS* pData = (chcore::FEEDBACK_ALREADYEXISTS*)pFeedbackParam;
CFeedbackReplaceDlg dlg(pData->spSrcFileInfo, pData->spDstFileInfo);
eFeedbackResult = (EFeedbackResult)dlg.DoModal();
bUseForAllItems = dlg.m_bAllItems;
@@ -70,7 +70,7 @@
if(!pFeedbackParam)
return eResult_Unknown;
- FEEDBACK_FILEERROR* pData = (FEEDBACK_FILEERROR*)pFeedbackParam;
+ chcore::FEEDBACK_FILEERROR* pData = (chcore::FEEDBACK_FILEERROR*)pFeedbackParam;
CFeedbackFileErrorDlg dlg(pData->pszSrcPath, pData->pszDstPath, pData->ulError);
eFeedbackResult = (EFeedbackResult)dlg.DoModal();
bUseForAllItems = dlg.m_bAllItems;
@@ -83,7 +83,7 @@
if(!pFeedbackParam)
return eResult_Unknown;
- FEEDBACK_NOTENOUGHSPACE* pData = (FEEDBACK_NOTENOUGHSPACE*)pFeedbackParam;
+ chcore::FEEDBACK_NOTENOUGHSPACE* pData = (chcore::FEEDBACK_NOTENOUGHSPACE*)pFeedbackParam;
CFeedbackNotEnoughSpaceDlg dlg(pData->ullRequiredSize, pData->pszSrcPath, pData->pszDstPath);
eFeedbackResult = (EFeedbackResult)dlg.DoModal();
bUseForAllItems = dlg.m_bAllItems;
Index: src/ch/FeedbackHandler.h
===================================================================
diff -u -r2aea3ad6f3c68be709ac65c70d9646eafe3b034c -r4d20d0e58f37f06ac91287015b960308db54d47e
--- src/ch/FeedbackHandler.h (.../FeedbackHandler.h) (revision 2aea3ad6f3c68be709ac65c70d9646eafe3b034c)
+++ src/ch/FeedbackHandler.h (.../FeedbackHandler.h) (revision 4d20d0e58f37f06ac91287015b960308db54d47e)
@@ -21,55 +21,8 @@
#include "../libchcore/FeedbackHandlerBase.h"
-struct FEEDBACK_ALREADYEXISTS
-{
- chcore::TFileInfoPtr spSrcFileInfo;
- chcore::TFileInfoPtr spDstFileInfo;
-};
-
-struct FEEDBACK_FILEERROR
-{
- const tchar_t* pszSrcPath;
- const tchar_t* pszDstPath;
- EFileError eFileError; // error type
- ulong_t ulError; // system error
-};
-
-struct FEEDBACK_NOTENOUGHSPACE
-{
- ull_t ullRequiredSize;
- const tchar_t* pszSrcPath;
- const tchar_t* pszDstPath;
-};
-
class CFeedbackHandler : public chcore::IFeedbackHandler
{
-public:
- enum EFeedbackType
- {
- eFT_Unknown = 0,
- // requests for use feedback
- eFT_FileAlreadyExists,
- eFT_FileError,
- eFT_NotEnoughSpace,
- // notifications
- eFT_OperationFinished, ///< Task has finished processing
- eFT_OperationError, ///< Error encountered while processing task
- eFT_LastType
- };
-
- enum EFeedbackResult
- {
- eResult_Unknown = 0,
- eResult_Overwrite,
- eResult_CopyRest,
- eResult_Skip,
- eResult_Cancel,
- eResult_Pause,
- eResult_Retry,
- eResult_Ignore
- };
-
protected:
CFeedbackHandler();
~CFeedbackHandler();
Fisheye: Tag 4d20d0e58f37f06ac91287015b960308db54d47e refers to a dead (removed) revision in file `src/ch/TSubTaskContext.h'.
Fisheye: No comparison available. Pass `N' to diff?
Fisheye: Tag 4d20d0e58f37f06ac91287015b960308db54d47e refers to a dead (removed) revision in file `src/ch/TSubTaskCopyMove.h'.
Fisheye: No comparison available. Pass `N' to diff?
Index: src/ch/ch.vc90.vcproj
===================================================================
diff -u -rfb4c4006dee5aaf815d08bc3e89312445b994307 -r4d20d0e58f37f06ac91287015b960308db54d47e
--- src/ch/ch.vc90.vcproj (.../ch.vc90.vcproj) (revision fb4c4006dee5aaf815d08bc3e89312445b994307)
+++ src/ch/ch.vc90.vcproj (.../ch.vc90.vcproj) (revision 4d20d0e58f37f06ac91287015b960308db54d47e)
@@ -434,14 +434,6 @@
RelativePath=".\task.h"
>
-
-
-
-
@@ -462,42 +454,6 @@
>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
RequestFeedback(CFeedbackHandler::eFT_OperationError, NULL);
SetTaskState(eTaskState_Error);
break;
- case TSubTaskBase::eSubResult_CancelRequest:
+ case chcore::TSubTaskBase::eSubResult_CancelRequest:
SetTaskState(eTaskState_Cancelled);
break;
- case TSubTaskBase::eSubResult_PauseRequest:
+ case chcore::TSubTaskBase::eSubResult_PauseRequest:
SetTaskState(eTaskState_Paused);
break;
- case TSubTaskBase::eSubResult_KillRequest:
+ case chcore::TSubTaskBase::eSubResult_KillRequest:
// the only operation
if(GetTaskState() == eTaskState_Waiting)
SetTaskState(eTaskState_Processing);
break;
- case TSubTaskBase::eSubResult_Continue:
+ case chcore::TSubTaskBase::eSubResult_Continue:
m_piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_OperationFinished, NULL);
SetTaskState(eTaskState_Finished);
break;
Index: src/ch/task.h
===================================================================
diff -u -rfb4c4006dee5aaf815d08bc3e89312445b994307 -r4d20d0e58f37f06ac91287015b960308db54d47e
--- src/ch/task.h (.../task.h) (revision fb4c4006dee5aaf815d08bc3e89312445b994307)
+++ src/ch/task.h (.../task.h) (revision 4d20d0e58f37f06ac91287015b960308db54d47e)
@@ -28,7 +28,7 @@
#include "../libchcore/TTaskDefinition.h"
#include "../libchcore/TTaskConfigTracker.h"
#include "../libchcore/TBasePathData.h"
-#include "TSubTaskBase.h"
+#include "../libchcore/TSubTaskBase.h"
#include "../libchcore/TTaskLocalStats.h"
#include "../libchcore/TTaskGlobalStats.h"
#include "../libchcore/TBasicProgressInfo.h"
@@ -174,7 +174,7 @@
/// Main function for the task processing thread
DWORD WINAPI ThrdProc();
- TSubTaskBase::ESubOperationResult CheckForWaitState();
+ chcore::TSubTaskBase::ESubOperationResult CheckForWaitState();
// m_nStatus
void SetStatusNL(UINT nStatus, UINT nMask);
Index: src/libchcore/ErrorCodes.h
===================================================================
diff -u -rfb4c4006dee5aaf815d08bc3e89312445b994307 -r4d20d0e58f37f06ac91287015b960308db54d47e
--- src/libchcore/ErrorCodes.h (.../ErrorCodes.h) (revision fb4c4006dee5aaf815d08bc3e89312445b994307)
+++ src/libchcore/ErrorCodes.h (.../ErrorCodes.h) (revision 4d20d0e58f37f06ac91287015b960308db54d47e)
@@ -69,6 +69,7 @@
// Filesystem errors
eErr_FixedDriveWithoutDriveLetter = 3000,
+ eErr_CannotGetFileInfo = 3001,
};
END_CHCORE_NAMESPACE
Index: src/libchcore/FeedbackHandlerBase.h
===================================================================
diff -u -rd5c3edd0d167db9b5d47d04248820fda49499a5e -r4d20d0e58f37f06ac91287015b960308db54d47e
--- src/libchcore/FeedbackHandlerBase.h (.../FeedbackHandlerBase.h) (revision d5c3edd0d167db9b5d47d04248820fda49499a5e)
+++ src/libchcore/FeedbackHandlerBase.h (.../FeedbackHandlerBase.h) (revision 4d20d0e58f37f06ac91287015b960308db54d47e)
@@ -21,12 +21,60 @@
#include "libchcore.h"
#include "../libicpf/interface.h"
+#include "../common/ErrorConstants.h"
BEGIN_CHCORE_NAMESPACE
+struct FEEDBACK_ALREADYEXISTS
+{
+ chcore::TFileInfoPtr spSrcFileInfo;
+ chcore::TFileInfoPtr spDstFileInfo;
+};
+
+struct FEEDBACK_FILEERROR
+{
+ const wchar_t* pszSrcPath;
+ const wchar_t* pszDstPath;
+ EFileError eFileError; // error type
+ ulong_t ulError; // system error
+};
+
+struct FEEDBACK_NOTENOUGHSPACE
+{
+ ull_t ullRequiredSize;
+ const wchar_t* pszSrcPath;
+ const wchar_t* pszDstPath;
+};
+
class IFeedbackHandler : public icpf::IInterface
{
public:
+ enum EFeedbackType
+ {
+ eFT_Unknown = 0,
+ // requests for use feedback
+ eFT_FileAlreadyExists,
+ eFT_FileError,
+ eFT_NotEnoughSpace,
+ // notifications
+ eFT_OperationFinished, ///< Task has finished processing
+ eFT_OperationError, ///< Error encountered while processing task
+ eFT_LastType
+ };
+
+ enum EFeedbackResult
+ {
+ eResult_Unknown = 0,
+ eResult_Overwrite,
+ eResult_CopyRest,
+ eResult_Skip,
+ eResult_Cancel,
+ eResult_Pause,
+ eResult_Retry,
+ eResult_Ignore
+ };
+
+public:
virtual ull_t RequestFeedback(ull_t ullFeedbackID, ptr_t pFeedbackParam) = 0;
};
Index: src/libchcore/TSubTaskBase.cpp
===================================================================
diff -u
--- src/libchcore/TSubTaskBase.cpp (revision 0)
+++ src/libchcore/TSubTaskBase.cpp (revision 4d20d0e58f37f06ac91287015b960308db54d47e)
@@ -0,0 +1,113 @@
+// ============================================================================
+// Copyright (C) 2001-2009 by Jozef Starosczyk
+// ixen@copyhandler.com
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Library General Public License
+// (version 2) as published by the Free Software Foundation;
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// ============================================================================
+/// @file TSubTaskBase.cpp
+/// @date 2010/09/19
+/// @brief Contains implementation of some common subtask elements.
+// ============================================================================
+#include "stdafx.h"
+#include "TSubTaskBase.h"
+#include "TBasePathData.h"
+#include "TLocalFilesystem.h"
+#include "TSubTaskContext.h"
+#include "TTaskDefinition.h"
+#include "TTaskConfiguration.h"
+#include
+
+BEGIN_CHCORE_NAMESPACE
+
+///////////////////////////////////////////////////////////////////////////
+// TSubTaskBase
+
+TSubTaskBase::TSubTaskBase(TSubTaskContext& rContext) :
+m_rContext(rContext)
+{
+}
+
+TSubTaskBase::~TSubTaskBase()
+{
+}
+
+TSmartPath TSubTaskBase::CalculateDestinationPath(const TFileInfoPtr& spFileInfo, TSmartPath pathDst, int iFlags) const
+{
+ const TBasePathDataContainer& rSourcePathsInfo = GetContext().GetBasePathDataContainer();
+
+ if(!spFileInfo)
+ THROW_CORE_EXCEPTION(eErr_InvalidArgument);
+
+ // iFlags: bit 0-ignore folders; bit 1-force creating directories
+ if(iFlags & 0x02)
+ {
+ // force create directories
+ TSmartPath pathCombined = pathDst + spFileInfo->GetFullFilePath().GetFileDir();
+
+ // force create directory
+ TLocalFilesystem::CreateDirectory(pathCombined, true);
+
+ return pathCombined + spFileInfo->GetFullFilePath().GetFileName();
+ }
+ else
+ {
+ size_t stSrcIndex = spFileInfo->GetSrcIndex();
+
+ if (!(iFlags & 0x01) && stSrcIndex != std::numeric_limits::max())
+ {
+ // generate new dest name
+ if(!rSourcePathsInfo.GetAt(stSrcIndex)->IsDestinationPathSet())
+ {
+ TSmartPath pathSubst = FindFreeSubstituteName(spFileInfo->GetFullFilePath(), pathDst);
+ rSourcePathsInfo.GetAt(stSrcIndex)->SetDestinationPath(pathSubst);
+ }
+
+ return pathDst + rSourcePathsInfo.GetAt(stSrcIndex)->GetDestinationPath() + spFileInfo->GetFilePath();
+ }
+ else
+ return pathDst + spFileInfo->GetFullFilePath().GetFileName();
+ }
+}
+
+// finds another name for a copy of src file(folder) in dest location
+TSmartPath TSubTaskBase::FindFreeSubstituteName(TSmartPath pathSrcPath, TSmartPath pathDstPath) const
+{
+ const TTaskDefinition& rTaskDefinition = GetContext().GetTaskDefinition();
+
+ // get the name from src path
+ pathSrcPath.StripSeparatorAtEnd();
+
+ TSmartPath pathFilename = pathSrcPath.GetFileName();
+
+ // set the dest path
+ TString strCheckPath = GetTaskPropValue(rTaskDefinition.GetConfiguration());
+ strCheckPath.Replace(_T("%name"), pathFilename.ToString());
+ TSmartPath pathCheckPath(PathFromString(strCheckPath));
+
+ // when adding to strDstPath check if the path already exists - if so - try again
+ int iCounter = 1;
+ TString strFmt = GetTaskPropValue(rTaskDefinition.GetConfiguration());
+ while(TLocalFilesystem::PathExist(pathDstPath + pathCheckPath))
+ {
+ strCheckPath = strFmt;
+ strCheckPath.Replace(_t("%name"), pathFilename.ToString());
+ strCheckPath.Replace(_t("%count"), boost::lexical_cast(++iCounter).c_str());
+ pathCheckPath.FromString(strCheckPath);
+ }
+
+ return pathCheckPath;
+}
+
+END_CHCORE_NAMESPACE
Fisheye: tag 2aea3ad6f3c68be709ac65c70d9646eafe3b034c is not in file src/libchcore/TSubTaskBase.h
Fisheye: Tag 4d20d0e58f37f06ac91287015b960308db54d47e refers to a dead (removed) revision in file `src/ch/TSubTaskBase.h'.
Fisheye: No comparison available. Pass `N' to diff?
Fisheye: tag fb4c4006dee5aaf815d08bc3e89312445b994307 is not in file src/libchcore/TSubTaskContext.cpp
Fisheye: Tag 4d20d0e58f37f06ac91287015b960308db54d47e refers to a dead (removed) revision in file `src/ch/TSubTaskContext.cpp'.
Fisheye: No comparison available. Pass `N' to diff?
Index: src/libchcore/TSubTaskContext.h
===================================================================
diff -u
--- src/libchcore/TSubTaskContext.h (revision 0)
+++ src/libchcore/TSubTaskContext.h (revision 4d20d0e58f37f06ac91287015b960308db54d47e)
@@ -0,0 +1,121 @@
+// ============================================================================
+// Copyright (C) 2001-2009 by Jozef Starosczyk
+// ixen@copyhandler.com
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Library General Public License
+// (version 2) as published by the Free Software Foundation;
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// ============================================================================
+/// @file TSubTaskContext.h
+/// @date 2010/09/19
+/// @brief Contains declaration of subtask context class.
+// ============================================================================
+#ifndef __TSUBTASKCONTEXT_H__
+#define __TSUBTASKCONTEXT_H__
+
+#include "libchcore.h"
+
+namespace icpf
+{
+ class log_file;
+}
+
+BEGIN_CHCORE_NAMESPACE
+
+class IFeedbackHandler;
+class TTaskDefinition;
+class TWorkerThreadController;
+class TBasePathDataContainer;
+class TTaskConfigTracker;
+class TLocalFilesystem;
+class TTaskLocalStats;
+class TTaskBasicProgressInfo;
+class TFileInfoArray;
+
+///////////////////////////////////////////////////////////////////////////
+// TSubTaskContext
+
+class LIBCHCORE_API TSubTaskContext
+{
+public:
+ TSubTaskContext(TTaskDefinition& rTaskDefinition, TBasePathDataContainer& rBasePathDataContainer, TFileInfoArray& rFilesCache, TTaskLocalStats& rTaskLocalStats,
+ TTaskBasicProgressInfo& rTaskBasicProgressInfo, TTaskConfigTracker& rCfgTracker, icpf::log_file& rLog,
+ IFeedbackHandler* piFeedbackHandler, TWorkerThreadController& rThreadController, TLocalFilesystem& rfsLocal);
+ ~TSubTaskContext();
+
+ TTaskDefinition& GetTaskDefinition() { return m_rTaskDefinition; }
+ const TTaskDefinition& GetTaskDefinition() const { return m_rTaskDefinition; }
+
+ TBasePathDataContainer& GetBasePathDataContainer() { return m_rBasePathDataContainer; }
+ const TBasePathDataContainer& GetBasePathDataContainer() const { return m_rBasePathDataContainer; }
+
+ TFileInfoArray& GetFilesCache() { return m_rFilesCache; }
+ const TFileInfoArray& GetFilesCache() const { return m_rFilesCache; }
+
+ TTaskLocalStats& GetTaskLocalStats() { return m_rTaskLocalStats; }
+ const TTaskLocalStats& GetTaskLocalStats() const { return m_rTaskLocalStats; }
+
+ TTaskBasicProgressInfo& GetTaskBasicProgressInfo() { return m_rTaskBasicProgressInfo; }
+ const TTaskBasicProgressInfo& GetTaskBasicProgressInfo() const { return m_rTaskBasicProgressInfo; }
+
+ TTaskConfigTracker& GetCfgTracker() { return m_rCfgTracker; }
+ const TTaskConfigTracker& GetCfgTracker() const { return m_rCfgTracker; }
+
+ icpf::log_file& GetLog() { return m_rLog; }
+ const icpf::log_file& GetLog() const { return m_rLog; }
+
+ IFeedbackHandler* GetFeedbackHandler() { return m_piFeedbackHandler; }
+ const IFeedbackHandler* GetFeedbackHandler() const { return m_piFeedbackHandler; }
+
+ TWorkerThreadController& GetThreadController() { return m_rThreadController; }
+ const TWorkerThreadController& GetThreadController() const { return m_rThreadController; }
+
+ TLocalFilesystem& GetLocalFilesystem() { return m_rfsLocal; }
+ const TLocalFilesystem& GetLocalFilesystem() const { return m_rfsLocal; }
+
+private:
+ TSubTaskContext(const TSubTaskContext& rSrc);
+ TSubTaskContext& operator=(const TSubTaskContext& rSrc);
+
+private:
+ TTaskDefinition& m_rTaskDefinition;
+
+ // information about input paths
+ TBasePathDataContainer& m_rBasePathDataContainer;
+
+ // data on which to operate
+ TFileInfoArray& m_rFilesCache;
+
+ // local stats for task
+ TTaskLocalStats& m_rTaskLocalStats;
+ TTaskBasicProgressInfo& m_rTaskBasicProgressInfo;
+
+ // configuration changes tracking
+ TTaskConfigTracker& m_rCfgTracker;
+
+ // local filesystem access functions
+ TLocalFilesystem& m_rfsLocal;
+
+ // additional data
+ icpf::log_file& m_rLog;
+
+ // feedback handling
+ IFeedbackHandler* m_piFeedbackHandler;
+
+ // thread control
+ TWorkerThreadController& m_rThreadController;
+};
+
+END_CHCORE_NAMESPACE
+
+#endif // __TSUBTASKCONTEXT_H__
Index: src/libchcore/TSubTaskCopyMove.cpp
===================================================================
diff -u
--- src/libchcore/TSubTaskCopyMove.cpp (revision 0)
+++ src/libchcore/TSubTaskCopyMove.cpp (revision 4d20d0e58f37f06ac91287015b960308db54d47e)
@@ -0,0 +1,1123 @@
+// ============================================================================
+// Copyright (C) 2001-2009 by Jozef Starosczyk
+// ixen@copyhandler.com
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Library General Public License
+// (version 2) as published by the Free Software Foundation;
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// ============================================================================
+/// @file TSubTaskCopyMove.cpp
+/// @date 2010/09/19
+/// @brief Contains implementations of classes responsible for copy and move sub-operation.
+// ============================================================================
+#include "stdafx.h"
+#include "TSubTaskCopyMove.h"
+#include "TSubTaskContext.h"
+#include "TTaskConfiguration.h"
+#include "TTaskDefinition.h"
+#include "TLocalFilesystem.h"
+#include "DataBuffer.h"
+#include "../libicpf/log.h"
+#include "TTaskLocalStats.h"
+#include "TBasicProgressInfo.h"
+#include "TTaskConfigTracker.h"
+#include "TWorkerThreadController.h"
+#include "FeedbackHandlerBase.h"
+#include
+#include "TBasePathData.h"
+#include
+
+BEGIN_CHCORE_NAMESPACE
+
+// assume max sectors of 4kB (for rounding)
+#define MAXSECTORSIZE 4096
+
+struct CUSTOM_COPY_PARAMS
+{
+ TFileInfoPtr spSrcFile; // CFileInfo - src file
+ TSmartPath pathDstFile; // dest path with filename
+
+ TDataBuffer dbBuffer; // buffer handling
+ bool bOnlyCreate; // flag from configuration - skips real copying - only create
+ bool bProcessed; // has the element been processed ? (false if skipped)
+};
+
+TSubTaskCopyMove::TSubTaskCopyMove(TSubTaskContext& tSubTaskContext) :
+ TSubTaskBase(tSubTaskContext)
+{
+}
+
+TSubTaskBase::ESubOperationResult TSubTaskCopyMove::Exec()
+{
+ icpf::log_file& rLog = GetContext().GetLog();
+ TFileInfoArray& rFilesCache = GetContext().GetFilesCache();
+ TTaskDefinition& rTaskDefinition = GetContext().GetTaskDefinition();
+ TTaskConfigTracker& rCfgTracker = GetContext().GetCfgTracker();
+ TTaskBasicProgressInfo& rBasicProgressInfo = GetContext().GetTaskBasicProgressInfo();
+ TWorkerThreadController& rThreadController = GetContext().GetThreadController();
+ IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler();
+ TTaskLocalStats& rLocalStats = GetContext().GetTaskLocalStats();
+
+ BOOST_ASSERT(piFeedbackHandler != NULL);
+ if(piFeedbackHandler == NULL)
+ return eSubResult_Error;
+
+ // log
+ rLog.logi(_T("Processing files/folders (ProcessFiles)"));
+
+ // count how much has been done (updates also a member in TSubTaskCopyMoveArray)
+ rLocalStats.SetProcessedSize(rFilesCache.CalculatePartialSize(rBasicProgressInfo.GetCurrentIndex()));
+
+ // now it's time to check if there is enough space on destination device
+ TSubTaskBase::ESubOperationResult eResult = CheckForFreeSpaceFB();
+ if(eResult != TSubTaskBase::eSubResult_Continue)
+ return eResult;
+
+ // begin at index which wasn't processed previously
+ size_t stSize = rFilesCache.GetSize();
+ bool bIgnoreFolders = GetTaskPropValue(rTaskDefinition.GetConfiguration());
+ bool bForceDirectories = GetTaskPropValue(rTaskDefinition.GetConfiguration());
+
+ // create a buffer of size m_nBufferSize
+ CUSTOM_COPY_PARAMS ccp;
+ ccp.bProcessed = false;
+ ccp.bOnlyCreate = GetTaskPropValue(rTaskDefinition.GetConfiguration());
+
+ // remove changes in buffer sizes to avoid re-creation later
+ rCfgTracker.RemoveModificationSet(TOptionsSet() % eTO_DefaultBufferSize % eTO_OneDiskBufferSize % eTO_TwoDisksBufferSize % eTO_CDBufferSize % eTO_LANBufferSize % eTO_UseOnlyDefaultBuffer);
+
+ TBufferSizes bs;
+ bs.SetOnlyDefault(GetTaskPropValue(rTaskDefinition.GetConfiguration()));
+ bs.SetDefaultSize(GetTaskPropValue(rTaskDefinition.GetConfiguration()));
+ bs.SetOneDiskSize(GetTaskPropValue(rTaskDefinition.GetConfiguration()));
+ bs.SetTwoDisksSize(GetTaskPropValue(rTaskDefinition.GetConfiguration()));
+ bs.SetCDSize(GetTaskPropValue(rTaskDefinition.GetConfiguration()));
+ bs.SetLANSize(GetTaskPropValue(rTaskDefinition.GetConfiguration()));
+
+ ccp.dbBuffer.Create(bs);
+
+ // helpers
+ DWORD dwLastError = 0;
+
+ // log
+ const TBufferSizes& rbs = ccp.dbBuffer.GetSizes();
+
+ TString strFormat;
+ strFormat = _T("Processing files/folders (ProcessFiles):\r\n\tOnlyCreate: %create\r\n\tBufferSize: [Def:%defsize, One:%onesize, Two:%twosize, CD:%cdsize, LAN:%lansize]\r\n\tFiles/folders count: %filecount\r\n\tIgnore Folders: %ignorefolders\r\n\tDest path: %dstpath\r\n\tCurrent index (0-based): %currindex");
+ strFormat.Replace(_T("%create"), boost::lexical_cast(ccp.bOnlyCreate).c_str());
+ strFormat.Replace(_T("%defsize"), boost::lexical_cast(rbs.GetDefaultSize()).c_str());
+ strFormat.Replace(_T("%onesize"), boost::lexical_cast(rbs.GetOneDiskSize()).c_str());
+ strFormat.Replace(_T("%twosize"), boost::lexical_cast(rbs.GetTwoDisksSize()).c_str());
+ strFormat.Replace(_T("%cdsize"), boost::lexical_cast(rbs.GetCDSize()).c_str());
+ strFormat.Replace(_T("%lansize"), boost::lexical_cast(rbs.GetLANSize()).c_str());
+ strFormat.Replace(_T("%filecount"), boost::lexical_cast(stSize).c_str());
+ strFormat.Replace(_T("%ignorefolders"), boost::lexical_cast(bIgnoreFolders).c_str());
+ strFormat.Replace(_T("%dstpath"), rTaskDefinition.GetDestinationPath().ToString());
+ strFormat.Replace(_T("%currindex"), boost::lexical_cast(rBasicProgressInfo.GetCurrentIndex()).c_str());
+
+ rLog.logi(strFormat);
+
+ for(size_t stIndex = rBasicProgressInfo.GetCurrentIndex(); stIndex < stSize; stIndex++)
+ {
+ // should we kill ?
+ if(rThreadController.KillRequested())
+ {
+ // log
+ rLog.logi(_T("Kill request while processing file in ProcessFiles"));
+ return TSubTaskBase::eSubResult_KillRequest;
+ }
+
+ // update m_stNextIndex, getting current CFileInfo
+ TFileInfoPtr spFileInfo = rFilesCache.GetAt(rBasicProgressInfo.GetCurrentIndex());
+
+ // set dest path with filename
+ ccp.pathDstFile = CalculateDestinationPath(spFileInfo, rTaskDefinition.GetDestinationPath(), ((int)bForceDirectories) << 1 | (int)bIgnoreFolders);
+
+ // are the files/folders lie on the same partition ?
+ wchar_t wchDestinationDrive = rTaskDefinition.GetDestinationPath().GetDriveLetter();
+ bool bMove = rTaskDefinition.GetOperationType() == eOperation_Move;
+ TSmartPath pathCurrent = spFileInfo->GetFullFilePath();
+ if(bMove && wchDestinationDrive != L'\0' && wchDestinationDrive == pathCurrent.GetDriveLetter() && GetMove(spFileInfo))
+ {
+ bool bRetry = true;
+ if(bRetry && !TLocalFilesystem::FastMove(pathCurrent, ccp.pathDstFile))
+ {
+ dwLastError=GetLastError();
+ //log
+ strFormat = _T("Error %errno while calling MoveFile %srcpath -> %dstpath (ProcessFiles)");
+ strFormat.Replace(_T("%errno"), boost::lexical_cast(dwLastError).c_str());
+ strFormat.Replace(_T("%srcpath"), spFileInfo->GetFullFilePath().ToString());
+ strFormat.Replace(_T("%dstpath"), ccp.pathDstFile.ToString());
+ rLog.loge(strFormat);
+
+ FEEDBACK_FILEERROR ferr = { spFileInfo->GetFullFilePath().ToString(), ccp.pathDstFile.ToString(), eFastMoveError, dwLastError };
+ IFeedbackHandler::EFeedbackResult frResult = (IFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(IFeedbackHandler::eFT_FileError, &ferr);
+ switch(frResult)
+ {
+ case IFeedbackHandler::eResult_Cancel:
+ return TSubTaskBase::eSubResult_CancelRequest;
+
+ case IFeedbackHandler::eResult_Retry:
+ continue;
+
+ case IFeedbackHandler::eResult_Pause:
+ return TSubTaskBase::eSubResult_PauseRequest;
+
+ case IFeedbackHandler::eResult_Skip:
+ bRetry = false;
+ break; // just do nothing
+ default:
+ BOOST_ASSERT(FALSE); // unknown result
+ THROW_CORE_EXCEPTION(eErr_UnhandledCase);
+ }
+ }
+ else
+ spFileInfo->SetFlags(FIF_PROCESSED, FIF_PROCESSED);
+ }
+ else
+ {
+ // if folder - create it
+ if(spFileInfo->IsDirectory())
+ {
+ bool bRetry = true;
+ if(bRetry && !TLocalFilesystem::CreateDirectory(ccp.pathDstFile, false) && (dwLastError=GetLastError()) != ERROR_ALREADY_EXISTS )
+ {
+ // log
+ strFormat = _T("Error %errno while calling CreateDirectory %path (ProcessFiles)");
+ strFormat.Replace(_T("%errno"), boost::lexical_cast(dwLastError).c_str());
+ strFormat.Replace(_T("%path"), ccp.pathDstFile.ToString());
+ rLog.loge(strFormat);
+
+ FEEDBACK_FILEERROR ferr = { ccp.pathDstFile.ToString(), NULL, eCreateError, dwLastError };
+ IFeedbackHandler::EFeedbackResult frResult = (IFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(IFeedbackHandler::eFT_FileError, &ferr);
+ switch(frResult)
+ {
+ case IFeedbackHandler::eResult_Cancel:
+ return TSubTaskBase::eSubResult_CancelRequest;
+
+ case IFeedbackHandler::eResult_Retry:
+ continue;
+
+ case IFeedbackHandler::eResult_Pause:
+ return TSubTaskBase::eSubResult_PauseRequest;
+
+ case IFeedbackHandler::eResult_Skip:
+ bRetry = false;
+ break; // just do nothing
+ default:
+ BOOST_ASSERT(FALSE); // unknown result
+ THROW_CORE_EXCEPTION(eErr_UnhandledCase);
+ }
+ }
+
+ rLocalStats.IncreaseProcessedSize(spFileInfo->GetLength64());
+ spFileInfo->SetFlags(FIF_PROCESSED, FIF_PROCESSED);
+ }
+ else
+ {
+ // start copying/moving file
+ ccp.spSrcFile = spFileInfo;
+ ccp.bProcessed = false;
+
+ // copy data
+ TSubTaskBase::ESubOperationResult eResult = CustomCopyFileFB(&ccp);
+ if(eResult != TSubTaskBase::eSubResult_Continue)
+ return eResult;
+
+ spFileInfo->SetFlags(ccp.bProcessed ? FIF_PROCESSED : 0, FIF_PROCESSED);
+
+ // if moving - delete file (only if config flag is set)
+ if(bMove && spFileInfo->GetFlags() & FIF_PROCESSED && !GetTaskPropValue(rTaskDefinition.GetConfiguration()))
+ {
+ if(!GetTaskPropValue(rTaskDefinition.GetConfiguration()))
+ TLocalFilesystem::SetAttributes(spFileInfo->GetFullFilePath(), FILE_ATTRIBUTE_NORMAL);
+ TLocalFilesystem::DeleteFile(spFileInfo->GetFullFilePath()); // there will be another try later, so I don't check
+ // if succeeded
+ }
+ }
+
+ // set a time
+ if(GetTaskPropValue(rTaskDefinition.GetConfiguration()))
+ TLocalFilesystem::SetFileDirectoryTime(ccp.pathDstFile, spFileInfo->GetCreationTime(), spFileInfo->GetLastAccessTime(), spFileInfo->GetLastWriteTime()); // no error checking (but most probably it should be checked)
+
+ // attributes
+ if(GetTaskPropValue(rTaskDefinition.GetConfiguration()))
+ TLocalFilesystem::SetAttributes(ccp.pathDstFile, spFileInfo->GetAttributes()); // as above
+ }
+
+ rBasicProgressInfo.SetCurrentIndex(stIndex + 1);
+ }
+
+ // delete buffer - it's not needed
+ ccp.dbBuffer.Delete();
+
+ // to look better (as 100%) - increase current index by 1
+ rBasicProgressInfo.SetCurrentIndex(stSize);
+
+ // log
+ rLog.logi(_T("Finished processing in ProcessFiles"));
+
+ return TSubTaskBase::eSubResult_Continue;
+}
+
+bool TSubTaskCopyMove::GetMove(const TFileInfoPtr& spFileInfo)
+{
+ if(!spFileInfo)
+ THROW_CORE_EXCEPTION(eErr_InvalidArgument);
+ if(spFileInfo->GetSrcIndex() == std::numeric_limits::max())
+ THROW_CORE_EXCEPTION(eErr_InvalidArgument);
+
+ // check if this information has already been stored
+ size_t stBaseIndex = spFileInfo->GetSrcIndex();
+ if(stBaseIndex >= GetContext().GetBasePathDataContainer().GetCount())
+ THROW_CORE_EXCEPTION(eErr_BoundsExceeded);
+
+ TBasePathDataPtr spPathData = GetContext().GetBasePathDataContainer().GetAt(stBaseIndex);
+ return spPathData->GetMove();
+}
+
+int TSubTaskCopyMove::GetBufferIndex(const TFileInfoPtr& spFileInfo)
+{
+ if(!spFileInfo)
+ THROW_CORE_EXCEPTION(eErr_InvalidArgument);
+
+ TSmartPath pathSource = spFileInfo->GetFullFilePath();
+ TSmartPath pathDestination = GetContext().GetTaskDefinition().GetDestinationPath();
+
+ TLocalFilesystem::EPathsRelation eRelation = GetContext().GetLocalFilesystem().GetPathsRelation(pathSource, pathDestination);
+ switch(eRelation)
+ {
+ case TLocalFilesystem::eRelation_Network:
+ return TBufferSizes::eBuffer_LAN;
+
+ case TLocalFilesystem::eRelation_CDRom:
+ return TBufferSizes::eBuffer_CD;
+
+ case TLocalFilesystem::eRelation_TwoPhysicalDisks:
+ return TBufferSizes::eBuffer_TwoDisks;
+
+ case TLocalFilesystem::eRelation_SinglePhysicalDisk:
+ return TBufferSizes::eBuffer_OneDisk;
+
+ //case eRelation_Other:
+ default:
+ return TBufferSizes::eBuffer_Default;
+ }
+}
+
+TSubTaskBase::ESubOperationResult TSubTaskCopyMove::CustomCopyFileFB(CUSTOM_COPY_PARAMS* pData)
+{
+ TTaskDefinition& rTaskDefinition = GetContext().GetTaskDefinition();
+ TTaskBasicProgressInfo& rBasicProgressInfo = GetContext().GetTaskBasicProgressInfo();
+ TWorkerThreadController& rThreadController = GetContext().GetThreadController();
+ TTaskLocalStats& rLocalStats = GetContext().GetTaskLocalStats();
+ icpf::log_file& rLog = GetContext().GetLog();
+ TTaskConfigTracker& rCfgTracker = GetContext().GetCfgTracker();
+
+ TLocalFilesystemFile fileSrc = TLocalFilesystem::CreateFileObject();
+ TLocalFilesystemFile fileDst = TLocalFilesystem::CreateFileObject();
+
+ TString strFormat;
+ TSubTaskBase::ESubOperationResult eResult = TSubTaskBase::eSubResult_Continue;
+ bool bSkip = false;
+
+ // calculate if we want to disable buffering for file transfer
+ // NOTE: we are using here the file size read when scanning directories for files; it might be
+ // outdated at this point, but at present we don't want to re-read file size since it
+ // will cost additional disk access
+ bool bNoBuffer = (GetTaskPropValue(rTaskDefinition.GetConfiguration()) &&
+ pData->spSrcFile->GetLength64() >= GetTaskPropValue(rTaskDefinition.GetConfiguration()));
+
+ // first open the source file and handle any failures
+ eResult = OpenSourceFileFB(fileSrc, pData->spSrcFile->GetFullFilePath(), bNoBuffer);
+ if(eResult != TSubTaskBase::eSubResult_Continue)
+ return eResult;
+ else if(!fileSrc.IsOpen())
+ {
+ // invalid handle = operation skipped by user
+ rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize());
+ pData->bProcessed = false;
+ return TSubTaskBase::eSubResult_Continue;
+ }
+
+ // change attributes of a dest file
+ // NOTE: probably should be removed from here and report problems with read-only files
+ // directly to the user (as feedback request)
+ if(!GetTaskPropValue(rTaskDefinition.GetConfiguration()))
+ SetFileAttributes(pData->pathDstFile.ToString(), FILE_ATTRIBUTE_NORMAL);
+
+ // open destination file, handle the failures and possibly existence of the destination file
+ unsigned long long ullSeekTo = 0;
+ bool bDstFileFreshlyCreated = false;
+
+ if(rBasicProgressInfo.GetCurrentFileProcessedSize() == 0)
+ {
+ // open destination file for case, when we start operation on this file (i.e. it is not resume of the
+ // old operation)
+ eResult = OpenDestinationFileFB(fileDst, pData->pathDstFile, bNoBuffer, pData->spSrcFile, ullSeekTo, bDstFileFreshlyCreated);
+ if(eResult != TSubTaskBase::eSubResult_Continue)
+ return eResult;
+ else if(!fileDst.IsOpen())
+ {
+ rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize());
+ pData->bProcessed = false;
+ return TSubTaskBase::eSubResult_Continue;
+ }
+ }
+ else
+ {
+ // we are resuming previous operation
+ eResult = OpenExistingDestinationFileFB(fileDst, pData->pathDstFile, bNoBuffer);
+ if(eResult != TSubTaskBase::eSubResult_Continue)
+ return eResult;
+ else if(!fileDst.IsOpen())
+ {
+ rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize());
+ pData->bProcessed = false;
+ return TSubTaskBase::eSubResult_Continue;
+ }
+
+ ullSeekTo = rBasicProgressInfo.GetCurrentFileProcessedSize();
+ }
+
+ if(!pData->bOnlyCreate)
+ {
+ // seek to the position where copying will start
+ if(ullSeekTo != 0) // src and dst files exists, requested resume at the specified index
+ {
+ // try to move file pointers to the end
+ ULONGLONG ullMove = (bNoBuffer ? ROUNDDOWN(ullSeekTo, MAXSECTORSIZE) : ullSeekTo);
+
+ eResult = SetFilePointerFB(fileSrc, ullMove, pData->spSrcFile->GetFullFilePath(), bSkip);
+ if(eResult != TSubTaskBase::eSubResult_Continue)
+ return eResult;
+ else if(bSkip)
+ {
+ rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize());
+ pData->bProcessed = false;
+ return TSubTaskBase::eSubResult_Continue;
+ }
+
+ eResult = SetFilePointerFB(fileDst, ullMove, pData->pathDstFile, bSkip);
+ if(eResult != TSubTaskBase::eSubResult_Continue)
+ return eResult;
+ else if(bSkip)
+ {
+ // with either first or second seek we got 'skip' answer...
+ rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize());
+ pData->bProcessed = false;
+ return TSubTaskBase::eSubResult_Continue;
+ }
+
+ rBasicProgressInfo.IncreaseCurrentFileProcessedSize(ullMove);
+ rLocalStats.IncreaseProcessedSize(ullMove);
+ }
+
+ // if the destination file already exists - truncate it to the current file position
+ if(!bDstFileFreshlyCreated)
+ {
+ // if destination file was opened (as opposed to newly created)
+ eResult = SetEndOfFileFB(fileDst, pData->pathDstFile, bSkip);
+ if(eResult != TSubTaskBase::eSubResult_Continue)
+ return eResult;
+ else if(bSkip)
+ {
+ pData->bProcessed = false;
+ return TSubTaskBase::eSubResult_Continue;
+ }
+ }
+
+ // copying
+ unsigned long ulToRead = 0;
+ unsigned long ulRead = 0;
+ unsigned long ulWritten = 0;
+ int iBufferIndex = 0;
+ bool bLastPart = false;
+
+ do
+ {
+ // kill flag checks
+ if(rThreadController.KillRequested())
+ {
+ // log
+ strFormat = _T("Kill request while main copying file %srcpath -> %dstpath");
+ strFormat.Replace(_T("%srcpath"), pData->spSrcFile->GetFullFilePath().ToString());
+ strFormat.Replace(_T("%dstpath"), pData->pathDstFile.ToString());
+ rLog.logi(strFormat);
+ return TSubTaskBase::eSubResult_KillRequest;
+ }
+
+ // recreate buffer if needed
+ if(rCfgTracker.IsModified() && rCfgTracker.IsModified(TOptionsSet() % eTO_DefaultBufferSize % eTO_OneDiskBufferSize % eTO_TwoDisksBufferSize % eTO_CDBufferSize % eTO_LANBufferSize % eTO_UseOnlyDefaultBuffer, true))
+ {
+ TBufferSizes bs;
+ bs.SetOnlyDefault(GetTaskPropValue(rTaskDefinition.GetConfiguration()));
+ bs.SetDefaultSize(GetTaskPropValue(rTaskDefinition.GetConfiguration()));
+ bs.SetOneDiskSize(GetTaskPropValue(rTaskDefinition.GetConfiguration()));
+ bs.SetTwoDisksSize(GetTaskPropValue(rTaskDefinition.GetConfiguration()));
+ bs.SetCDSize(GetTaskPropValue(rTaskDefinition.GetConfiguration()));
+ bs.SetLANSize(GetTaskPropValue(rTaskDefinition.GetConfiguration()));
+
+ // log
+ const TBufferSizes& rbs1 = pData->dbBuffer.GetSizes();
+
+ strFormat = _T("Changing buffer size from [Def:%defsize, One:%onesize, Two:%twosize, CD:%cdsize, LAN:%lansize] to [Def:%defsize2, One:%onesize2, Two:%twosize2, CD:%cdsize2, LAN:%lansize2] wile copying %srcfile -> %dstfile (CustomCopyFileFB)");
+
+ strFormat.Replace(_T("%defsize"), boost::lexical_cast(rbs1.GetDefaultSize()).c_str());
+ strFormat.Replace(_T("%onesize"), boost::lexical_cast(rbs1.GetOneDiskSize()).c_str());
+ strFormat.Replace(_T("%twosize"), boost::lexical_cast(rbs1.GetTwoDisksSize()).c_str());
+ strFormat.Replace(_T("%cdsize"), boost::lexical_cast(rbs1.GetCDSize()).c_str());
+ strFormat.Replace(_T("%lansize"), boost::lexical_cast(rbs1.GetLANSize()).c_str());
+ strFormat.Replace(_T("%defsize2"), boost::lexical_cast(bs.GetDefaultSize()).c_str());
+ strFormat.Replace(_T("%onesize2"), boost::lexical_cast(bs.GetOneDiskSize()).c_str());
+ strFormat.Replace(_T("%twosize2"), boost::lexical_cast(bs.GetTwoDisksSize()).c_str());
+ strFormat.Replace(_T("%cdsize2"), boost::lexical_cast(bs.GetCDSize()).c_str());
+ strFormat.Replace(_T("%lansize2"), boost::lexical_cast(bs.GetLANSize()).c_str());
+ strFormat.Replace(_T("%srcfile"), pData->spSrcFile->GetFullFilePath().ToString());
+ strFormat.Replace(_T("%dstfile"), pData->pathDstFile.ToString());
+
+ rLog.logi(strFormat);
+ pData->dbBuffer.Create(bs);
+ }
+
+ // establish count of data to read
+ if(GetTaskPropValue(rTaskDefinition.GetConfiguration()))
+ iBufferIndex = TBufferSizes::eBuffer_Default;
+ else
+ iBufferIndex = GetBufferIndex(pData->spSrcFile);
+ rLocalStats.SetCurrentBufferIndex(iBufferIndex);
+
+ ulToRead = bNoBuffer ? ROUNDUP(pData->dbBuffer.GetSizes().GetSizeByType((TBufferSizes::EBufferType)iBufferIndex), MAXSECTORSIZE) : pData->dbBuffer.GetSizes().GetSizeByType((TBufferSizes::EBufferType)iBufferIndex);
+
+ // read data from file to buffer
+ eResult = ReadFileFB(fileSrc, pData->dbBuffer, ulToRead, ulRead, pData->spSrcFile->GetFullFilePath(), bSkip);
+ if(eResult != TSubTaskBase::eSubResult_Continue)
+ return eResult;
+ else if(bSkip)
+ {
+ rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize());
+ pData->bProcessed = false;
+ return TSubTaskBase::eSubResult_Continue;
+ }
+
+ if(ulRead > 0)
+ {
+ // determine if this is the last chunk of data we could get from the source file (EOF condition)
+ bLastPart = (ulToRead != ulRead);
+
+ // handle not aligned part at the end of file when no buffering is enabled
+ if(bNoBuffer && bLastPart)
+ {
+ // count of data read from the file is less than requested - we're at the end of source file
+ // and this is the operation with system buffering turned off
+
+ // write as much as possible to the destination file with no buffering
+ // NOTE: as an alternative, we could write more data to the destination file and then truncate the file
+ unsigned long ulDataToWrite = ROUNDDOWN(ulRead, MAXSECTORSIZE);
+ if(ulDataToWrite > 0)
+ {
+ eResult = WriteFileFB(fileDst, pData->dbBuffer, ulDataToWrite, ulWritten, pData->pathDstFile, bSkip);
+ if(eResult != TSubTaskBase::eSubResult_Continue)
+ return eResult;
+ else if(bSkip)
+ {
+ rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize());
+ pData->bProcessed = false;
+ return TSubTaskBase::eSubResult_Continue;
+ }
+
+ // increase count of processed data
+ rBasicProgressInfo.IncreaseCurrentFileProcessedSize(ulWritten);
+ rLocalStats.IncreaseProcessedSize(ulWritten);
+
+ // calculate count of bytes left to be written
+ ulRead -= ulWritten;
+
+ // now remove part of data from buffer (ulWritten bytes)
+ pData->dbBuffer.CutDataFromBuffer(ulWritten);
+ }
+
+ // close and re-open the destination file with buffering option for append
+ fileDst.Close();
+
+ // are there any more data to be written?
+ if(ulRead != 0)
+ {
+ // re-open the destination file, this time with standard buffering to allow writing not aligned part of file data
+ eResult = OpenExistingDestinationFileFB(fileDst, pData->pathDstFile, false);
+ if(eResult != TSubTaskBase::eSubResult_Continue)
+ return eResult;
+ else if(!fileDst.IsOpen())
+ {
+ rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize());
+ pData->bProcessed = false;
+ return TSubTaskBase::eSubResult_Continue;
+ }
+
+ // move file pointer to the end of destination file
+ eResult = SetFilePointerFB(fileDst, rBasicProgressInfo.GetCurrentFileProcessedSize(), pData->pathDstFile, bSkip);
+ if(eResult != TSubTaskBase::eSubResult_Continue)
+ return eResult;
+ else if(bSkip)
+ {
+ // with either first or second seek we got 'skip' answer...
+ rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize());
+ pData->bProcessed = false;
+ return TSubTaskBase::eSubResult_Continue;
+ }
+ }
+ }
+
+ // write
+ if(ulRead != 0)
+ {
+ eResult = WriteFileFB(fileDst, pData->dbBuffer, ulRead, ulWritten, pData->pathDstFile, bSkip);
+ if(eResult != TSubTaskBase::eSubResult_Continue)
+ return eResult;
+ else if(bSkip)
+ {
+ rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize());
+ pData->bProcessed = false;
+ return TSubTaskBase::eSubResult_Continue;
+ }
+
+ // increase count of processed data
+ rBasicProgressInfo.IncreaseCurrentFileProcessedSize(ulRead);
+ rLocalStats.IncreaseProcessedSize(ulRead);
+ }
+ }
+ }
+ while(ulRead != 0 && !bLastPart);
+ }
+ else
+ {
+ // we don't copy contents, but need to increase processed size
+ rLocalStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64() - rBasicProgressInfo.GetCurrentFileProcessedSize());
+ }
+
+ pData->bProcessed = true;
+ rBasicProgressInfo.SetCurrentFileProcessedSize(0);
+
+ return TSubTaskBase::eSubResult_Continue;
+}
+
+
+TSubTaskBase::ESubOperationResult TSubTaskCopyMove::OpenSourceFileFB(TLocalFilesystemFile& fileSrc, const TSmartPath& spPathToOpen, bool bNoBuffering)
+{
+ IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler();
+ icpf::log_file& rLog = GetContext().GetLog();
+
+ BOOST_ASSERT(!spPathToOpen.IsEmpty());
+ if(spPathToOpen.IsEmpty())
+ THROW_CORE_EXCEPTION(eErr_InvalidArgument);
+
+ bool bRetry = false;
+
+ fileSrc.Close();
+
+ do
+ {
+ bRetry = false;
+
+ if(!fileSrc.OpenExistingForReading(spPathToOpen, bNoBuffering))
+ {
+ DWORD dwLastError = GetLastError();
+
+ FEEDBACK_FILEERROR feedStruct = { spPathToOpen.ToString(), NULL, eCreateError, dwLastError };
+ IFeedbackHandler::EFeedbackResult frResult = (IFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(IFeedbackHandler::eFT_FileError, &feedStruct);
+
+ switch(frResult)
+ {
+ case IFeedbackHandler::eResult_Skip:
+ break; // will return INVALID_HANDLE_VALUE
+
+ case IFeedbackHandler::eResult_Cancel:
+ {
+ // log
+ TString strFormat = _T("Cancel request [error %errno] while opening source file %path (OpenSourceFileFB)");
+ strFormat.Replace(_T("%errno"), boost::lexical_cast(dwLastError).c_str());
+ strFormat.Replace(_T("%path"), spPathToOpen.ToString());
+ rLog.loge(strFormat);
+
+ return TSubTaskBase::eSubResult_CancelRequest;
+ }
+
+ case IFeedbackHandler::eResult_Pause:
+ return TSubTaskBase::eSubResult_PauseRequest;
+
+ case IFeedbackHandler::eResult_Retry:
+ {
+ // log
+ TString strFormat = _T("Retrying [error %errno] to open source file %path (OpenSourceFileFB)");
+ strFormat.Replace(_T("%errno"), boost::lexical_cast(dwLastError).c_str());
+ strFormat.Replace(_T("%path"), spPathToOpen.ToString());
+ rLog.loge(strFormat);
+
+ bRetry = true;
+ break;
+ }
+
+ default:
+ BOOST_ASSERT(FALSE); // unknown result
+ THROW_CORE_EXCEPTION(eErr_UnhandledCase);
+ }
+ }
+ }
+ while(bRetry);
+
+ return TSubTaskBase::eSubResult_Continue;
+}
+
+TSubTaskBase::ESubOperationResult TSubTaskCopyMove::OpenDestinationFileFB(TLocalFilesystemFile& fileDst, const TSmartPath& pathDstFile, bool bNoBuffering, const TFileInfoPtr& spSrcFileInfo, unsigned long long& ullSeekTo, bool& bFreshlyCreated)
+{
+ IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler();
+ icpf::log_file& rLog = GetContext().GetLog();
+
+ bool bRetry = false;
+
+ ullSeekTo = 0;
+ bFreshlyCreated = true;
+
+ fileDst.Close();
+ do
+ {
+ bRetry = false;
+
+ if(!fileDst.CreateNewForWriting(pathDstFile, bNoBuffering))
+ {
+ DWORD dwLastError = GetLastError();
+ if(dwLastError == ERROR_FILE_EXISTS)
+ {
+ bFreshlyCreated = false;
+
+ // pass it to the specialized method
+ TSubTaskBase::ESubOperationResult eResult = OpenExistingDestinationFileFB(fileDst, pathDstFile, bNoBuffering);
+ if(eResult != TSubTaskBase::eSubResult_Continue)
+ return eResult;
+ else if(!fileDst.IsOpen())
+ return TSubTaskBase::eSubResult_Continue;
+
+ // read info about the existing destination file,
+ // NOTE: it is not known which one would be faster - reading file parameters
+ // by using spDstFileInfo->Create() (which uses FindFirstFile()) or by
+ // reading parameters using opened handle; need to be tested in the future
+ TFileInfoPtr spDstFileInfo(boost::make_shared());
+
+ if(!TLocalFilesystem::GetFileInfo(pathDstFile, spDstFileInfo))
+ THROW_CORE_EXCEPTION_WIN32(eErr_CannotGetFileInfo, GetLastError());
+
+ // src and dst files are the same
+ FEEDBACK_ALREADYEXISTS feedStruct = { spSrcFileInfo, spDstFileInfo };
+ IFeedbackHandler::EFeedbackResult frResult = (IFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(IFeedbackHandler::eFT_FileAlreadyExists, &feedStruct);
+ // check for dialog result
+ switch(frResult)
+ {
+ case IFeedbackHandler::eResult_Overwrite:
+ ullSeekTo = 0;
+ break;
+
+ case IFeedbackHandler::eResult_CopyRest:
+ ullSeekTo = spDstFileInfo->GetLength64();
+ break;
+
+ case IFeedbackHandler::eResult_Skip:
+ return TSubTaskBase::eSubResult_Continue;
+
+ case IFeedbackHandler::eResult_Cancel:
+ {
+ // log
+ TString strFormat = _T("Cancel request while checking result of dialog before opening source file %path (CustomCopyFileFB)");
+ strFormat.Replace(_T("%path"), pathDstFile.ToString());
+ rLog.logi(strFormat);
+
+ return TSubTaskBase::eSubResult_CancelRequest;
+ }
+ case IFeedbackHandler::eResult_Pause:
+ return TSubTaskBase::eSubResult_PauseRequest;
+
+ default:
+ BOOST_ASSERT(FALSE); // unknown result
+ THROW_CORE_EXCEPTION(eErr_UnhandledCase);
+ }
+ }
+ else
+ {
+ FEEDBACK_FILEERROR feedStruct = { pathDstFile.ToString(), NULL, eCreateError, dwLastError };
+ IFeedbackHandler::EFeedbackResult frResult = (IFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(IFeedbackHandler::eFT_FileError, &feedStruct);
+ switch(frResult)
+ {
+ case IFeedbackHandler::eResult_Retry:
+ {
+ // log
+ TString strFormat = _T("Retrying [error %errno] to open destination file %path (CustomCopyFileFB)");
+ strFormat.Replace(_T("%errno"), boost::lexical_cast(dwLastError).c_str());
+ strFormat.Replace(_T("%path"), pathDstFile.ToString());
+ rLog.loge(strFormat);
+
+ bRetry = true;
+
+ break;
+ }
+ case IFeedbackHandler::eResult_Cancel:
+ {
+ // log
+ TString strFormat = _T("Cancel request [error %errno] while opening destination file %path (CustomCopyFileFB)");
+ strFormat.Replace(_T("%errno"), boost::lexical_cast(dwLastError).c_str());
+ strFormat.Replace(_T("%path"), pathDstFile.ToString());
+ rLog.loge(strFormat);
+
+ return TSubTaskBase::eSubResult_CancelRequest;
+ }
+
+ case IFeedbackHandler::eResult_Skip:
+ break; // will return invalid handle value
+
+ case IFeedbackHandler::eResult_Pause:
+ return TSubTaskBase::eSubResult_PauseRequest;
+
+ default:
+ BOOST_ASSERT(FALSE); // unknown result
+ THROW_CORE_EXCEPTION(eErr_UnhandledCase);
+ }
+ }
+ }
+ }
+ while(bRetry);
+
+ return TSubTaskBase::eSubResult_Continue;
+}
+
+TSubTaskBase::ESubOperationResult TSubTaskCopyMove::OpenExistingDestinationFileFB(TLocalFilesystemFile& fileDst, const TSmartPath& pathDstFile, bool bNoBuffering)
+{
+ IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler();
+ icpf::log_file& rLog = GetContext().GetLog();
+
+ bool bRetry = false;
+
+ fileDst.Close();
+
+ do
+ {
+ bRetry = false;
+
+ if(!fileDst.OpenExistingForWriting(pathDstFile, bNoBuffering))
+ {
+ DWORD dwLastError = GetLastError();
+ FEEDBACK_FILEERROR feedStruct = { pathDstFile.ToString(), NULL, eCreateError, dwLastError };
+ IFeedbackHandler::EFeedbackResult frResult = (IFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(IFeedbackHandler::eFT_FileError, &feedStruct);
+ switch (frResult)
+ {
+ case IFeedbackHandler::eResult_Retry:
+ {
+ // log
+ TString strFormat = _T("Retrying [error %errno] to open destination file %path (CustomCopyFileFB)");
+ strFormat.Replace(_T("%errno"), boost::lexical_cast(dwLastError).c_str());
+ strFormat.Replace(_t("%path"), pathDstFile.ToString());
+ rLog.loge(strFormat);
+
+ bRetry = true;
+
+ break;
+ }
+ case IFeedbackHandler::eResult_Cancel:
+ {
+ // log
+ TString strFormat = _T("Cancel request [error %errno] while opening destination file %path (CustomCopyFileFB)");
+ strFormat.Replace(_T("%errno"), boost::lexical_cast(dwLastError).c_str());
+ strFormat.Replace(_T("%path"), pathDstFile.ToString());
+ rLog.loge(strFormat);
+
+ return TSubTaskBase::eSubResult_CancelRequest;
+ }
+
+ case IFeedbackHandler::eResult_Skip:
+ break; // will return invalid handle value
+
+ case IFeedbackHandler::eResult_Pause:
+ return TSubTaskBase::eSubResult_PauseRequest;
+
+ default:
+ BOOST_ASSERT(FALSE); // unknown result
+ THROW_CORE_EXCEPTION(eErr_UnhandledCase);
+ }
+ }
+ }
+ while(bRetry);
+
+ return TSubTaskBase::eSubResult_Continue;
+}
+
+TSubTaskBase::ESubOperationResult TSubTaskCopyMove::SetFilePointerFB(TLocalFilesystemFile& file, long long llDistance, const TSmartPath& pathFile, bool& bSkip)
+{
+ IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler();
+ icpf::log_file& rLog = GetContext().GetLog();
+
+ bSkip = false;
+ bool bRetry = false;
+ do
+ {
+ bRetry = false;
+
+ if(!file.SetFilePointer(llDistance, FILE_BEGIN))
+ {
+ DWORD dwLastError = GetLastError();
+
+ // log
+ TString strFormat = _T("Error %errno while moving file pointer of %path to %pos");
+ strFormat.Replace(_t("%errno"), boost::lexical_cast(dwLastError).c_str());
+ strFormat.Replace(_t("%path"), pathFile.ToString());
+ strFormat.Replace(_t("%pos"), boost::lexical_cast(llDistance).c_str());
+ rLog.loge(strFormat);
+
+ FEEDBACK_FILEERROR ferr = { pathFile.ToString(), NULL, eSeekError, dwLastError };
+ IFeedbackHandler::EFeedbackResult frResult = (IFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(IFeedbackHandler::eFT_FileError, &ferr);
+ switch(frResult)
+ {
+ case IFeedbackHandler::eResult_Cancel:
+ return TSubTaskBase::eSubResult_CancelRequest;
+
+ case IFeedbackHandler::eResult_Retry:
+ bRetry = true;
+ break;
+
+ case IFeedbackHandler::eResult_Pause:
+ return TSubTaskBase::eSubResult_PauseRequest;
+
+ case IFeedbackHandler::eResult_Skip:
+ bSkip = true;
+ return TSubTaskBase::eSubResult_Continue;
+
+ default:
+ BOOST_ASSERT(FALSE); // unknown result
+ THROW_CORE_EXCEPTION(eErr_UnhandledCase);
+ }
+ }
+ }
+ while(bRetry);
+
+ return TSubTaskBase::eSubResult_Continue;
+}
+
+TSubTaskBase::ESubOperationResult TSubTaskCopyMove::SetEndOfFileFB(TLocalFilesystemFile& file, const TSmartPath& pathFile, bool& bSkip)
+{
+ IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler();
+ icpf::log_file& rLog = GetContext().GetLog();
+
+ bSkip = false;
+
+ bool bRetry = false;
+ do
+ {
+ if(!file.SetEndOfFile())
+ {
+ // log
+ DWORD dwLastError = GetLastError();
+
+ TString strFormat = _T("Error %errno while setting size of file %path to 0");
+ strFormat.Replace(_t("%errno"), boost::lexical_cast(dwLastError).c_str());
+ strFormat.Replace(_t("%path"), pathFile.ToString());
+ rLog.loge(strFormat);
+
+ FEEDBACK_FILEERROR ferr = { pathFile.ToString(), NULL, eResizeError, dwLastError };
+ IFeedbackHandler::EFeedbackResult frResult = (IFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(IFeedbackHandler::eFT_FileError, &ferr);
+ switch(frResult)
+ {
+ case IFeedbackHandler::eResult_Cancel:
+ return TSubTaskBase::eSubResult_CancelRequest;
+
+ case IFeedbackHandler::eResult_Retry:
+ bRetry = true;
+
+ case IFeedbackHandler::eResult_Pause:
+ return TSubTaskBase::eSubResult_PauseRequest;
+
+ case IFeedbackHandler::eResult_Skip:
+ bSkip = true;
+ return TSubTaskBase::eSubResult_Continue;
+
+ default:
+ BOOST_ASSERT(FALSE); // unknown result
+ THROW_CORE_EXCEPTION(eErr_UnhandledCase);
+ }
+ }
+ }
+ while(bRetry);
+
+ return TSubTaskBase::eSubResult_Continue;
+}
+
+TSubTaskBase::ESubOperationResult TSubTaskCopyMove::ReadFileFB(TLocalFilesystemFile& file, TDataBuffer& rBuffer, DWORD dwToRead, DWORD& rdwBytesRead, const TSmartPath& pathFile, bool& bSkip)
+{
+ IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler();
+ icpf::log_file& rLog = GetContext().GetLog();
+
+ bSkip = false;
+ bool bRetry = false;
+ do
+ {
+ bRetry = false;
+
+ if(!file.ReadFile(rBuffer, dwToRead, rdwBytesRead))
+ {
+ // log
+ DWORD dwLastError = GetLastError();
+
+ TString strFormat = _T("Error %errno while trying to read %count bytes from source file %path (CustomCopyFileFB)");
+ strFormat.Replace(_t("%errno"), boost::lexical_cast(dwLastError).c_str());
+ strFormat.Replace(_t("%count"), boost::lexical_cast(dwToRead).c_str());
+ strFormat.Replace(_t("%path"), pathFile.ToString());
+ rLog.loge(strFormat);
+
+ FEEDBACK_FILEERROR ferr = { pathFile.ToString(), NULL, eReadError, dwLastError };
+ IFeedbackHandler::EFeedbackResult frResult = (IFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(IFeedbackHandler::eFT_FileError, &ferr);
+ switch(frResult)
+ {
+ case IFeedbackHandler::eResult_Cancel:
+ return TSubTaskBase::eSubResult_CancelRequest;
+
+ case IFeedbackHandler::eResult_Retry:
+ bRetry = true;
+ break;
+
+ case IFeedbackHandler::eResult_Pause:
+ return TSubTaskBase::eSubResult_PauseRequest;
+
+ case IFeedbackHandler::eResult_Skip:
+ bSkip = true;
+ return TSubTaskBase::eSubResult_Continue;
+
+ default:
+ BOOST_ASSERT(FALSE); // unknown result
+ THROW_CORE_EXCEPTION(eErr_UnhandledCase);
+ }
+ }
+ }
+ while(bRetry);
+
+ return TSubTaskBase::eSubResult_Continue;
+}
+
+TSubTaskBase::ESubOperationResult TSubTaskCopyMove::WriteFileFB(TLocalFilesystemFile& file, TDataBuffer& rBuffer, DWORD dwToWrite, DWORD& rdwBytesWritten, const TSmartPath& pathFile, bool& bSkip)
+{
+ IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler();
+ icpf::log_file& rLog = GetContext().GetLog();
+
+ bSkip = false;
+
+ bool bRetry = false;
+ do
+ {
+ bRetry = false;
+
+ if(!file.WriteFile(rBuffer, dwToWrite, rdwBytesWritten))
+ {
+ // log
+ DWORD dwLastError = GetLastError();
+
+ TString strFormat = _T("Error %errno while trying to write %count bytes to destination file %path (CustomCopyFileFB)");
+ strFormat.Replace(_t("%errno"), boost::lexical_cast(dwLastError).c_str());
+ strFormat.Replace(_t("%count"), boost::lexical_cast(dwToWrite).c_str());
+ strFormat.Replace(_t("%path"), pathFile.ToString());
+ rLog.loge(strFormat);
+
+ FEEDBACK_FILEERROR ferr = { pathFile.ToString(), NULL, eWriteError, dwLastError };
+ IFeedbackHandler::EFeedbackResult frResult = (IFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(IFeedbackHandler::eFT_FileError, &ferr);
+ switch(frResult)
+ {
+ case IFeedbackHandler::eResult_Cancel:
+ return TSubTaskBase::eSubResult_CancelRequest;
+
+ case IFeedbackHandler::eResult_Retry:
+ bRetry = true;
+ break;
+
+ case IFeedbackHandler::eResult_Pause:
+ return TSubTaskBase::eSubResult_PauseRequest;
+
+ case IFeedbackHandler::eResult_Skip:
+ bSkip = true;
+ return TSubTaskBase::eSubResult_Continue;
+
+ default:
+ BOOST_ASSERT(FALSE); // unknown result
+ THROW_CORE_EXCEPTION(eErr_UnhandledCase);
+ }
+ }
+ }
+ while(bRetry);
+
+ return TSubTaskBase::eSubResult_Continue;
+}
+
+TSubTaskBase::ESubOperationResult TSubTaskCopyMove::CheckForFreeSpaceFB()
+{
+ icpf::log_file& rLog = GetContext().GetLog();
+ TTaskDefinition& rTaskDefinition = GetContext().GetTaskDefinition();
+ IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler();
+ TTaskLocalStats& rLocalStats = GetContext().GetTaskLocalStats();
+ TLocalFilesystem& rLocalFilesystem = GetContext().GetLocalFilesystem();
+
+ ull_t ullNeededSize = 0, ullAvailableSize = 0;
+ bool bRetry = false;
+
+ do
+ {
+ bRetry = false;
+
+ rLog.logi(_T("Checking for free space on destination disk..."));
+
+ ullNeededSize = rLocalStats.GetUnProcessedSize(); // it'd be nice to round up to take cluster size into consideration,
+
+ // get free space
+ bool bResult = rLocalFilesystem.GetDynamicFreeSpace(rTaskDefinition.GetDestinationPath(), ullAvailableSize);
+ if(bResult && ullNeededSize > ullAvailableSize)
+ {
+ TString strFormat = _T("Not enough free space on disk - needed %needsize bytes for data, available: %availablesize bytes.");
+ strFormat.Replace(_t("%needsize"), boost::lexical_cast(ullNeededSize).c_str());
+ strFormat.Replace(_t("%availablesize"), boost::lexical_cast(ullAvailableSize).c_str());
+ rLog.logw(strFormat);
+
+ if(rTaskDefinition.GetSourcePathCount() > 0)
+ {
+ FEEDBACK_NOTENOUGHSPACE feedStruct = { ullNeededSize, rTaskDefinition.GetSourcePathAt(0).ToString(), rTaskDefinition.GetDestinationPath().ToString() };
+ IFeedbackHandler::EFeedbackResult frResult = (IFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(IFeedbackHandler::eFT_NotEnoughSpace, &feedStruct);
+
+ // default
+ switch(frResult)
+ {
+ case IFeedbackHandler::eResult_Cancel:
+ rLog.logi(_T("Cancel request while checking for free space on disk."));
+ return TSubTaskBase::eSubResult_CancelRequest;
+
+ case IFeedbackHandler::eResult_Retry:
+ rLog.logi(_T("Retrying to read drive's free space..."));
+ bRetry = true;
+ break;
+
+ case IFeedbackHandler::eResult_Ignore:
+ rLog.logi(_T("Ignored warning about not enough place on disk to copy data."));
+ return TSubTaskBase::eSubResult_Continue;
+
+ default:
+ BOOST_ASSERT(FALSE); // unknown result
+ THROW_CORE_EXCEPTION(eErr_UnhandledCase);
+ }
+ }
+ }
+ }
+ while(bRetry);
+
+ return TSubTaskBase::eSubResult_Continue;
+}
+
+END_CHCORE_NAMESPACE
Index: src/libchcore/TSubTaskCopyMove.h
===================================================================
diff -u
--- src/libchcore/TSubTaskCopyMove.h (revision 0)
+++ src/libchcore/TSubTaskCopyMove.h (revision 4d20d0e58f37f06ac91287015b960308db54d47e)
@@ -0,0 +1,65 @@
+// ============================================================================
+// Copyright (C) 2001-2009 by Jozef Starosczyk
+// ixen@copyhandler.com
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Library General Public License
+// (version 2) as published by the Free Software Foundation;
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// ============================================================================
+/// @file TSubTaskCopyMove.h
+/// @date 2010/09/18
+/// @brief Contains declarations of classes responsible for copy and move sub-operation.
+// ============================================================================
+#ifndef __TSUBTASKCOPYMOVE_H__
+#define __TSUBTASKCOPYMOVE_H__
+
+#include "libchcore.h"
+#include "TSubTaskBase.h"
+
+BEGIN_CHCORE_NAMESPACE
+
+class TDataBuffer;
+class TLocalFilesystemFile;
+typedef boost::shared_ptr TFileInfoPtr;
+struct CUSTOM_COPY_PARAMS;
+
+class LIBCHCORE_API TSubTaskCopyMove : public TSubTaskBase
+{
+public:
+ TSubTaskCopyMove(TSubTaskContext& tSubTaskContext);
+
+ ESubOperationResult Exec();
+
+private:
+ bool GetMove(const TFileInfoPtr& spFileInfo);
+ int GetBufferIndex(const TFileInfoPtr& spFileInfo);
+
+ ESubOperationResult CustomCopyFileFB(CUSTOM_COPY_PARAMS* pData);
+
+ ESubOperationResult OpenSourceFileFB(TLocalFilesystemFile& fileSrc, const TSmartPath& spPathToOpen, bool bNoBuffering);
+ ESubOperationResult OpenDestinationFileFB(TLocalFilesystemFile& fileDst, const TSmartPath& pathDstFile, bool bNoBuffering, const TFileInfoPtr& spSrcFileInfo, unsigned long long& ullSeekTo, bool& bFreshlyCreated);
+ ESubOperationResult OpenExistingDestinationFileFB(TLocalFilesystemFile& fileDst, const TSmartPath& pathDstFilePath, bool bNoBuffering);
+
+ ESubOperationResult SetFilePointerFB(TLocalFilesystemFile& file, long long llDistance, const TSmartPath& pathFile, bool& bSkip);
+ ESubOperationResult SetEndOfFileFB(TLocalFilesystemFile& file, const TSmartPath& pathFile, bool& bSkip);
+
+ ESubOperationResult ReadFileFB(TLocalFilesystemFile& file, TDataBuffer& rBuffer, DWORD dwToRead, DWORD& rdwBytesRead, const TSmartPath& pathFile, bool& bSkip);
+ ESubOperationResult WriteFileFB(TLocalFilesystemFile& file, TDataBuffer& rBuffer, DWORD dwToWrite, DWORD& rdwBytesWritten, const TSmartPath& pathFile, bool& bSkip);
+ ESubOperationResult CreateDirectoryFB(const TSmartPath& pathDirectory);
+
+ ESubOperationResult CheckForFreeSpaceFB();
+};
+
+END_CHCORE_NAMESPACE
+
+#endif
Index: src/libchcore/TSubTaskDelete.cpp
===================================================================
diff -u
--- src/libchcore/TSubTaskDelete.cpp (revision 0)
+++ src/libchcore/TSubTaskDelete.cpp (revision 4d20d0e58f37f06ac91287015b960308db54d47e)
@@ -0,0 +1,144 @@
+// ============================================================================
+// Copyright (C) 2001-2009 by Jozef Starosczyk
+// ixen@copyhandler.com
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Library General Public License
+// (version 2) as published by the Free Software Foundation;
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// ============================================================================
+/// @file TSubTaskDelete.cpp
+/// @date 2010/09/19
+/// @brief Contains implementation of classes responsible for delete sub-operation.
+// ============================================================================
+#include "stdafx.h"
+#include "TSubTaskDelete.h"
+#include "TSubTaskContext.h"
+#include "TBasicProgressInfo.h"
+#include "TWorkerThreadController.h"
+#include "TTaskConfiguration.h"
+#include "TTaskDefinition.h"
+//#include "FeedbackHandler.h"
+#include "TLocalFilesystem.h"
+#include "..\libicpf\log.h"
+#include "FeedbackHandlerBase.h"
+#include
+
+BEGIN_CHCORE_NAMESPACE
+
+TSubTaskDelete::TSubTaskDelete(TSubTaskContext& rContext) :
+ TSubTaskBase(rContext)
+{
+}
+
+TSubTaskBase::ESubOperationResult TSubTaskDelete::Exec()
+{
+ // log
+ icpf::log_file& rLog = GetContext().GetLog();
+ TFileInfoArray& rFilesCache = GetContext().GetFilesCache();
+ TTaskDefinition& rTaskDefinition = GetContext().GetTaskDefinition();
+ TTaskBasicProgressInfo& rBasicProgressInfo = GetContext().GetTaskBasicProgressInfo();
+ TWorkerThreadController& rThreadController = GetContext().GetThreadController();
+ IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler();
+
+ // log
+ rLog.logi(_T("Deleting files (DeleteFiles)..."));
+
+ // current processed path
+ BOOL bSuccess;
+ TFileInfoPtr spFileInfo;
+ TString strFormat;
+
+ // index points to 0 or next item to process
+ size_t stIndex = rBasicProgressInfo.GetCurrentIndex();
+ while(stIndex < rFilesCache.GetSize())
+ {
+ // set index in pTask to currently deleted element
+ rBasicProgressInfo.SetCurrentIndex(stIndex);
+
+ // check for kill flag
+ if(rThreadController.KillRequested())
+ {
+ // log
+ rLog.logi(_T("Kill request while deleting files (Delete Files)"));
+ return TSubTaskBase::eSubResult_KillRequest;
+ }
+
+ // current processed element
+ spFileInfo = rFilesCache.GetAt(rFilesCache.GetSize() - stIndex - 1);
+ if(!(spFileInfo->GetFlags() & FIF_PROCESSED))
+ {
+ ++stIndex;
+ continue;
+ }
+
+ // delete data
+ if(spFileInfo->IsDirectory())
+ {
+ if(!GetTaskPropValue(rTaskDefinition.GetConfiguration()))
+ TLocalFilesystem::SetAttributes(spFileInfo->GetFullFilePath(), FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY);
+ bSuccess = TLocalFilesystem::RemoveDirectory(spFileInfo->GetFullFilePath());
+ }
+ else
+ {
+ // set files attributes to normal - it'd slow processing a bit, but it's better.
+ if(!GetTaskPropValue(rTaskDefinition.GetConfiguration()))
+ TLocalFilesystem::SetAttributes(spFileInfo->GetFullFilePath(), FILE_ATTRIBUTE_NORMAL);
+ bSuccess = TLocalFilesystem::DeleteFile(spFileInfo->GetFullFilePath());
+ }
+
+ // operation failed
+ DWORD dwLastError = GetLastError();
+ if(!bSuccess && dwLastError != ERROR_PATH_NOT_FOUND && dwLastError != ERROR_FILE_NOT_FOUND)
+ {
+ // log
+ strFormat = _T("Error #%errno while deleting file/folder %path");
+ strFormat.Replace(_T("%errno"), boost::lexical_cast(dwLastError).c_str());
+ strFormat.Replace(_T("%path"), spFileInfo->GetFullFilePath().ToString());
+ rLog.loge(strFormat);
+
+ FEEDBACK_FILEERROR ferr = { spFileInfo->GetFullFilePath().ToString(), NULL, eDeleteError, dwLastError };
+ IFeedbackHandler::EFeedbackResult frResult = (IFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(IFeedbackHandler::eFT_FileError, &ferr);
+ switch(frResult)
+ {
+ case IFeedbackHandler::eResult_Cancel:
+ rLog.logi(_T("Cancel request while deleting file."));
+ return TSubTaskBase::eSubResult_CancelRequest;
+
+ case IFeedbackHandler::eResult_Retry:
+ continue; // no stIndex bump, since we are trying again
+
+ case IFeedbackHandler::eResult_Pause:
+ return TSubTaskBase::eSubResult_PauseRequest;
+
+ case IFeedbackHandler::eResult_Skip:
+ break; // just do nothing
+
+ default:
+ BOOST_ASSERT(FALSE); // unknown result
+ THROW_CORE_EXCEPTION(eErr_UnhandledCase);
+ }
+ }
+
+ ++stIndex;
+ }//while
+
+ // add 1 to current index
+ rBasicProgressInfo.IncreaseCurrentIndex();
+
+ // log
+ rLog.logi(_T("Deleting files finished"));
+
+ return TSubTaskBase::eSubResult_Continue;
+}
+
+END_CHCORE_NAMESPACE
Fisheye: tag c866a5e96f5eaf160278a8c128bf86e342cc2409 is not in file src/libchcore/TSubTaskDelete.h
Fisheye: Tag 4d20d0e58f37f06ac91287015b960308db54d47e refers to a dead (removed) revision in file `src/ch/TSubTaskDelete.h'.
Fisheye: No comparison available. Pass `N' to diff?
Index: src/libchcore/TSubTaskScanDirectory.cpp
===================================================================
diff -u
--- src/libchcore/TSubTaskScanDirectory.cpp (revision 0)
+++ src/libchcore/TSubTaskScanDirectory.cpp (revision 4d20d0e58f37f06ac91287015b960308db54d47e)
@@ -0,0 +1,266 @@
+// ============================================================================
+// Copyright (C) 2001-2009 by Jozef Starosczyk
+// ixen@copyhandler.com
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Library General Public License
+// (version 2) as published by the Free Software Foundation;
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+// ============================================================================
+/// @file TSubTaskScanDirectory.cpp
+/// @date 2010/09/18
+/// @brief Contains implementation of classes related to scan directory subtask.
+// ============================================================================
+#include "stdafx.h"
+#include "TSubTaskScanDirectory.h"
+#include "TSubTaskContext.h"
+#include "TTaskConfiguration.h"
+#include "TTaskDefinition.h"
+//#include "FeedbackHandler.h"
+#include "TLocalFilesystem.h"
+#include "FeedbackHandlerBase.h"
+#include "TBasePathData.h"
+#include "TWorkerThreadController.h"
+#include "TTaskLocalStats.h"
+#include
+#include "..\libicpf\log.h"
+
+BEGIN_CHCORE_NAMESPACE
+
+TSubTaskScanDirectories::TSubTaskScanDirectories(TSubTaskContext& rContext) :
+ TSubTaskBase(rContext)
+{
+}
+
+TSubTaskScanDirectories::~TSubTaskScanDirectories()
+{
+}
+
+TSubTaskScanDirectories::ESubOperationResult TSubTaskScanDirectories::Exec()
+{
+ // log
+ icpf::log_file& rLog = GetContext().GetLog();
+ TFileInfoArray& rFilesCache = GetContext().GetFilesCache();
+ TTaskDefinition& rTaskDefinition = GetContext().GetTaskDefinition();
+ IFeedbackHandler* piFeedbackHandler = GetContext().GetFeedbackHandler();
+ const TBasePathDataContainer& rarrSourcePathsInfo = GetContext().GetBasePathDataContainer();
+ TWorkerThreadController& rThreadController = GetContext().GetThreadController();
+ TTaskLocalStats& rTaskLocalStats = GetContext().GetTaskLocalStats();
+
+ rLog.logi(_T("Searching for files..."));
+
+ // reset progress
+ rTaskLocalStats.SetProcessedSize(0);
+ rTaskLocalStats.SetTotalSize(0);
+
+ // delete the content of rFilesCache
+ rFilesCache.Clear();
+
+ // read filtering options
+ TFiltersArray afFilters;
+ GetTaskPropValue(rTaskDefinition.GetConfiguration(), afFilters);
+
+ // enter some data to rFilesCache
+ wchar_t wchDestinationDriveLetter = rTaskDefinition.GetDestinationPath().GetDriveLetter();
+
+ bool bIgnoreDirs = GetTaskPropValue(rTaskDefinition.GetConfiguration());
+ bool bForceDirectories = GetTaskPropValue(rTaskDefinition.GetConfiguration());
+ bool bMove = rTaskDefinition.GetOperationType() == eOperation_Move;
+
+ // add everything
+ TString strFormat;
+ bool bRetry = true;
+ bool bSkipInputPath = false;
+
+ size_t stSize = rTaskDefinition.GetSourcePathCount();
+ for(size_t stIndex = 0; stIndex < stSize ; stIndex++)
+ {
+ TFileInfoPtr spFileInfo;
+
+ bSkipInputPath = false;
+
+ spFileInfo.reset(new TFileInfo());
+
+ // try to get some info about the input path; let user know if the path does not exist.
+ do
+ {
+ bRetry = false;
+
+ // read attributes of src file/folder
+ bool bExists = TLocalFilesystem::GetFileInfo(rTaskDefinition.GetSourcePathAt(stIndex), spFileInfo, stIndex, &rTaskDefinition.GetSourcePaths());
+ if(!bExists)
+ {
+ FEEDBACK_FILEERROR ferr = { rTaskDefinition.GetSourcePathAt(stIndex).ToString(), NULL, eFastMoveError, ERROR_FILE_NOT_FOUND };
+ IFeedbackHandler::EFeedbackResult frResult = (IFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(IFeedbackHandler::eFT_FileError, &ferr);
+ switch(frResult)
+ {
+ case IFeedbackHandler::eResult_Cancel:
+ rFilesCache.Clear();
+ return eSubResult_CancelRequest;
+
+ case IFeedbackHandler::eResult_Retry:
+ bRetry = true;
+ break;
+
+ case IFeedbackHandler::eResult_Pause:
+ rFilesCache.Clear();
+ return eSubResult_PauseRequest;
+
+ case IFeedbackHandler::eResult_Skip:
+ bSkipInputPath = true;
+ break; // just do nothing
+
+ default:
+ BOOST_ASSERT(FALSE); // unknown result
+ THROW_CORE_EXCEPTION(eErr_UnhandledCase);
+ }
+ }
+ }
+ while(bRetry);
+
+ // if we have chosen to skip the input path then there's nothing to do
+ if(bSkipInputPath)
+ continue;
+
+ // log
+ strFormat = _T("Adding file/folder (clipboard) : %path ...");
+ strFormat.Replace(_T("%path"), rTaskDefinition.GetSourcePathAt(stIndex).ToString());
+ rLog.logi(strFormat);
+
+ // found file/folder - check if the dest name has been generated
+ if(!rarrSourcePathsInfo.GetAt(stIndex)->IsDestinationPathSet())
+ {
+ // generate something - if dest folder == src folder - search for copy
+ if(rTaskDefinition.GetDestinationPath() == spFileInfo->GetFullFilePath().GetFileRoot())
+ {
+ TSmartPath pathSubst = FindFreeSubstituteName(spFileInfo->GetFullFilePath(), rTaskDefinition.GetDestinationPath());
+ rarrSourcePathsInfo.GetAt(stIndex)->SetDestinationPath(pathSubst);
+ }
+ else
+ rarrSourcePathsInfo.GetAt(stIndex)->SetDestinationPath(spFileInfo->GetFullFilePath().GetFileName());
+ }
+
+ wchar_t wchSourceDriveLetter = spFileInfo->GetFullFilePath().GetDriveLetter();
+
+ // add if needed
+ if(spFileInfo->IsDirectory())
+ {
+ // add if folder's aren't ignored
+ if(!bIgnoreDirs && !bForceDirectories)
+ {
+ // add directory info; it is not to be filtered with afFilters
+ rFilesCache.AddFileInfo(spFileInfo);
+
+ // log
+ strFormat = _T("Added folder %path");
+ strFormat.Replace(_T("%path"), spFileInfo->GetFullFilePath().ToString());
+ rLog.logi(strFormat);
+ }
+
+ // don't add folder contents when moving inside one disk boundary
+ if(bIgnoreDirs || !bMove || wchDestinationDriveLetter == L'\0' || wchDestinationDriveLetter != wchSourceDriveLetter ||
+ TLocalFilesystem::PathExist(CalculateDestinationPath(spFileInfo, rTaskDefinition.GetDestinationPath(), ((int)bForceDirectories) << 1)) )
+ {
+ // log
+ strFormat = _T("Recursing folder %path");
+ strFormat.Replace(_t("%path"), spFileInfo->GetFullFilePath().ToString());
+ rLog.logi(strFormat);
+
+ // no movefile possibility - use CustomCopyFileFB
+ rarrSourcePathsInfo.GetAt(stIndex)->SetMove(false);
+
+ ScanDirectory(spFileInfo->GetFullFilePath(), stIndex, true, !bIgnoreDirs || bForceDirectories, afFilters);
+ }
+
+ // check for kill need
+ if(rThreadController.KillRequested())
+ {
+ // log
+ rLog.logi(_T("Kill request while adding data to files array (RecurseDirectories)"));
+ rFilesCache.Clear();
+ return eSubResult_KillRequest;
+ }
+ }
+ else
+ {
+ if(bMove && wchDestinationDriveLetter != L'\0' && wchDestinationDriveLetter == wchSourceDriveLetter &&
+ !TLocalFilesystem::PathExist(CalculateDestinationPath(spFileInfo, rTaskDefinition.GetDestinationPath(), ((int)bForceDirectories) << 1)) )
+ {
+ // if moving within one partition boundary set the file size to 0 so the overall size will
+ // be ok
+ spFileInfo->SetLength64(0);
+ }
+ else
+ rarrSourcePathsInfo.GetAt(stIndex)->SetMove(false); // no MoveFile
+
+ // add file info if passes filters
+ if(afFilters.Match(spFileInfo))
+ rFilesCache.AddFileInfo(spFileInfo);
+
+ // log
+ strFormat = _T("Added file %path");
+ strFormat.Replace(_T("%path"), spFileInfo->GetFullFilePath().ToString());
+ rLog.logi(strFormat);
+ }
+ }
+
+ // calc size of all files
+ rTaskLocalStats.SetTotalSize(rFilesCache.CalculateTotalSize());
+
+ // log
+ rLog.logi(_T("Searching for files finished"));
+
+ return eSubResult_Continue;
+}
+
+int TSubTaskScanDirectories::ScanDirectory(TSmartPath pathDirName, size_t stSrcIndex, bool bRecurse, bool bIncludeDirs, TFiltersArray& afFilters)
+{
+ TFileInfoArray& rFilesCache = GetContext().GetFilesCache();
+ TTaskDefinition& rTaskDefinition = GetContext().GetTaskDefinition();
+ TWorkerThreadController& rThreadController = GetContext().GetThreadController();
+
+ TLocalFilesystemFind finder = TLocalFilesystem::CreateFinderObject(pathDirName, PathFromString(_T("*")));
+ TFileInfoPtr spFileInfo(boost::make_shared());
+
+ while(finder.FindNext(spFileInfo))
+ {
+ if(rThreadController.KillRequested())
+ break;
+
+ if(!spFileInfo->IsDirectory())
+ {
+ if(afFilters.Match(spFileInfo))
+ {
+ spFileInfo->SetParentObject(stSrcIndex, &rTaskDefinition.GetSourcePaths());
+ rFilesCache.AddFileInfo(spFileInfo);
+ spFileInfo = boost::make_shared();
+ }
+ }
+ else
+ {
+ TSmartPath pathCurrent = spFileInfo->GetFullFilePath();
+ if(bIncludeDirs)
+ {
+ spFileInfo->SetParentObject(stSrcIndex, &rTaskDefinition.GetSourcePaths());
+ rFilesCache.AddFileInfo(spFileInfo);
+ spFileInfo = boost::make_shared();
+ }
+
+ if(bRecurse)
+ ScanDirectory(pathCurrent, stSrcIndex, bRecurse, bIncludeDirs, afFilters);
+ }
+ }
+
+ return 0;
+}
+
+END_CHCORE_NAMESPACE
Fisheye: tag 2aea3ad6f3c68be709ac65c70d9646eafe3b034c is not in file src/libchcore/TSubTaskScanDirectory.h
Fisheye: Tag 4d20d0e58f37f06ac91287015b960308db54d47e refers to a dead (removed) revision in file `src/ch/TSubTaskScanDirectory.h'.
Fisheye: No comparison available. Pass `N' to diff?
Index: src/libchcore/TTaskConfiguration.h
===================================================================
diff -u -rfb4c4006dee5aaf815d08bc3e89312445b994307 -r4d20d0e58f37f06ac91287015b960308db54d47e
--- src/libchcore/TTaskConfiguration.h (.../TTaskConfiguration.h) (revision fb4c4006dee5aaf815d08bc3e89312445b994307)
+++ src/libchcore/TTaskConfiguration.h (.../TTaskConfiguration.h) (revision 4d20d0e58f37f06ac91287015b960308db54d47e)
@@ -122,8 +122,8 @@
TASK_PROPERTY(eTO_Filters, chcore::TFiltersArray, _T("Operation.Filtering"), chcore::TFiltersArray());
// Naming settings
-TASK_PROPERTY(eTO_AlternateFilenameFormatString_First, CString, _T("Naming.AlternateFilenameFormatFirst"), _T("Copy of %name"));
-TASK_PROPERTY(eTO_AlternateFilenameFormatString_AfterFirst, CString, _T("Naming.AlternateFilenameFormatAfterFirst"), _T("Copy (%count) of %name"));
+TASK_PROPERTY(eTO_AlternateFilenameFormatString_First, TString, _T("Naming.AlternateFilenameFormatFirst"), _T("Copy of %name"));
+TASK_PROPERTY(eTO_AlternateFilenameFormatString_AfterFirst, TString, _T("Naming.AlternateFilenameFormatAfterFirst"), _T("Copy (%count) of %name"));
/////////////////////////////////////////////////////////////////////////////////////////////
// other properties names
Index: src/libchcore/libchcore.vc90.vcproj
===================================================================
diff -u -rfb4c4006dee5aaf815d08bc3e89312445b994307 -r4d20d0e58f37f06ac91287015b960308db54d47e
--- src/libchcore/libchcore.vc90.vcproj (.../libchcore.vc90.vcproj) (revision fb4c4006dee5aaf815d08bc3e89312445b994307)
+++ src/libchcore/libchcore.vc90.vcproj (.../libchcore.vc90.vcproj) (revision 4d20d0e58f37f06ac91287015b960308db54d47e)
@@ -392,6 +392,14 @@
>
+
+
+
+
@@ -419,6 +427,42 @@
RelativePath=".\TTaskLocalStats.h"
>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+