Files
easy7zip/CPP/7zip/UI/FileManager/FSFolderCopy.cpp
Igor Pavlov f08f4dcc3c 9.34
2016-05-28 00:16:51 +01:00

528 lines
14 KiB
C++

// FSFolderCopy.cpp
#include "StdAfx.h"
#include <Winbase.h>
#include "../../../Common/Defs.h"
#include "../../../Common/StringConvert.h"
#include "../../../Common/Wildcard.h"
#include "../../../Windows/DLL.h"
#include "../../../Windows/ErrorMsg.h"
#include "../../../Windows/FileDir.h"
#include "../../../Windows/FileName.h"
#include "../../Common/FilePathAutoRename.h"
#include "FSFolder.h"
using namespace NWindows;
using namespace NFile;
using namespace NDir;
using namespace NName;
using namespace NFind;
#ifndef _UNICODE
extern bool g_IsNT;
#endif
namespace NFsFolder {
/*
static bool IsItWindows2000orHigher()
{
OSVERSIONINFO versionInfo;
versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
if (!::GetVersionEx(&versionInfo))
return false;
return (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
(versionInfo.dwMajorVersion >= 5);
}
*/
struct CProgressInfo
{
UInt64 StartPos;
IProgress *Progress;
};
#ifndef PROGRESS_CONTINUE
#define PROGRESS_CONTINUE 0
#define PROGRESS_CANCEL 1
#define COPY_FILE_FAIL_IF_EXISTS 0x00000001
typedef
DWORD
(WINAPI* LPPROGRESS_ROUTINE)(
LARGE_INTEGER TotalFileSize,
LARGE_INTEGER TotalBytesTransferred,
LARGE_INTEGER StreamSize,
LARGE_INTEGER StreamBytesTransferred,
DWORD dwStreamNumber,
DWORD dwCallbackReason,
HANDLE hSourceFile,
HANDLE hDestinationFile,
LPVOID lpData
);
#endif
static DWORD CALLBACK CopyProgressRoutine(
LARGE_INTEGER /* TotalFileSize */, // file size
LARGE_INTEGER TotalBytesTransferred, // bytes transferred
LARGE_INTEGER /* StreamSize */, // bytes in stream
LARGE_INTEGER /* StreamBytesTransferred */, // bytes transferred for stream
DWORD /* dwStreamNumber */, // current stream
DWORD /* dwCallbackReason */, // callback reason
HANDLE /* hSourceFile */, // handle to source file
HANDLE /* hDestinationFile */, // handle to destination file
LPVOID lpData // from CopyFileEx
)
{
CProgressInfo &progressInfo = *(CProgressInfo *)lpData;
UInt64 completed = progressInfo.StartPos + TotalBytesTransferred.QuadPart;
if (progressInfo.Progress->SetCompleted(&completed) != S_OK)
return PROGRESS_CANCEL;
return PROGRESS_CONTINUE;
}
typedef BOOL (WINAPI * CopyFileExPointer)(
IN LPCSTR lpExistingFileName,
IN LPCSTR lpNewFileName,
IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
IN LPVOID lpData OPTIONAL,
IN LPBOOL pbCancel OPTIONAL,
IN DWORD dwCopyFlags
);
typedef BOOL (WINAPI * CopyFileExPointerW)(
IN LPCWSTR lpExistingFileName,
IN LPCWSTR lpNewFileName,
IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
IN LPVOID lpData OPTIONAL,
IN LPBOOL pbCancel OPTIONAL,
IN DWORD dwCopyFlags
);
static bool FsCopyFile(CFSTR oldFile, CFSTR newFile, IProgress *progress, UInt64 &completedSize)
{
CProgressInfo progressInfo;
progressInfo.Progress = progress;
progressInfo.StartPos = completedSize;
BOOL CancelFlag = FALSE;
#ifndef _UNICODE
if (g_IsNT)
#endif
{
const wchar_t *k_DllName =
#ifdef UNDER_CE
L"coredll.dll"
#else
L"kernel32.dll"
#endif
;
CopyFileExPointerW copyFunctionW = (CopyFileExPointerW)
My_GetProcAddress(::GetModuleHandleW(k_DllName), "CopyFileExW");
IF_USE_MAIN_PATH_2(oldFile, newFile)
{
if (copyFunctionW == 0)
return BOOLToBool(::CopyFileW(fs2us(oldFile), fs2us(newFile), TRUE));
if (copyFunctionW(fs2us(oldFile), fs2us(newFile), CopyProgressRoutine,
&progressInfo, &CancelFlag, COPY_FILE_FAIL_IF_EXISTS))
return true;
}
#ifdef WIN_LONG_PATH
if (USE_SUPER_PATH_2)
{
UString longPathExisting, longPathNew;
if (!GetSuperPaths(oldFile, newFile, longPathExisting, longPathNew, USE_MAIN_PATH_2))
return false;
if (copyFunctionW(longPathExisting, longPathNew, CopyProgressRoutine,
&progressInfo, &CancelFlag, COPY_FILE_FAIL_IF_EXISTS))
return true;
}
#endif
return false;
}
#ifndef _UNICODE
else
{
CopyFileExPointer copyFunction = (CopyFileExPointer)
::GetProcAddress(::GetModuleHandleA("kernel32.dll"),
"CopyFileExA");
if (copyFunction != 0)
{
if (copyFunction(fs2fas(oldFile), fs2fas(newFile),
CopyProgressRoutine,&progressInfo, &CancelFlag, COPY_FILE_FAIL_IF_EXISTS))
return true;
if (::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
return false;
}
return BOOLToBool(::CopyFile(fs2fas(oldFile), fs2fas(newFile), TRUE));
}
#endif
}
typedef BOOL (WINAPI * MoveFileWithProgressPointer)(
IN LPCWSTR lpExistingFileName,
IN LPCWSTR lpNewFileName,
IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
IN LPVOID lpData OPTIONAL,
IN DWORD dwFlags
);
#ifdef UNDER_CE
#define NON_CE_VAR(_v_)
#else
#define NON_CE_VAR(_v_) _v_
#endif
static bool FsMoveFile(CFSTR oldFile, CFSTR newFile,
IProgress * NON_CE_VAR(progress),
UInt64 & NON_CE_VAR(completedSize))
{
#ifndef UNDER_CE
// if (IsItWindows2000orHigher())
// {
CProgressInfo progressInfo;
progressInfo.Progress = progress;
progressInfo.StartPos = completedSize;
MoveFileWithProgressPointer moveFunction = (MoveFileWithProgressPointer)
My_GetProcAddress(::GetModuleHandle(TEXT("kernel32.dll")),
"MoveFileWithProgressW");
if (moveFunction != 0)
{
IF_USE_MAIN_PATH_2(oldFile, newFile)
{
if (moveFunction(fs2us(oldFile), fs2us(newFile), CopyProgressRoutine,
&progressInfo, MOVEFILE_COPY_ALLOWED))
return true;
}
#ifdef WIN_LONG_PATH
if ((!(USE_MAIN_PATH_2) || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) && USE_SUPER_PATH_2)
{
UString longPathExisting, longPathNew;
if (!GetSuperPaths(oldFile, newFile, longPathExisting, longPathNew, USE_MAIN_PATH_2))
return false;
if (moveFunction(longPathExisting, longPathNew, CopyProgressRoutine,
&progressInfo, MOVEFILE_COPY_ALLOWED))
return true;
}
#endif
if (::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
return false;
}
// }
// else
#endif
return MyMoveFile(oldFile, newFile);
}
static HRESULT SendMessageError(IFolderOperationsExtractCallback *callback,
const wchar_t *message, const FString &fileName)
{
return callback->ShowMessage(message + fs2us(fileName));
}
static HRESULT SendMessageError(IFolderOperationsExtractCallback *callback,
const char *message, const FString &fileName)
{
return SendMessageError(callback, MultiByteToUnicodeString(message), fileName);
}
static HRESULT FsCopyFile(
const FString &srcPath,
const CFileInfo &srcFileInfo,
const FString &destPathSpec,
IFolderOperationsExtractCallback *callback,
UInt64 &completedSize)
{
FString destPath = destPathSpec;
if (CompareFileNames(destPath, srcPath) == 0)
{
RINOK(SendMessageError(callback, "can not copy file onto itself: ", destPath));
return E_ABORT;
}
Int32 writeAskResult;
CMyComBSTR destPathResult;
RINOK(callback->AskWrite(
fs2us(srcPath),
BoolToInt(false),
&srcFileInfo.MTime, &srcFileInfo.Size,
fs2us(destPath),
&destPathResult,
&writeAskResult));
if (IntToBool(writeAskResult))
{
FString destPathNew = us2fs(destPathResult);
RINOK(callback->SetCurrentFilePath(fs2us(srcPath)));
if (!FsCopyFile(srcPath, destPathNew, callback, completedSize))
{
RINOK(SendMessageError(callback, NError::MyFormatMessage(GetLastError()) + L" : ", destPathNew));
return E_ABORT;
}
}
completedSize += srcFileInfo.Size;
return callback->SetCompleted(&completedSize);
}
static FString CombinePath(const FString &folderPath, const FString &fileName)
{
return folderPath + FCHAR_PATH_SEPARATOR + fileName;
}
static bool IsDestChild(const FString &src, const FString &dest)
{
unsigned len = src.Len();
if (dest.Len() < len)
return false;
if (dest.Len() != len && dest[len] != FCHAR_PATH_SEPARATOR)
return false;
return CompareFileNames(dest.Left(len), src) == 0;
}
static HRESULT CopyFolder(
const FString &srcPath,
const FString &destPath,
IFolderOperationsExtractCallback *callback,
UInt64 &completedSize)
{
RINOK(callback->SetCompleted(&completedSize));
if (IsDestChild(srcPath, destPath))
{
RINOK(SendMessageError(callback, "can not copy folder onto itself: ", destPath));
return E_ABORT;
}
if (!CreateComplexDir(destPath))
{
RINOK(SendMessageError(callback, "can not create folder: ", destPath));
return E_ABORT;
}
CEnumerator enumerator(CombinePath(srcPath, FSTRING_ANY_MASK));
CDirItem fi;
while (enumerator.Next(fi))
{
const FString srcPath2 = CombinePath(srcPath, fi.Name);
const FString destPath2 = CombinePath(destPath, fi.Name);
if (fi.IsDir())
{
RINOK(CopyFolder(srcPath2, destPath2, callback, completedSize))
}
else
{
RINOK(FsCopyFile(srcPath2, fi, destPath2, callback, completedSize));
}
}
return S_OK;
}
/////////////////////////////////////////////////
// Move Operations
static HRESULT FsMoveFile(
const FString &srcPath,
const CFileInfo &srcFileInfo,
const FString &destPath,
IFolderOperationsExtractCallback *callback,
UInt64 &completedSize)
{
if (CompareFileNames(destPath, srcPath) == 0)
{
RINOK(SendMessageError(callback, "can not move file onto itself: ", srcPath));
return E_ABORT;
}
Int32 writeAskResult;
CMyComBSTR destPathResult;
RINOK(callback->AskWrite(
fs2us(srcPath),
BoolToInt(false),
&srcFileInfo.MTime, &srcFileInfo.Size,
fs2us(destPath),
&destPathResult,
&writeAskResult));
if (IntToBool(writeAskResult))
{
FString destPathNew = us2fs(destPathResult);
RINOK(callback->SetCurrentFilePath(fs2us(srcPath)));
if (!FsMoveFile(srcPath, destPathNew, callback, completedSize))
{
RINOK(SendMessageError(callback, "can not move to file: ", destPathNew));
}
}
completedSize += srcFileInfo.Size;
RINOK(callback->SetCompleted(&completedSize));
return S_OK;
}
static HRESULT FsMoveFolder(
const FString &srcPath,
const FString &destPath,
IFolderOperationsExtractCallback *callback,
UInt64 &completedSize)
{
if (IsDestChild(srcPath, destPath))
{
RINOK(SendMessageError(callback, "can not move folder onto itself: ", destPath));
return E_ABORT;
}
if (FsMoveFile(srcPath, destPath, callback, completedSize))
return S_OK;
if (!CreateComplexDir(destPath))
{
RINOK(SendMessageError(callback, "can not create folder: ", destPath));
return E_ABORT;
}
{
CEnumerator enumerator(CombinePath(srcPath, FSTRING_ANY_MASK));
CDirItem fi;
while (enumerator.Next(fi))
{
const FString srcPath2 = CombinePath(srcPath, fi.Name);
const FString destPath2 = CombinePath(destPath, fi.Name);
if (fi.IsDir())
{
RINOK(FsMoveFolder(srcPath2, destPath2, callback, completedSize));
}
else
{
RINOK(FsMoveFile(srcPath2, fi, destPath2, callback, completedSize));
}
}
}
if (!RemoveDir(srcPath))
{
RINOK(SendMessageError(callback, "can not remove folder: ", srcPath));
return E_ABORT;
}
return S_OK;
}
STDMETHODIMP CFSFolder::CopyTo(Int32 moveMode, const UInt32 *indices, UInt32 numItems,
Int32 /* includeAltStreams */, Int32 /* replaceAltStreamColon */,
const wchar_t *path, IFolderOperationsExtractCallback *callback)
{
if (numItems == 0)
return S_OK;
CFsFolderStat stat;
stat.Progress = callback;
RINOK(GetItemsFullSize(indices, numItems, stat));
RINOK(callback->SetTotal(stat.Size));
RINOK(callback->SetNumFiles(stat.NumFiles));
FString destPath = us2fs(path);
if (destPath.IsEmpty())
return E_INVALIDARG;
bool directName = (destPath.Back() != FCHAR_PATH_SEPARATOR);
if (directName)
{
if (numItems > 1)
return E_INVALIDARG;
}
else
{
// Does CreateComplexDir work in network ?
if (!CreateComplexDir(destPath))
{
RINOK(SendMessageError(callback, "can not create folder: ", destPath));
return E_ABORT;
}
}
UInt64 completedSize = 0;
RINOK(callback->SetCompleted(&completedSize));
for (UInt32 i = 0; i < numItems; i++)
{
UInt32 index = indices[i];
if (index >= (UInt32)Files.Size())
continue;
const CDirItem &fi = Files[index];
FString destPath2 = destPath;
if (!directName)
destPath2 += fi.Name;
FString srcPath;
GetFullPath(fi, srcPath);
if (fi.IsDir())
{
if (moveMode)
{
RINOK(FsMoveFolder(srcPath, destPath2, callback, completedSize));
}
else
{
RINOK(CopyFolder(srcPath, destPath2, callback, completedSize));
}
}
else
{
if (moveMode)
{
RINOK(FsMoveFile(srcPath, fi, destPath2, callback, completedSize));
}
else
{
RINOK(FsCopyFile(srcPath, fi, destPath2, callback, completedSize));
}
}
}
return S_OK;
}
STDMETHODIMP CFSFolder::CopyFrom(Int32 /* moveMode */, const wchar_t * /* fromFolderPath */,
const wchar_t ** /* itemsPaths */, UInt32 /* numItems */, IProgress * /* progress */)
{
/*
UInt64 numFolders, numFiles, totalSize;
numFiles = numFolders = totalSize = 0;
UInt32 i;
for (i = 0; i < numItems; i++)
{
UString path = (UString)fromFolderPath + itemsPaths[i];
CFileInfo fi;
if (!FindFile(path, fi))
return ::GetLastError();
if (fi.IsDir())
{
UInt64 subFolders, subFiles, subSize;
RINOK(GetFolderSize(CombinePath(path, fi.Name), subFolders, subFiles, subSize, progress));
numFolders += subFolders;
numFolders++;
numFiles += subFiles;
totalSize += subSize;
}
else
{
numFiles++;
totalSize += fi.Size;
}
}
RINOK(progress->SetTotal(totalSize));
RINOK(callback->SetNumFiles(numFiles));
for (i = 0; i < numItems; i++)
{
UString path = (UString)fromFolderPath + itemsPaths[i];
}
return S_OK;
*/
return E_NOTIMPL;
}
STDMETHODIMP CFSFolder::CopyFromFile(UInt32 /* index */, const wchar_t * /* fullFilePath */, IProgress * /* progress */)
{
return E_NOTIMPL;
}
}