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