From a2c905244b0e2c0b58026b70571b58f0d9646ccc Mon Sep 17 00:00:00 2001 From: Aric Stewart Date: Thu, 27 Feb 2014 20:09:11 +0900 Subject: [PATCH] shell32: Improve the FO_MOVE operation. --- dlls/shell32/shlfileop.c | 42 +++++++++---- dlls/shell32/tests/shlfileop.c | 105 ++++++++++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 11 deletions(-) diff --git a/dlls/shell32/shlfileop.c b/dlls/shell32/shlfileop.c index 4ef78034819..e6d44b0f4a7 100644 --- a/dlls/shell32/shlfileop.c +++ b/dlls/shell32/shlfileop.c @@ -53,6 +53,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell); #define FO_MASK 0xF +#define DE_SAMEFILE 0x71 +#define DE_DESTSAMETREE 0x7D + static const WCHAR wWildcardFile[] = {'*',0}; static const WCHAR wWildcardChars[] = {'*','?',0}; @@ -1428,11 +1431,15 @@ static void move_to_dir(LPSHFILEOPSTRUCTW lpFileOp, const FILE_ENTRY *feFrom, co static DWORD move_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom, const FILE_LIST *flTo) { DWORD i; + INT mismatched = 0; const FILE_ENTRY *entryToMove; const FILE_ENTRY *fileDest; - if (!flFrom->dwNumFiles || !flTo->dwNumFiles) - return ERROR_CANCELLED; + if (!flFrom->dwNumFiles) + return ERROR_SUCCESS; + + if (!flTo->dwNumFiles) + return ERROR_FILE_NOT_FOUND; if (!(lpFileOp->fFlags & FOF_MULTIDESTFILES) && flTo->dwNumFiles > 1 && flFrom->dwNumFiles > 1) @@ -1450,29 +1457,44 @@ static DWORD move_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom, con if (!PathFileExistsW(flTo->feFiles[0].szDirectory)) return ERROR_CANCELLED; - if ((lpFileOp->fFlags & FOF_MULTIDESTFILES) && - flFrom->dwNumFiles != flTo->dwNumFiles) - { - return ERROR_CANCELLED; - } + if (lpFileOp->fFlags & FOF_MULTIDESTFILES) + mismatched = flFrom->dwNumFiles - flTo->dwNumFiles; fileDest = &flTo->feFiles[0]; for (i = 0; i < flFrom->dwNumFiles; i++) { entryToMove = &flFrom->feFiles[i]; - if (lpFileOp->fFlags & FOF_MULTIDESTFILES) - fileDest = &flTo->feFiles[i]; - if (!PathFileExistsW(fileDest->szDirectory)) return ERROR_CANCELLED; + if (lpFileOp->fFlags & FOF_MULTIDESTFILES) + { + if (i >= flTo->dwNumFiles) + break; + fileDest = &flTo->feFiles[i]; + if (mismatched && !fileDest->bExists) + { + create_dest_dirs(flTo->feFiles[i].szFullPath); + flTo->feFiles[i].bExists = TRUE; + flTo->feFiles[i].attributes = FILE_ATTRIBUTE_DIRECTORY; + } + } + if (fileDest->bExists && IsAttribDir(fileDest->attributes)) move_to_dir(lpFileOp, entryToMove, fileDest); else SHNotifyMoveFileW(entryToMove->szFullPath, fileDest->szFullPath); } + if (mismatched > 0) + { + if (flFrom->bAnyDirectories) + return DE_DESTSAMETREE; + else + return DE_SAMEFILE; + } + return ERROR_SUCCESS; } diff --git a/dlls/shell32/tests/shlfileop.c b/dlls/shell32/tests/shlfileop.c index 14ace24afc8..64d0fbc9ea3 100644 --- a/dlls/shell32/tests/shlfileop.c +++ b/dlls/shell32/tests/shlfileop.c @@ -1871,7 +1871,8 @@ static void test_move(void) init_shfo_tests(); - /* number of sources do not correspond to number of targets */ + /* number of sources do not correspond to number of targets, + include directories */ set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0"); set_curr_dir_path(to, "test6.txt\0test7.txt\0"); retval = SHFileOperationA(&shfo2); @@ -1902,6 +1903,108 @@ static void test_move(void) "specified as a target\n"); } + init_shfo_tests(); + /* number of sources do not correspond to number of targets, + files only, + from exceeds to */ + set_curr_dir_path(from, "test1.txt\0test2.txt\0test3.txt\0"); + set_curr_dir_path(to, "test6.txt\0test7.txt\0"); + retval = SHFileOperationA(&shfo2); + if (dir_exists("test6.txt")) + { + if (retval == ERROR_SUCCESS) + { + /* Old shell32 */ + DeleteFileA("test6.txt\\test1.txt"); + DeleteFileA("test6.txt\\test2.txt"); + RemoveDirectoryA("test6.txt\\test4.txt"); + RemoveDirectoryA("test6.txt"); + } + else + { + /* Vista and W2K8 (broken or new behavior ?) */ + ok(retval == DE_SAMEFILE, "Expected DE_SAMEFILE, got %d\n", retval); + ok(DeleteFileA("test6.txt\\test1.txt"), "The file is not moved\n"); + RemoveDirectoryA("test6.txt"); + ok(DeleteFileA("test7.txt\\test2.txt"), "The file is not moved\n"); + RemoveDirectoryA("test7.txt"); + ok(file_exists("test3.txt"), "File should not be moved\n"); + } + } + else + { + expect_retval(ERROR_CANCELLED, DE_OPCANCELLED /* Win9x, NT4 */); + ok(!file_exists("test6.txt"), "The file is not moved - many files are " + "specified as a target\n"); + } + + init_shfo_tests(); + /* number of sources do not correspond to number of targets, + files only, + too exceeds from */ + set_curr_dir_path(from, "test1.txt\0test2.txt\0"); + set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0"); + retval = SHFileOperationA(&shfo2); + if (dir_exists("test6.txt")) + { + ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval); + ok(DeleteFileA("test6.txt\\test1.txt"),"The file is not moved\n"); + ok(DeleteFileA("test7.txt\\test2.txt"),"The file is not moved\n"); + ok(!dir_exists("test8.txt") && !file_exists("test8.txt"), + "Directory should not be created\n"); + RemoveDirectoryA("test6.txt"); + RemoveDirectoryA("test7.txt"); + } + else + { + expect_retval(ERROR_CANCELLED, DE_OPCANCELLED /* WinXp, Win2k */); + ok(!file_exists("test6.txt"), "The file is not moved - many files are " + "specified as a target\n"); + } + + init_shfo_tests(); + /* number of sources do not correspond to number of targets, + target directories */ + set_curr_dir_path(from, "test1.txt\0test2.txt\0test3.txt\0"); + set_curr_dir_path(to, "test4.txt\0test5.txt\0"); + retval = SHFileOperationA(&shfo2); + if (dir_exists("test5.txt")) + { + ok(retval == DE_SAMEFILE, "Expected DE_SAMEFILE, got %d\n", retval); + ok(DeleteFileA("test4.txt\\test1.txt"),"The file is not moved\n"); + ok(DeleteFileA("test5.txt\\test2.txt"),"The file is not moved\n"); + ok(file_exists("test3.txt"), "The file is not moved\n"); + RemoveDirectoryA("test4.txt"); + RemoveDirectoryA("test5.txt"); + } + else + { + ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval); + ok(DeleteFileA("test4.txt\\test1.txt"),"The file is not moved\n"); + ok(DeleteFileA("test4.txt\\test2.txt"),"The file is not moved\n"); + ok(DeleteFileA("test4.txt\\test3.txt"),"The file is not moved\n"); + } + + + init_shfo_tests(); + /* 0 incomming files */ + set_curr_dir_path(from, "\0\0"); + set_curr_dir_path(to, "test6.txt\0\0"); + retval = SHFileOperationA(&shfo2); + ok(retval == ERROR_SUCCESS || retval == ERROR_ACCESS_DENIED + , "Expected ERROR_SUCCESS || ERROR_ACCESS_DENIED, got %d\n", retval); + ok(!file_exists("test6.txt"), "The file should not exist\n"); + + init_shfo_tests(); + /* 0 outgoing files */ + set_curr_dir_path(from, "test1\0\0"); + set_curr_dir_path(to, "\0\0"); + retval = SHFileOperationA(&shfo2); + ok(retval == ERROR_FILE_NOT_FOUND || + broken(retval == 1026) + , "Expected ERROR_FILE_NOT_FOUND, got %d\n", retval); + ok(!file_exists("test6.txt"), "The file should not exist\n"); + init_shfo_tests(); set_curr_dir_path(from, "test3.txt\0");