Hi,
This patch is untested. It breaks up SHFileOperation into two functions and reformats it somewhat. Anybody feel like testing it for me??? ;)
Mike
ChangeLog: * Refactoring of SHFileOperation
Index: dlls/shell32/shlfileop.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/shlfileop.c,v retrieving revision 1.31 diff -u -r1.31 shlfileop.c --- dlls/shell32/shlfileop.c 21 Aug 2003 21:26:23 -0000 1.31 +++ dlls/shell32/shlfileop.c 26 Aug 2003 14:57:51 -0000 @@ -761,489 +761,470 @@ return wine_dbg_sprintf("%s", cFO_Name[ op ]); } -/************************************************************************* - * SHFileOperationW [SHELL32.@] - * - * See SHFileOperationA - */ -DWORD WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp) + +/*#define W98_FO_RENEME */ +#define HIGH_ADR (LPWSTR)0xffffffff + +static DWORD do_FileOp( SHFILEOPSTRUCTW *nFileOp, long FuncSwitch, long level, + BOOL ask_overwrite, BOOL not_overwrite, BOOL b_Multi, BOOL b_MultiTo, + BOOL b_MultiFrom, BOOL b_ToInvalidTail, BOOL b_ToTailSlash, + LPCWSTR pFrom, LPWSTR pTempFrom, LPWSTR pFromFile, + LPCWSTR pTo, LPWSTR pTempTo, LPWSTR pToFile ) { - SHFILEOPSTRUCTW nFileOp = *(lpFileOp); + WIN32_FIND_DATAW wfd; + BOOL b_Mask, b_SameRoot, b_SameTailName; + BOOL b_ToValid; /* for W98-Bug for FO_MOVE with source and target in same rootdrive */ + DWORD retCode = 0, ToAttr, ToPathAttr, FromPathAttr; + HANDLE hFind = INVALID_HANDLE_VALUE; + LPWSTR lpFileName; + + /* for all */ + b_Mask = (NULL != StrPBrkW(&pFromFile[1], wWildcardChars)); + if (FO_RENAME == FuncSwitch) + { + /* temporary only for FO_RENAME */ + /* ??? b_Mask = (NULL != strrbrk(pFrom,"*?")); */ + if (b_MultiTo || b_MultiFrom || (b_Mask && !b_ToInvalidTail)) + { + /* no work, only RC=0 */ + /* ??? nFileOp->fAnyOperationsAborted = TRUE; */ +#ifdef W98_FO_RENEME + return 0x80000000 +#endif + return 0x1; /* 1 value unknown, W98 returns no error */ + } + } - LPCWSTR pNextFrom = nFileOp.pFrom; - LPCWSTR pNextTo = nFileOp.pTo; - LPCWSTR pFrom = pNextFrom; - LPCWSTR pTo = NULL; - HANDLE hFind = INVALID_HANDLE_VALUE; - WIN32_FIND_DATAW wfd; - LPWSTR pTempFrom = NULL; - LPWSTR pTempTo = NULL; - LPWSTR pFromFile; - LPWSTR pToFile = NULL; - LPWSTR lpFileName; - long retCode = 0; - DWORD ToAttr; - DWORD ToPathAttr; - DWORD FromPathAttr; - FILEOP_FLAGS OFl = ((FILEOP_FLAGS)lpFileOp->fFlags & 0xfff); - - BOOL b_Multi = (nFileOp.fFlags & FOF_MULTIDESTFILES); - - BOOL b_MultiTo = (FO_DELETE != (lpFileOp->wFunc & FO_MASK)); - BOOL b_MultiPaired = (!b_MultiTo); - BOOL b_MultiFrom = FALSE; - BOOL not_overwrite; - BOOL ask_overwrite; - BOOL b_SameRoot; - BOOL b_SameTailName; - BOOL b_ToInvalidTail = FALSE; - BOOL b_ToValid; /* for W98-Bug for FO_MOVE with source and target in same rootdrive */ - BOOL b_Mask; - BOOL b_ToTailSlash = FALSE; - - long FuncSwitch = (nFileOp.wFunc & FO_MASK); - long level= nFileOp.wFunc>>4; - - /* default no error */ - nFileOp.fAnyOperationsAborted = FALSE; - - if ((FuncSwitch < FO_MOVE) || (FuncSwitch > FO_RENAME)) - goto shfileop_normal; /* no valid FunctionCode */ - - if (level == 0) - TRACE("%s: flags (0x%04x) : %s\n", - debug_shfileops_action(FuncSwitch), nFileOp.fFlags, - debug_shfileops_flags(nFileOp.fFlags) ); - - /* establish when pTo is interpreted as the name of the destination file - * or the directory where the Fromfile should be copied to. - * This depends on: - * (1) pTo points to the name of an existing directory; - * (2) the flag FOF_MULTIDESTFILES is present; - * (3) whether pFrom point to multiple filenames. - * - * Some experiments: - * - * destisdir 1 1 1 1 0 0 0 0 - * FOF_MULTIDESTFILES 1 1 0 0 1 1 0 0 - * multiple from filenames 1 0 1 0 1 0 1 0 - * --------------- - * copy files to dir 1 0 1 1 0 0 1 0 - * create dir 0 0 0 0 0 0 1 0 - */ -/* - * FOF_MULTIDESTFILES, FOF_NOCONFIRMATION, FOF_FILESONLY are implemented - * FOF_CONFIRMMOUSE, FOF_SILENT, FOF_NOCONFIRMMKDIR, - * FOF_SIMPLEPROGRESS, FOF_NOCOPYSECURITYATTRIBS are not implemented and ignored - * FOF_RENAMEONCOLLISION are implemented partially and breaks if file exist - * FOF_ALLOWUNDO, FOF_WANTMAPPINGHANDLE are not implemented and breaks - * if any other flag set, an error occurs - */ - TRACE("%s level=%ld nFileOp.fFlags=0x%x\n", - debug_shfileops_action(FuncSwitch), level, lpFileOp->fFlags); + hFind = FindFirstFileW(pFrom, &wfd); + if (INVALID_HANDLE_VALUE == hFind) + { + if ((FO_DELETE == FuncSwitch) && (b_Mask)) + { + pFromFile[0] = '\0'; + FromPathAttr = GetFileAttributesW(pTempFrom); + pFromFile[0] = '\\'; + if (IsAttribDir(FromPathAttr)) + { + /* FO_DELETE with mask and without found is valid */ + return 0x80000000; + } + } + /* root (without mask) is also not allowed as source, tested in W98 */ + return 0x402; /* 1026 */ + } + + /* + * ??? b_Mask = (!SHFileStrICmpA(&pFromFile[1], + * &wfd.cFileName[0], HIGH_ADR, HIGH_ADR)); + */ + + if (!pTo) /* FO_DELETE */ + { + do + { + lpFileName = wfd.cAlternateFileName; + if (!lpFileName[0]) + lpFileName = wfd.cFileName; + if (IsDotDir(lpFileName) || + ((b_Mask) && IsAttribDir(wfd.dwFileAttributes) && + (nFileOp->fFlags & FOF_FILESONLY))) + continue; + SHFileStrCpyCatW(&pFromFile[1], lpFileName, NULL); + /* TODO: Check the SHELL_DeleteFileOrDirectoryW() function in shell32.dll */ + if (IsAttribFile(wfd.dwFileAttributes)) + { + nFileOp->fAnyOperationsAborted = (!SHNotifyDeleteFileW(pTempFrom)); + retCode = 0x78; /* value unknown */ + } + else + { + nFileOp->fAnyOperationsAborted = !SHELL_DeleteDirectoryW( + pTempFrom, (!(nFileOp->fFlags & FOF_NOCONFIRMATION))); + retCode = 0x79; /* value unknown */ + } + } while (!nFileOp->fAnyOperationsAborted && FindNextFileW(hFind, &wfd)); + FindClose(hFind); + return retCode; + } /* FO_DELETE ends, pTo must be always valid from here */ + + b_SameRoot = (toupperW(pTempFrom[0]) == toupperW(pTempTo[0])); + b_SameTailName = SHFileStrICmpW(pToFile, pFromFile, NULL, NULL); + + ToPathAttr = ToAttr = GetFileAttributesW(pTempTo); + if (!b_Mask && (ToAttr == -1) && (pToFile)) + { + pToFile[0] = '\0'; + ToPathAttr = GetFileAttributesW(pTempTo); + pToFile[0] = '\\'; + } + + if (FO_RENAME == FuncSwitch) + { + FindClose(hFind); + if (!b_SameRoot || b_Mask /* FO_RENAME works not with Mask */ + || !SHFileStrICmpW(pTempFrom, pTempTo, pFromFile, NULL) + || (SHFileStrICmpW(pTempFrom, pTempTo, pFromFile, HIGH_ADR) && !b_ToTailSlash)) + return 0x73; + if (b_ToInvalidTail) + return 0x2; + if (-1 == ToPathAttr) + return 0x75; + if (IsAttribDir(wfd.dwFileAttributes) && IsAttribDir(ToAttr)) + return (b_ToTailSlash) ? 0xb7 : 0x7b; + /* we use SHNotifyMoveFile() instead MoveFileW */ + if (!SHNotifyMoveFileW(pTempFrom, pTempTo)) + { + /* we need still the value for the returncode, we use the mostly assumed */ + return 0xb7; + } + return 0x80000000; + } -/* OFl &= (-1 - (FOF_MULTIDESTFILES | FOF_FILESONLY)); */ -/* OFl ^= (FOF_SILENT | FOF_NOCONFIRMATION | FOF_SIMPLEPROGRESS | FOF_NOCONFIRMMKDIR); */ - OFl &= (~(FOF_MULTIDESTFILES | FOF_NOCONFIRMATION | FOF_FILESONLY)); /* implemented */ - OFl ^= (FOF_SILENT | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_NOCOPYSECURITYATTRIBS); /* ignored, if one */ - OFl &= (~FOF_SIMPLEPROGRESS); /* ignored, only with FOF_SILENT */ - if (OFl) - { - if (OFl & (~(FOF_CONFIRMMOUSE | FOF_SILENT | FOF_RENAMEONCOLLISION | - FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_NOCOPYSECURITYATTRIBS))) - { - TRACE("%s level=%ld lpFileOp->fFlags=0x%x not implemented, Aborted=TRUE, stub\n", - debug_shfileops_action(FuncSwitch), level, OFl); - retCode = 0x403; /* 1027, we need an extension to shlfileop */ - goto shfileop_error; - } - else - { - TRACE("%s level=%ld lpFileOp->fFlags=0x%x not fully implemented, stub\n", - debug_shfileops_action(FuncSwitch), level, OFl); - } - } + /* W98 Bug with FO_MOVE different to FO_COPY, better the same as FO_COPY */ + b_ToValid = ((b_SameTailName && b_SameRoot && (FO_COPY == FuncSwitch)) || + (b_SameTailName && !b_SameRoot) || (b_ToInvalidTail)); + + /* handle mask in source */ + if (b_Mask) + { + if (!IsAttribDir(ToAttr)) + return (b_ToInvalidTail && (FO_MOVE == FuncSwitch)) ? 0x2 : 0x75; + pToFile = SHFileStrCpyCatW(pTempTo, NULL, wBackslash); + nFileOp->fFlags = (nFileOp->fFlags | FOF_MULTIDESTFILES); + do + { + lpFileName = wfd.cAlternateFileName; + if (!lpFileName[0]) + lpFileName = wfd.cFileName; + if (IsDotDir(lpFileName) || + (IsAttribDir(wfd.dwFileAttributes) && (nFileOp->fFlags & FOF_FILESONLY))) + continue; /* next name in pTempFrom(dir) */ + SHFileStrCpyCatW(&pToFile[1], lpFileName, NULL); + SHFileStrCpyCatW(&pFromFile[1], lpFileName, NULL); + retCode = SHFileOperationW (nFileOp); + } while(!nFileOp->fAnyOperationsAborted && FindNextFileW(hFind, &wfd)); + } + FindClose(hFind); + /* FO_COPY/FO_MOVE with mask, FO_DELETE and FO_RENAME are solved */ + if (b_Mask) + return retCode; + + /* only FO_COPY/FO_MOVE without mask, all others are (must be) solved */ + if (IsAttribDir(wfd.dwFileAttributes) && (ToAttr == -1)) + { + if (pToFile) + { + pToFile[0] = '\0'; + ToPathAttr = GetFileAttributesW(pTempTo); + if ((ToPathAttr == -1) && b_ToValid) + { + /* create dir must be here, sample target D:\y\ *.* create with RC=10003 */ + if (SHCreateDirectoryExW(NULL, pTempTo, NULL)) + return 0x73;/* value unknown */ + ToPathAttr = GetFileAttributesW(pTempTo); + } + pToFile[0] = '\\'; + if (b_ToInvalidTail) + return 0x10003; + } + } - if ((pNextFrom) && (!(b_MultiTo) || (pNextTo))) + /* trailing BackSlash is ever removed and pToFile points to BackSlash before */ + if (!b_MultiTo && (b_MultiFrom || (!(b_Multi) && IsAttribDir(ToAttr)))) + { + if ( (FO_MOVE == FuncSwitch) && IsAttribDir(ToAttr) && + IsAttribDir(wfd.dwFileAttributes)) { - nFileOp.pFrom = pTempFrom = HeapAlloc(GetProcessHeap(), 0, ((1 + 2 * (b_MultiTo)) * MAX_PATH + 6) * sizeof(WCHAR)); - if (!pTempFrom) - { - retCode = ERROR_OUTOFMEMORY; - SetLastError(retCode); - goto shfileop_error; - } - if (b_MultiTo) - pTempTo = &pTempFrom[MAX_PATH + 4]; - nFileOp.pTo = pTempTo; - ask_overwrite = (!(nFileOp.fFlags & FOF_NOCONFIRMATION) && !(nFileOp.fFlags & FOF_RENAMEONCOLLISION)); - not_overwrite = (!(nFileOp.fFlags & FOF_NOCONFIRMATION) || (nFileOp.fFlags & FOF_RENAMEONCOLLISION)); + if (b_Multi) + return 0x73; /* !b_Multi = 0x8 ?? */ + } + pToFile = SHFileStrCpyCatW(pTempTo, NULL, wfd.cFileName); + ToAttr = GetFileAttributesW(pTempTo); + } + + if (IsAttribDir(ToAttr)) + { + if (IsAttribFile(wfd.dwFileAttributes)) + return (FO_COPY == FuncSwitch) ? 0x75 : 0xb7; + } + else + { + pToFile[0] = '\0'; + ToPathAttr = GetFileAttributesW(pTempTo); + pToFile[0] = '\\'; + if (IsAttribFile(ToPathAttr)) + return 0x777402; /* error, is this tested ? */ + } + + /* singlesource + no mask */ + if (-1 == (ToAttr & ToPathAttr)) + return 0x75; /* Target-dir does not exist, and cannot be created */ + + switch(FuncSwitch) + { + case FO_MOVE: + pToFile = NULL; + if ((ToAttr == -1) && SHFileStrICmpW(pTempFrom, pTempTo, pFromFile, NULL)) + { + nFileOp->wFunc = ((level+1)<<4) + FO_RENAME; } else { - retCode = 0x402; /* 1026 */ - goto shfileop_error; + if (b_SameRoot && IsAttribDir(ToAttr) && IsAttribDir(wfd.dwFileAttributes)) + { + /* we need pToFile for FO_DELETE after FO_MOVE contence */ + pToFile = SHFileStrCpyCatW(pTempFrom, NULL, wWildcardFile); + } + else + { + nFileOp->wFunc = ((level+1)<<4) + FO_COPY; + } } - /* need break at error before change sourcepointer */ - while(!nFileOp.fAnyOperationsAborted && (pNextFrom[0])) + retCode = SHFileOperationW(nFileOp); + if (pToFile) + ((DWORD*)pToFile)[0] = '\0'; + if (!nFileOp->fAnyOperationsAborted && (FO_RENAME != (nFileOp->wFunc & 0xf))) { - nFileOp.wFunc = ((level + 1) << 4) + FuncSwitch; - nFileOp.fFlags = lpFileOp->fFlags; - - if (b_MultiTo) - { - pTo = pNextTo; - pNextTo = &pNextTo[lstrlenW(pTo)+1]; - b_MultiTo = (b_Multi && pNextTo[0]); - } - - pFrom = pNextFrom; - pNextFrom = &pNextFrom[lstrlenW(pNextFrom)+1]; - if (!b_MultiFrom && !b_MultiTo) - b_MultiFrom = (pNextFrom[0]); - - pFromFile = SHFileStrCpyCatW(pTempFrom, pFrom, NULL); - - if (pTo) - { - pToFile = SHFileStrCpyCatW(pTempTo, pTo, NULL); - } - if (!b_MultiPaired) - { - b_MultiPaired = - SHELL_FileNamesMatch(lpFileOp->pFrom, lpFileOp->pTo, (!b_Multi || b_MultiFrom)); - } - if (!(b_MultiPaired) || !(pFromFile) || !(pFromFile[1]) || ((pTo) && !(pToFile))) - { - retCode = 0x402; /* 1026 */ - goto shfileop_error; - } - if (pTo) - { - b_ToTailSlash = (!pToFile[1]); - if (b_ToTailSlash) - { - pToFile[0] = '\0'; - if (StrChrW(pTempTo,'\\')) - { - pToFile = SHFileStrCpyCatW(pTempTo, NULL, NULL); - } - } - b_ToInvalidTail = (NULL != StrPBrkW(&pToFile[1], wWildcardChars)); - } - - /* for all */ - b_Mask = (NULL != StrPBrkW(&pFromFile[1], wWildcardChars)); - if (FO_RENAME == FuncSwitch) - { - /* temporary only for FO_RENAME */ -/* ??? b_Mask = (NULL != strrbrk(pFrom,"*?")); */ - if (b_MultiTo || b_MultiFrom || (b_Mask && !b_ToInvalidTail)) - { - /* no work, only RC=0 */ -/* ??? nFileOp.fAnyOperationsAborted = TRUE; */ -/*#define W98_FO_RENEME */ -#ifdef W98_FO_RENEME - goto shfileop_normal; -#endif - retCode = 0x1; /* 1 value unknown, W98 returns no error */ - goto shfileop_error; - } - } + nFileOp->wFunc = ((level+1)<<4) + FO_DELETE; + return SHFileOperationW(nFileOp); + } + return retCode; - hFind = FindFirstFileW(pFrom, &wfd); - if (INVALID_HANDLE_VALUE == hFind) - { - if ((FO_DELETE == FuncSwitch) && (b_Mask)) - { - pFromFile[0] = '\0'; - FromPathAttr = GetFileAttributesW(pTempFrom); - pFromFile[0] = '\\'; - if (IsAttribDir(FromPathAttr)) - { - /* FO_DELETE with mask and without found is valid */ - goto shfileop_normal; - } - } - /* root (without mask) is also not allowed as source, tested in W98 */ - retCode = 0x402; /* 1026 */ - goto shfileop_error; - } + case FO_COPY: + if (SHFileStrICmpW(pTempFrom, pTempTo, NULL, NULL)) + /* target is the same as source ? */ + /* we still need the value for the returncode, we assume 0x71 */ + return 0x71; -/* for all */ -#define HIGH_ADR (LPWSTR)0xffffffff + if (IsAttribDir((ToAttr & wfd.dwFileAttributes))) + { + if (IsAttribDir(ToAttr) || !SHCreateDirectoryExW(NULL,pTempTo, NULL)) + { + /* ??? nFileOp->fFlags = (nFileOp->fFlags | FOF_MULTIDESTFILES); */ + SHFileStrCpyCatW(pTempFrom, NULL, wWildcardFile); + return SHFileOperationW(nFileOp); + } + else + return 0x750;/* value unknown */ + } + else + { + if (!(ask_overwrite && SHELL_ConfirmDialogW(ASK_OVERWRITE_FILE, pTempTo)) + && (not_overwrite)) + /* we still need the value for the returncode, we use the mostly assumed */ + return 0x73; + + if (!(SHNotifyCopyFileW(pTempFrom, pTempTo, + nFileOp->fFlags & FOF_RENAMEONCOLLISION))) + return 0x77; /* value unknown */ + } + } + return retCode; +} -/* ??? b_Mask = (!SHFileStrICmpA(&pFromFile[1], &wfd.cFileName[0], HIGH_ADR, HIGH_ADR)); */ - if (!pTo) /* FO_DELETE */ - { - do - { - lpFileName = wfd.cAlternateFileName; - if (!lpFileName[0]) - lpFileName = wfd.cFileName; - if (IsDotDir(lpFileName) || - ((b_Mask) && IsAttribDir(wfd.dwFileAttributes) && (nFileOp.fFlags & FOF_FILESONLY))) - continue; - SHFileStrCpyCatW(&pFromFile[1], lpFileName, NULL); - /* TODO: Check the SHELL_DeleteFileOrDirectoryW() function in shell32.dll */ - if (IsAttribFile(wfd.dwFileAttributes)) - { - nFileOp.fAnyOperationsAborted = (!SHNotifyDeleteFileW(pTempFrom)); - retCode = 0x78; /* value unknown */ - } - else - { - nFileOp.fAnyOperationsAborted = (!SHELL_DeleteDirectoryW(pTempFrom, (!(nFileOp.fFlags & FOF_NOCONFIRMATION)))); - retCode = 0x79; /* value unknown */ - } - } while (!nFileOp.fAnyOperationsAborted && FindNextFileW(hFind, &wfd)); - FindClose(hFind); - hFind = INVALID_HANDLE_VALUE; - if (nFileOp.fAnyOperationsAborted) - { - goto shfileop_error; - } - continue; - } /* FO_DELETE ends, pTo must be always valid from here */ +/************************************************************************* + * SHFileOperationW [SHELL32.@] + * + * See SHFileOperationA + */ +DWORD WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp) +{ + SHFILEOPSTRUCTW nFileOp = *(lpFileOp); - b_SameRoot = (toupperW(pTempFrom[0]) == toupperW(pTempTo[0])); - b_SameTailName = SHFileStrICmpW(pToFile, pFromFile, NULL, NULL); + LPCWSTR pNextFrom = nFileOp.pFrom; + LPCWSTR pNextTo = nFileOp.pTo; + LPCWSTR pFrom = pNextFrom; + LPCWSTR pTo = NULL; + LPWSTR pTempFrom = NULL, pTempTo = NULL, pFromFile, pToFile = NULL; + long retCode = 0; + FILEOP_FLAGS OFl = ((FILEOP_FLAGS)lpFileOp->fFlags & 0xfff); + + BOOL b_Multi = (nFileOp.fFlags & FOF_MULTIDESTFILES); + BOOL b_MultiTo = (FO_DELETE != (lpFileOp->wFunc & FO_MASK)); + BOOL b_MultiPaired = (!b_MultiTo); + BOOL not_overwrite, ask_overwrite; + BOOL b_ToInvalidTail = FALSE, b_ToTailSlash = FALSE, b_MultiFrom = FALSE; + + long FuncSwitch = (nFileOp.wFunc & FO_MASK); + long level= nFileOp.wFunc>>4; + + /* default no error */ + nFileOp.fAnyOperationsAborted = FALSE; + + if ((FuncSwitch < FO_MOVE) || (FuncSwitch > FO_RENAME)) + return 0x80000000; /* no valid FunctionCode */ + + if (level == 0) + TRACE("%s: flags (0x%04x) : %s\n", + debug_shfileops_action(FuncSwitch), nFileOp.fFlags, + debug_shfileops_flags(nFileOp.fFlags) ); + + /* establish when pTo is interpreted as the name of the destination file + * or the directory where the Fromfile should be copied to. + * This depends on: + * (1) pTo points to the name of an existing directory; + * (2) the flag FOF_MULTIDESTFILES is present; + * (3) whether pFrom point to multiple filenames. + * + * Some experiments: + * + * destisdir 1 1 1 1 0 0 0 0 + * FOF_MULTIDESTFILES 1 1 0 0 1 1 0 0 + * multiple from filenames 1 0 1 0 1 0 1 0 + * --------------- + * copy files to dir 1 0 1 1 0 0 1 0 + * create dir 0 0 0 0 0 0 1 0 + * + * Implemented flags: + * FOF_MULTIDESTFILES, FOF_NOCONFIRMATION, FOF_FILESONLY + * + * Partially implemented flag (breaks if file exist): + * FOF_RENAMEONCOLLISION + * + * Unimplemented flags (ignored): + * FOF_CONFIRMMOUSE, FOF_SILENT, FOF_NOCONFIRMMKDIR, + * FOF_SIMPLEPROGRESS, FOF_NOCOPYSECURITYATTRIBS + * + * Unimplemented flags: + * FOF_ALLOWUNDO, FOF_WANTMAPPINGHANDLE + * + * if any other flag set, an error occurs + */ + + TRACE("%s level=%ld nFileOp.fFlags=0x%x\n", + debug_shfileops_action(FuncSwitch), level, lpFileOp->fFlags); + + /* OFl &= (-1 - (FOF_MULTIDESTFILES | FOF_FILESONLY)); */ + /* + * OFl ^= ( FOF_SILENT | FOF_NOCONFIRMATION | + * FOF_SIMPLEPROGRESS | FOF_NOCONFIRMMKDIR); + */ + OFl &= ~( FOF_MULTIDESTFILES | FOF_NOCONFIRMATION | + FOF_FILESONLY); /* implemented */ + OFl ^= ( FOF_SILENT | FOF_NOCONFIRMMKDIR | + FOF_NOERRORUI | FOF_NOCOPYSECURITYATTRIBS); /* ignored, if one */ + OFl &= (~FOF_SIMPLEPROGRESS); /* ignored, only with FOF_SILENT */ + if (OFl) + { + if (OFl & (~(FOF_CONFIRMMOUSE | FOF_SILENT | FOF_RENAMEONCOLLISION | + FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_NOCOPYSECURITYATTRIBS))) + { + TRACE("%s level=%ld lpFileOp->fFlags=0x%x not implemented, Aborted=TRUE, stub\n", + debug_shfileops_action(FuncSwitch), level, OFl); + retCode = 0x403; /* 1027, we need an extension to shlfileop */ + goto shfileop_error; + } + else + { + TRACE("%s level=%ld lpFileOp->fFlags=0x%x not fully implemented, stub\n", + debug_shfileops_action(FuncSwitch), level, OFl); + } + } - ToPathAttr = ToAttr = GetFileAttributesW(pTempTo); - if (!b_Mask && (ToAttr == -1) && (pToFile)) - { - pToFile[0] = '\0'; - ToPathAttr = GetFileAttributesW(pTempTo); - pToFile[0] = '\\'; - } + if ((pNextFrom) && (!(b_MultiTo) || (pNextTo))) + { + pTempFrom = HeapAlloc(GetProcessHeap(), 0, + ((1 + 2 * (b_MultiTo)) * MAX_PATH + 6) * sizeof(WCHAR)); + nFileOp.pFrom = pTempFrom; + if (!pTempFrom) + { + retCode = ERROR_OUTOFMEMORY; + SetLastError(retCode); + goto shfileop_error; + } + if (b_MultiTo) + pTempTo = &pTempFrom[MAX_PATH + 4]; + nFileOp.pTo = pTempTo; + ask_overwrite = (!(nFileOp.fFlags & FOF_NOCONFIRMATION) && + !(nFileOp.fFlags & FOF_RENAMEONCOLLISION)); + not_overwrite = (!(nFileOp.fFlags & FOF_NOCONFIRMATION) || + (nFileOp.fFlags & FOF_RENAMEONCOLLISION)); + } + else + { + retCode = 0x402; /* 1026 */ + goto shfileop_error; + } + /* need break at error before change sourcepointer */ + while(!nFileOp.fAnyOperationsAborted && (pNextFrom[0])) + { + nFileOp.wFunc = ((level + 1) << 4) + FuncSwitch; + nFileOp.fFlags = lpFileOp->fFlags; - if (FO_RENAME == FuncSwitch) - { - if (!b_SameRoot || b_Mask /* FO_RENAME works not with Mask */ - || !SHFileStrICmpW(pTempFrom, pTempTo, pFromFile, NULL) - || (SHFileStrICmpW(pTempFrom, pTempTo, pFromFile, HIGH_ADR) && !b_ToTailSlash)) - { - retCode = 0x73; - goto shfileop_error; - } - if (b_ToInvalidTail) - { - retCode=0x2; - goto shfileop_error; - } - if (-1 == ToPathAttr) - { - retCode = 0x75; - goto shfileop_error; - } - if (IsAttribDir(wfd.dwFileAttributes) && IsAttribDir(ToAttr)) - { - retCode = (b_ToTailSlash) ? 0xb7 : 0x7b; - goto shfileop_error; - } - /* we use SHNotifyMoveFile() instead MoveFileW */ - if (!SHNotifyMoveFileW(pTempFrom, pTempTo)) - { - /* we need still the value for the returncode, we use the mostly assumed */ - retCode = 0xb7; - goto shfileop_error; - } - goto shfileop_normal; - } + if (b_MultiTo) + { + pTo = pNextTo; + pNextTo = &pNextTo[lstrlenW(pTo)+1]; + b_MultiTo = (b_Multi && pNextTo[0]); + } - /* W98 Bug with FO_MOVE different to FO_COPY, better the same as FO_COPY */ - b_ToValid = ((b_SameTailName && b_SameRoot && (FO_COPY == FuncSwitch)) || - (b_SameTailName && !b_SameRoot) || (b_ToInvalidTail)); - - /* handle mask in source */ - if (b_Mask) - { - if (!IsAttribDir(ToAttr)) - { - retCode = (b_ToInvalidTail &&/* b_SameTailName &&*/ (FO_MOVE == FuncSwitch)) \ - ? 0x2 : 0x75; - goto shfileop_error; - } - pToFile = SHFileStrCpyCatW(pTempTo, NULL, wBackslash); - nFileOp.fFlags = (nFileOp.fFlags | FOF_MULTIDESTFILES); - do - { - lpFileName = wfd.cAlternateFileName; - if (!lpFileName[0]) - lpFileName = wfd.cFileName; - if (IsDotDir(lpFileName) || - (IsAttribDir(wfd.dwFileAttributes) && (nFileOp.fFlags & FOF_FILESONLY))) - continue; /* next name in pTempFrom(dir) */ - SHFileStrCpyCatW(&pToFile[1], lpFileName, NULL); - SHFileStrCpyCatW(&pFromFile[1], lpFileName, NULL); - retCode = SHFileOperationW (&nFileOp); - } while(!nFileOp.fAnyOperationsAborted && FindNextFileW(hFind, &wfd)); - } - FindClose(hFind); - hFind = INVALID_HANDLE_VALUE; - /* FO_COPY/FO_MOVE with mask, FO_DELETE and FO_RENAME are solved */ - if (b_Mask) - continue; + pFrom = pNextFrom; + pNextFrom = &pNextFrom[lstrlenW(pNextFrom)+1]; + if (!b_MultiFrom && !b_MultiTo) + b_MultiFrom = (pNextFrom[0]); - /* only FO_COPY/FO_MOVE without mask, all others are (must be) solved */ - if (IsAttribDir(wfd.dwFileAttributes) && (ToAttr == -1)) - { - if (pToFile) - { - pToFile[0] = '\0'; - ToPathAttr = GetFileAttributesW(pTempTo); - if ((ToPathAttr == -1) && b_ToValid) - { - /* create dir must be here, sample target D:\y\ *.* create with RC=10003 */ - if (SHCreateDirectoryExW(NULL, pTempTo, NULL)) - { - retCode = 0x73;/* value unknown */ - goto shfileop_error; - } - ToPathAttr = GetFileAttributesW(pTempTo); - } - pToFile[0] = '\\'; - if (b_ToInvalidTail) - { - retCode = 0x10003; - goto shfileop_error; - } - } - } + pFromFile = SHFileStrCpyCatW(pTempFrom, pFrom, NULL); - /* trailing BackSlash is ever removed and pToFile points to BackSlash before */ - if (!b_MultiTo && (b_MultiFrom || (!(b_Multi) && IsAttribDir(ToAttr)))) - { - if ((FO_MOVE == FuncSwitch) && IsAttribDir(ToAttr) && IsAttribDir(wfd.dwFileAttributes)) - { - if (b_Multi) - { - retCode = 0x73; /* !b_Multi = 0x8 ?? */ - goto shfileop_error; - } - } - pToFile = SHFileStrCpyCatW(pTempTo, NULL, wfd.cFileName); - ToAttr = GetFileAttributesW(pTempTo); - } - - if (IsAttribDir(ToAttr)) - { - if (IsAttribFile(wfd.dwFileAttributes)) - { - retCode = (FO_COPY == FuncSwitch) ? 0x75 : 0xb7; - goto shfileop_error; - } - } - else - { + if (pTo) + { + pToFile = SHFileStrCpyCatW(pTempTo, pTo, NULL); + } + if (!b_MultiPaired) + { + b_MultiPaired = + SHELL_FileNamesMatch(lpFileOp->pFrom, lpFileOp->pTo, (!b_Multi || b_MultiFrom)); + } + if (!(b_MultiPaired) || !(pFromFile) || !(pFromFile[1]) || ((pTo) && !(pToFile))) + { + retCode = 0x402; /* 1026 */ + goto shfileop_error; + } + if (pTo) + { + b_ToTailSlash = (!pToFile[1]); + if (b_ToTailSlash) + { pToFile[0] = '\0'; - ToPathAttr = GetFileAttributesW(pTempTo); - pToFile[0] = '\\'; - if (IsAttribFile(ToPathAttr)) - { - /* error, is this tested ? */ - retCode = 0x777402; - goto shfileop_error; - } - } - - /* singlesource + no mask */ - if (-1 == (ToAttr & ToPathAttr)) - { - /* Target-dir does not exist, and cannot be created */ - retCode=0x75; - goto shfileop_error; - } - - switch(FuncSwitch) - { - case FO_MOVE: - pToFile = NULL; - if ((ToAttr == -1) && SHFileStrICmpW(pTempFrom, pTempTo, pFromFile, NULL)) - { - nFileOp.wFunc = ((level+1)<<4) + FO_RENAME; - } - else - { - if (b_SameRoot && IsAttribDir(ToAttr) && IsAttribDir(wfd.dwFileAttributes)) - { - /* we need pToFile for FO_DELETE after FO_MOVE contence */ - pToFile = SHFileStrCpyCatW(pTempFrom, NULL, wWildcardFile); - } - else - { - nFileOp.wFunc = ((level+1)<<4) + FO_COPY; - } - } - retCode = SHFileOperationW(&nFileOp); - if (pToFile) - ((DWORD*)pToFile)[0] = '\0'; - if (!nFileOp.fAnyOperationsAborted && (FO_RENAME != (nFileOp.wFunc & 0xf))) - { - nFileOp.wFunc = ((level+1)<<4) + FO_DELETE; - retCode = SHFileOperationW(&nFileOp); - } - continue; - case FO_COPY: - if (SHFileStrICmpW(pTempFrom, pTempTo, NULL, NULL)) - { /* target is the same as source ? */ - /* we still need the value for the returncode, we assume 0x71 */ - retCode = 0x71; - goto shfileop_error; - } - if (IsAttribDir((ToAttr & wfd.dwFileAttributes))) - { - if (IsAttribDir(ToAttr) || !SHCreateDirectoryExW(NULL,pTempTo, NULL)) - { -/* ??? nFileOp.fFlags = (nFileOp.fFlags | FOF_MULTIDESTFILES); */ - SHFileStrCpyCatW(pTempFrom, NULL, wWildcardFile); - retCode = SHFileOperationW(&nFileOp); - } - else - { - retCode = 0x750;/* value unknown */ - goto shfileop_error; - } - } - else + if (StrChrW(pTempTo,'\\')) { - if (!(ask_overwrite && SHELL_ConfirmDialogW(ASK_OVERWRITE_FILE, pTempTo)) - && (not_overwrite)) - { - /* we still need the value for the returncode, we use the mostly assumed */ - retCode = 0x73; - goto shfileop_error; - } - if (!(SHNotifyCopyFileW(pTempFrom, pTempTo, nFileOp.fFlags & FOF_RENAMEONCOLLISION))) - { - retCode = 0x77; /* value unknown */ - goto shfileop_error; - } + pToFile = SHFileStrCpyCatW(pTempTo, NULL, NULL); } - } + } + b_ToInvalidTail = (NULL != StrPBrkW(&pToFile[1], wWildcardChars)); } + retCode = do_FileOp( &nFileOp, FuncSwitch, level, + ask_overwrite, not_overwrite, + b_Multi, b_MultiTo, b_MultiFrom, + b_ToInvalidTail, b_ToTailSlash, + pFrom, pTempFrom, pFromFile, + pTo, pTempTo, pToFile ); + if( retCode ) + goto shfileop_error; + if( retCode & 0x80000000 ) + { + retCode = 0; + goto shfileop_normal; + } + } shfileop_normal: - if (!(nFileOp.fAnyOperationsAborted)) - retCode = 0; + if (!(nFileOp.fAnyOperationsAborted)) + retCode = 0; shfileop_error: - if (hFind != INVALID_HANDLE_VALUE) - FindClose(hFind); - hFind = INVALID_HANDLE_VALUE; - if (pTempFrom) - HeapFree(GetProcessHeap(), 0, pTempFrom); - if (retCode) - { - nFileOp.fAnyOperationsAborted = TRUE; - } - TRACE("%s level=%ld AnyOpsAborted=%s ret=0x%lx, with %s %s%s\n", - debug_shfileops_action(FuncSwitch), level, - nFileOp.fAnyOperationsAborted ? "TRUE":"FALSE", - retCode, debugstr_w(pFrom), pTo ? "-> ":"", debugstr_w(pTo)); + if (pTempFrom) + HeapFree(GetProcessHeap(), 0, pTempFrom); + if (retCode) + nFileOp.fAnyOperationsAborted = TRUE; + + TRACE("%s level=%ld AnyOpsAborted=%s ret=0x%lx, with %s %s%s\n", + debug_shfileops_action(FuncSwitch), level, + nFileOp.fAnyOperationsAborted ? "TRUE":"FALSE", + retCode, debugstr_w(pFrom), pTo ? "-> ":"", debugstr_w(pTo)); - lpFileOp->fAnyOperationsAborted = nFileOp.fAnyOperationsAborted; - return retCode; + lpFileOp->fAnyOperationsAborted = nFileOp.fAnyOperationsAborted; + return retCode; } + /************************************************************************* * SHFileOperation [SHELL32.@]