Refactoring of SHFileOperation
Mike McCormack
mike at codeweavers.com
Tue Aug 26 10:06:37 CDT 2003
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
-------------- next part --------------
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.@]
More information about the wine-patches
mailing list