SHFileOperationW split

Joris Huizer jorishuizer at planet.nl
Fri Dec 16 11:39:55 CST 2005


I seperated code of deleting, renaming, moving, and copying, and the 
overall result is that code becomes simpler - though a small amount of 
code is duplicated (mostly in move/copy operations, that's why I split 
the largest part of common code in a separate function)

If we want that, recursion still needs to be fixed in the non-obvious 
cases - probably with functions especially written for that;
Are there existing funtions in wine that are able to move/copy contents 
of directories and subdirectories into a target directory?

I renamed the old SHFileOperationW into SHFileOperationW0 as the patch 
became unreadable otherwise; will send another patch removing that one 
if this one is accepted

+ changed indenting to four spaces (though I could change that in case 
you really prefer indenting by tabs like most of that file does?)
-------------- next part --------------
? .shlfileop.c.swp
? added
? shell32.dll.dbg.c
Index: shlfileop.c
===================================================================
RCS file: /home/wine/wine/dlls/shell32/shlfileop.c,v
retrieving revision 1.56
diff -u -p -r1.56 shlfileop.c
--- shlfileop.c	10 Nov 2005 11:15:22 -0000	1.56
+++ shlfileop.c	16 Dec 2005 17:20:31 -0000
@@ -866,9 +866,68 @@ static const char * debug_shfileops_acti
     return wine_dbg_sprintf("%s", cFO_Name[ op ]);
 }
 
+struct fileops_file_data
+{
+    LPCWSTR next;
+    LPCWSTR name;
+    LPWSTR temp;
+    LPWSTR file;
+    DWORD attr;
+    DWORD pathAttr;
+    BOOL multi;
+    BOOL invalidTail;
+    BOOL valid;
+    BOOL tailSlash;
+};
+
+void printFileData(struct fileops_file_data *field)
+{
+    TRACE("data on %p:\n"
+            "next: %s\n"
+            "name: %s\n"
+            "temp: %s\n"
+            "file: %s\n"
+            "attr: %lx\n"
+            "pathAttr: %lx\n"
+            "multi: %d\n"
+            "invalidTail: %d\n"
+            "valid: %d\n"
+            "tailSlash: %d\n",
+            field,
+            debugstr_w(field->next),
+            debugstr_w(field->name),
+            debugstr_w(field->temp),
+            debugstr_w(field->file),
+            field->attr,
+            field->pathAttr,
+            field->multi,
+            field->invalidTail,
+            field->valid,
+            field->tailSlash
+                );
+}
+
 #define ERROR_SHELL_INTERNAL_FILE_NOT_FOUND 1026
 #define HIGH_ADR (LPWSTR)0xffffffff
 
+static int shfileops_delete_file(WIN32_FIND_DATAW *wfd,int confirm,LPWSTR pFromFile,LPWSTR pTempFrom)
+{
+    int retCode = 0;
+    LPWSTR lpFileName;
+    lpFileName = wfd->cAlternateFileName;
+    if (!lpFileName[0])
+        lpFileName = wfd->cFileName;
+    SHFileStrCpyCatW(&pFromFile[1], lpFileName, NULL);
+    if (IsAttribFile(wfd->dwFileAttributes))
+    {
+        if(SHNotifyDeleteFileW(pTempFrom) != ERROR_SUCCESS)
+            retCode = 0x78; /* value unknown */
+    }
+    else if(!SHELL_DeleteDirectoryW(pTempFrom,confirm))
+        retCode = 0x79; /* value unknown */
+    return retCode;
+}
+
 /* handle the complete deletion of `pTempFrom` */
 static int shfileops_delete(WIN32_FIND_DATAW *wfd,SHFILEOPSTRUCTW nFileOp, LPWSTR pFromFile,LPWSTR pTempFrom,HANDLE *hFind)
 
@@ -884,20 +943,12 @@ static int shfileops_delete(WIN32_FIND_D
         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))
-        {
-            if(SHNotifyDeleteFileW(pTempFrom) != ERROR_SUCCESS)
-            {
-                nFileOp.fAnyOperationsAborted = TRUE;
-                retCode = 0x78; /* value unknown */
-            }
-        }
-        else if(!SHELL_DeleteDirectoryW(pTempFrom, (!(nFileOp.fFlags & FOF_NOCONFIRMATION))))
+
+        retCode = shfileops_delete_file(wfd,(nFileOp.fFlags & FOF_NOCONFIRMATION) == 0,pFromFile,pTempFrom);
+        if (retCode)
         {
-            nFileOp.fAnyOperationsAborted = TRUE;
-            retCode = 0x79; /* value unknown */
+          nFileOp.fAnyOperationsAborted = TRUE;
+          break;
         }
     }
     while (!nFileOp.fAnyOperationsAborted && FindNextFileW(*hFind,wfd));
@@ -1006,7 +1057,7 @@ static DWORD shfileops_get_parent_attr(L
  *
  * See SHFileOperationA.
  */
-int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
+int WINAPI SHFileOperationW0(LPSHFILEOPSTRUCTW lpFileOp)
 {
 	SHFILEOPSTRUCTW nFileOp = *(lpFileOp);
 
@@ -1051,7 +1102,7 @@ int WINAPI SHFileOperationW(LPSHFILEOPST
             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:
@@ -1389,6 +1440,600 @@ shfileop_end:
 	return retCode;
 }
 
+static int shfileop_unmasked_setup(WIN32_FIND_DATAW wfd, struct fileops_file_data *to,struct fileops_file_data *from,BOOL b_Multi,long FuncSwitch)
+{
+    int retCode = 0;
+
+    TRACE("b_Multi: %d\n",b_Multi);
+    TRACE("to:\n");
+    printFileData(to);
+    TRACE("from:\n");
+    printFileData(from);
+
+    if (IsAttribDir(wfd.dwFileAttributes) && (to->attr == INVALID_FILE_ATTRIBUTES))
+    {
+        if (to->file)
+        {
+            to->pathAttr = shfileops_get_parent_attr2(to->file,to->temp,to->valid,&retCode);
+            if (retCode)
+                return retCode;
+            if (to->invalidTail)
+            {
+                retCode = 0x10003;
+                return retCode;
+            }
+        }
+    }
+
+    /* trailing BackSlash is ever removed and to->file points to BackSlash before */
+    if (!to->multi && (from->multi || (!(b_Multi) && IsAttribDir(to->attr))))
+    {
+        if ((FO_MOVE == FuncSwitch) && IsAttribDir(to->attr) && IsAttribDir(wfd.dwFileAttributes))
+        {
+            if (b_Multi)
+            {
+                retCode = 0x73; /* !b_Multi = 0x8 ?? */
+                return retCode;
+            }
+        }
+        to->file = SHFileStrCpyCatW(to->temp, NULL, wfd.cFileName);
+        to->attr = GetFileAttributesW(to->temp);
+    }
+
+    if (IsAttribDir(to->attr))
+    {
+        if (IsAttribFile(wfd.dwFileAttributes))
+        {
+            retCode = (FO_COPY == FuncSwitch) ? 0x75 : 0xb7;
+            return retCode;
+        }
+    }
+    else
+    {
+        to->pathAttr = shfileops_get_parent_attr(to->file,to->temp);
+        if (IsAttribFile(to->pathAttr))
+        {
+            /* error, is this tested ? */
+            retCode = 0x777402;
+            return retCode;
+        }
+    }
+
+    /* singlesource + no mask */
+    if (INVALID_FILE_ATTRIBUTES == (to->attr & to->pathAttr))
+    {
+        /* Target-dir does not exist, and cannot be created */
+        retCode=0x75;
+        return retCode;
+    }
+
+    return retCode;
+}
+
+static int shfileop_move_unmasked( SHFILEOPSTRUCTW nFileOp,WIN32_FIND_DATAW wfd, struct fileops_file_data to, struct fileops_file_data from)
+{
+    int retCode = 0;
+    int level = nFileOp.wFunc >> 4;
+
+    BOOL b_SameRoot = (toupperW(from.temp[0]) == toupperW(to.temp[0]));
+
+    to.file = NULL;
+    if ((to.attr == INVALID_FILE_ATTRIBUTES) && SHFileStrICmpW(from.temp, to.temp, from.file, NULL))
+    {
+        nFileOp.wFunc =  ((level+1)<<4) + FO_RENAME;
+    }
+    else
+    {
+        if (b_SameRoot && IsAttribDir(to.attr) && IsAttribDir(wfd.dwFileAttributes))
+        {
+            /* FIXME: moving directory contents into the other one:
+             * - path\\ -> path\\*
+             *   MOVE
+             * - loop over files in path\\*, eg path\\file
+             *   MOVE
+             * - COPY, DELETE
+             *
+             * is this really necessary? why not a dedicated recursive
+             * (light-weight) function?
+             */
+            /* we need to.file for FO_DELETE after FO_MOVE contence */
+            to.file = SHFileStrCpyCatW(from.temp, NULL, wWildcardFile);
+        }
+        else
+        {
+            nFileOp.wFunc =  ((level+1)<<4) + FO_COPY;
+        }
+    }
+
+    switch(nFileOp.wFunc & FO_MASK)
+    {
+        case FO_MOVE:
+            retCode = SHFileOperationW(&nFileOp);
+            break;
+        case FO_RENAME:
+            /*
+               retCode = SHFileOperationW(&nFileOp);
+               */
+            if (SHNotifyMoveFileW(from.temp, to.temp) != ERROR_SUCCESS)
+            {
+                /* we need still the value for the returncode, we use the mostly assumed */
+                retCode = 0xb7;
+                nFileOp.fAnyOperationsAborted = TRUE;
+            }
+            break;
+        case FO_COPY:
+            retCode = SHFileOperationW(&nFileOp);
+            break;
+        default:
+            retCode = SHFileOperationW(&nFileOp);
+            break;
+    }
+
+    if (to.file)
+        ((DWORD*)to.file)[0] = '\0';
+    if (!nFileOp.fAnyOperationsAborted && (FO_RENAME != (nFileOp.wFunc & 0xf)))
+    {
+        /*
+           TRACE("preparing for delete\n");
+           nFileOp.wFunc =  ((level+1)<<4) + FO_DELETE;
+           TRACE("delete: going to execute: %s: flags (0x%04x) : %s\n",
+           debug_shfileops_action(nFileOp.wFunc & FO_MASK), nFileOp.fFlags,
+           debug_shfileops_flags(nFileOp.fFlags) );
+
+           retCode = SHFileOperationW(&nFileOp);
+           TRACE("returned: %d\n",retCode);
+           */
+        retCode = shfileops_delete_file(&wfd,(nFileOp.fFlags & FOF_NOCONFIRMATION) == 0,from.file,from.temp);
+    }
+
+    return retCode;
+}
+
+static int shfileop_copy_unmasked(SHFILEOPSTRUCTW nFileOp,WIN32_FIND_DATAW wfd,struct fileops_file_data to,struct fileops_file_data from)
+{
+    int retCode = 0;
+    BOOL not_overwrite = (!(nFileOp.fFlags & FOF_NOCONFIRMATION) ||  (nFileOp.fFlags & FOF_RENAMEONCOLLISION));
+    BOOL ask_overwrite = (!(nFileOp.fFlags & FOF_NOCONFIRMATION) && !(nFileOp.fFlags & FOF_RENAMEONCOLLISION));
+
+    if (SHFileStrICmpW(from.temp, to.temp, NULL, NULL))
+    { /* target is the same as source ? */
+        /* we still need the value for the returncode, we assume 0x71 */
+        retCode = 0x71;
+        return retCode;
+    }
+    if (IsAttribDir((to.attr & wfd.dwFileAttributes)))
+    {
+        if (IsAttribDir(to.attr) || !SHNotifyCreateDirectoryW(to.temp, NULL))
+        {
+            /* FIXME: copying directory contents into the other one:
+             * - path\\ -> path\\*
+             *   COPY
+             * - loop over files in path\\*, eg path\\file
+             *   COPY
+             *
+             * is this really necessary? why not a dedicated recursive
+             * (light-weight) function?
+             */
+
+            /* ??? nFileOp.fFlags = (nFileOp.fFlags | FOF_MULTIDESTFILES); */
+            SHFileStrCpyCatW(from.temp, NULL, wWildcardFile);
+            TRACE("recursive copy\n");
+            retCode = SHFileOperationW(&nFileOp);
+        }
+        else
+        {
+            retCode = 0x750;/* value unknown */
+            return retCode;
+        }
+    }
+    else
+    {
+        if (!(ask_overwrite && SHELL_ConfirmDialogW(ASK_OVERWRITE_FILE, to.temp))
+                && (not_overwrite))
+        {
+            /* we still need the value for the returncode, we use the mostly assumed */
+            retCode = 0x73;
+            return retCode;
+        }
+        if (SHNotifyCopyFileW(from.temp, to.temp, TRUE) != ERROR_SUCCESS)
+        {
+            retCode = 0x77; /* value unknown */
+            return retCode;
+        }
+    }
+    return retCode;
+}
+
+static int shfileop_copy(SHFILEOPSTRUCTW nFileOp,struct fileops_file_data from,struct fileops_file_data to)
+{
+    WIN32_FIND_DATAW wfd;
+    HANDLE hFind = FindFirstFileW(from.name, &wfd);
+    int retCode = 0;
+
+    BOOL b_Multi = (nFileOp.fFlags & FOF_MULTIDESTFILES);
+    BOOL b_SameRoot;
+    BOOL b_SameTailName;
+    BOOL b_Mask = (NULL != StrPBrkW(&from.file[1], wWildcardChars));
+
+    /* to.name must be always valid */
+
+    if (INVALID_HANDLE_VALUE == hFind)
+    {
+        /* root (without mask) is also not allowed as source, tested in W98 */
+        return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND;
+    }
+
+    b_SameRoot = (toupperW(from.temp[0]) == toupperW(to.temp[0]));
+    b_SameTailName = SHFileStrICmpW(to.file, from.file, NULL, NULL);
+
+    to.pathAttr = to.attr = GetFileAttributesW(to.temp);
+    if (!b_Mask && (to.attr == INVALID_FILE_ATTRIBUTES) && (to.file))
+    {
+        to.pathAttr = shfileops_get_parent_attr(to.file,to.temp);
+    }
+
+
+    to.valid = ((b_SameTailName &&  b_SameRoot) ||
+            (b_SameTailName && !b_SameRoot) || (to.invalidTail));
+
+    /* handle mask in source */
+    if (b_Mask)
+    {
+        if (!IsAttribDir(to.attr))
+        {
+            retCode = 0x75;
+            goto shfileop_end;
+        }
+        to.file = SHFileStrCpyCatW(to.temp, NULL, wBackslash);
+        nFileOp.fFlags = (nFileOp.fFlags | FOF_MULTIDESTFILES);
+        do
+        {
+            /* should be possible to call shfileop_unmasked_setup & shfileop_copy_unmasked here... */
+            retCode = shfileops_do_operation(wfd,&nFileOp,to.file,from.file);
+        } while(!nFileOp.fAnyOperationsAborted && FindNextFileW(hFind, &wfd));
+    }
+    FindClose(hFind);
+    hFind = INVALID_HANDLE_VALUE;
+
+    if (!b_Mask)
+    {
+        retCode = shfileop_unmasked_setup(wfd,&to,&from,b_Multi,FO_COPY);
+        if (retCode == 0)
+            retCode = shfileop_copy_unmasked(nFileOp,wfd,to,from);
+    }
+
+shfileop_end:
+
+    if (hFind != INVALID_HANDLE_VALUE)
+        FindClose(hFind);
+    return retCode;
+}
+
+static int shfileop_delete(SHFILEOPSTRUCTW nFileOp,struct fileops_file_data from,struct fileops_file_data to)
+{
+    int retCode = 0;
+    WIN32_FIND_DATAW wfd;
+    HANDLE hFind = FindFirstFileW(from.name, &wfd);
+    BOOL b_Mask = (NULL != StrPBrkW(&from.file[1], wWildcardChars));
+
+    if (INVALID_HANDLE_VALUE == hFind)
+    {
+        /* root (without mask) is also not allowed as source, tested in W98 */
+        if (!((b_Mask) && IsAttribDir(shfileops_get_parent_attr(from.file,from.temp))))
+            return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND;
+        else
+            return 0;
+    }
+    else if (to.name)
+        FIXME("FO_DELETE with lpFileOp->pTo unequal to NULL, ignoring\n");
+    else
+    {
+        do
+        {
+            LPWSTR lpFileName = wfd.cAlternateFileName;
+            if (!lpFileName[0])
+                lpFileName = wfd.cFileName;
+            if (IsDotDir(lpFileName) ||
+                    ((b_Mask) && IsAttribDir(wfd.dwFileAttributes) && (nFileOp.fFlags & FOF_FILESONLY)))
+                continue;
+            retCode = shfileops_delete_file(&wfd,(nFileOp.fFlags & FOF_NOCONFIRMATION) == 0,from.file,from.temp);
+            if (retCode)
+                break;
+        }
+        while (FindNextFileW(hFind,&wfd));
+    }
+
+    FindClose(hFind);
+    return retCode;
+}
+
+static int shfileop_move(SHFILEOPSTRUCTW nFileOp,struct fileops_file_data from,struct fileops_file_data to)
+{
+    WIN32_FIND_DATAW wfd;
+    HANDLE hFind = FindFirstFileW(from.name, &wfd);
+    int retCode = 0;
+
+    BOOL b_Multi = (nFileOp.fFlags & FOF_MULTIDESTFILES);
+    BOOL b_SameRoot;
+    BOOL b_SameTailName;
+    BOOL b_Mask = (NULL != StrPBrkW(&from.file[1], wWildcardChars));
+
+    /* to.name must be always valid */
+
+    if (INVALID_HANDLE_VALUE == hFind)
+    {
+        /* root (without mask) is also not allowed as source, tested in W98 */
+        return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND;
+    }
+
+    b_SameRoot = (toupperW(from.temp[0]) == toupperW(to.temp[0]));
+    b_SameTailName = SHFileStrICmpW(to.file, from.file, NULL, NULL);
+
+    to.pathAttr = to.attr = GetFileAttributesW(to.temp);
+    if (!b_Mask && (to.attr == INVALID_FILE_ATTRIBUTES) && (to.file))
+    {
+        to.pathAttr = shfileops_get_parent_attr(to.file,to.temp);
+    }
+
+    /* W98 Bug with FO_MOVE different from FO_COPY, better the same as FO_COPY */
+    to.valid = ((b_SameTailName && !b_SameRoot) || (to.invalidTail));
+
+    /* handle mask in source */
+    if (b_Mask)
+    {
+        if (!IsAttribDir(to.attr))
+        {
+            retCode = (to.invalidTail/* && b_SameTailName*/)
+                ? 0x2 : 0x75;
+            goto shfileop_end;
+        }
+        to.file = SHFileStrCpyCatW(to.temp, NULL, wBackslash);
+        nFileOp.fFlags = (nFileOp.fFlags | FOF_MULTIDESTFILES);
+        do
+        {
+            /* should be possible to call shfileop_move_unmasked & shfileop_move_unmasked here... */
+            retCode = shfileops_do_operation(wfd,&nFileOp,to.file,from.file);
+        } while(!nFileOp.fAnyOperationsAborted && FindNextFileW(hFind, &wfd));
+    }
+    FindClose(hFind);
+    hFind = INVALID_HANDLE_VALUE;
+
+    if (!b_Mask)
+    {
+        retCode = shfileop_unmasked_setup(wfd,&to,&from,b_Multi,FO_MOVE);
+        if (retCode == 0)
+            retCode = shfileop_move_unmasked(nFileOp,wfd,to,from);
+    }
+
+shfileop_end:
+
+    if (hFind != INVALID_HANDLE_VALUE)
+        FindClose(hFind);
+    return retCode;
+}
+
+static int shfileop_rename(SHFILEOPSTRUCTW nFileOp,struct fileops_file_data from,struct fileops_file_data to)
+{
+    HANDLE hFind = INVALID_HANDLE_VALUE;
+    WIN32_FIND_DATAW wfd;
+    int retCode = 0;
+
+    BOOL b_SameRoot = (toupperW(from.temp[0]) == toupperW(to.temp[0]));
+    BOOL b_Mask = (NULL != StrPBrkW(&from.file[1], wWildcardChars));
+
+    /* to.name must be always valid */
+
+    if (to.multi || from.multi || (b_Mask && !to.invalidTail))
+    {
+        /* W2K ERROR_GEN_FAILURE, W98 returns no error */
+        return ERROR_GEN_FAILURE;
+    }
+
+    hFind = FindFirstFileW(from.name, &wfd);
+    if (INVALID_HANDLE_VALUE == hFind)
+    {
+        /* root (without mask) is also not allowed as source, tested in W98 */
+        return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND;
+    }
+
+    to.pathAttr = to.attr = GetFileAttributesW(to.temp);
+    if (!b_Mask && (to.attr == INVALID_FILE_ATTRIBUTES) && (to.file))
+    {
+        to.pathAttr = shfileops_get_parent_attr(to.file,to.temp);
+    }
+
+    if (!b_SameRoot || b_Mask /* FO_RENAME works not with Mask */
+            || !SHFileStrICmpW(from.temp, to.temp, from.file, NULL)
+            || (SHFileStrICmpW(from.temp, to.temp, from.file, HIGH_ADR) && !to.tailSlash))
+    {
+        retCode = 0x73;
+    }
+    else if (to.invalidTail)
+    {
+        retCode=0x2;
+    }
+    else if (INVALID_FILE_ATTRIBUTES == to.pathAttr)
+    {
+        retCode = 0x75;
+    }
+    else if (IsAttribDir(wfd.dwFileAttributes) && IsAttribDir(to.attr))
+    {
+        retCode = (to.tailSlash) ? 0xb7 : 0x7b;
+    }
+    /* we use SHNotifyMoveFile() instead MoveFileW */
+    else if (SHNotifyMoveFileW(from.temp, to.temp) != ERROR_SUCCESS)
+    {
+        /* we need still the value for the returncode, we use the mostly assumed */
+        retCode = 0xb7;
+    }
+
+    FindClose(hFind);
+    return retCode;
+}
+
+/*************************************************************************
+ * SHFileOperationW          [SHELL32.@]
+ *
+ * See SHFileOperationA.
+ */
+int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
+{
+    SHFILEOPSTRUCTW nFileOp = *(lpFileOp);
+
+    struct fileops_file_data from;        
+    struct fileops_file_data to;        
+
+    int retCode = 0;
+    BOOL b_Multi = (nFileOp.fFlags & FOF_MULTIDESTFILES);
+    BOOL b_MultiPaired = (FO_DELETE == (lpFileOp->wFunc & FO_MASK));
+    long FuncSwitch = (nFileOp.wFunc & FO_MASK);
+    long level= nFileOp.wFunc>>4;
+
+
+    /* init helper structures */
+    from.next = nFileOp.pFrom;
+    from.name = from.next;
+    from.temp = NULL;
+    from.multi = FALSE;
+
+    to.next = nFileOp.pTo;
+    to.name = NULL;
+    to.temp = NULL;
+    to.file = NULL;
+    to.multi = (FO_DELETE != (lpFileOp->wFunc & FO_MASK));
+    to.invalidTail = FALSE;
+    to.tailSlash = FALSE;
+
+    /*  default no error */
+    nFileOp.fAnyOperationsAborted = FALSE;
+
+    if ((FuncSwitch < FO_MOVE) || (FuncSwitch > FO_RENAME))
+        goto shfileop_end; /* 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
+     */
+
+    retCode = shfileops_check_flags(nFileOp);
+    if (retCode)
+        goto shfileop_end;
+
+    if ((from.next) && (!(to.multi) || (to.next)))
+    {
+        nFileOp.pFrom = from.temp = HeapAlloc(GetProcessHeap(), 0, ((1 + 2 * (to.multi)) * MAX_PATH + 6) * sizeof(WCHAR));
+        if (!from.temp)
+        {
+            retCode = ERROR_OUTOFMEMORY;
+            SetLastError(retCode);
+            goto shfileop_end;
+        }
+        if (to.multi)
+            to.temp = &from.temp[MAX_PATH + 4];
+        nFileOp.pTo = to.temp;
+    }
+    else
+    {
+        retCode = ERROR_SHELL_INTERNAL_FILE_NOT_FOUND;
+        goto shfileop_end;
+    }
+    /* need break at error before change sourcepointer */
+    while(!nFileOp.fAnyOperationsAborted && (from.next[0]))
+    {
+        nFileOp.wFunc =  ((level + 1) << 4) + FuncSwitch;
+        nFileOp.fFlags = lpFileOp->fFlags;
+
+        if (to.multi)
+        {
+            to.name = to.next;
+            to.next = &to.next[lstrlenW(to.name)+1];
+            to.multi = (b_Multi && to.next[0]);
+        }
+
+        from.name = from.next;
+        from.next = &from.next[lstrlenW(from.next)+1];
+        if (!from.multi && !to.multi)
+            from.multi = (from.next[0]);
+
+        from.file = SHFileStrCpyCatW(from.temp, from.name, NULL);
+
+        if (to.name)
+        {
+            to.file = SHFileStrCpyCatW(to.temp, to.name, NULL);
+        }
+        if (!b_MultiPaired)
+        {
+            b_MultiPaired =
+                SHELL_FileNamesMatch(lpFileOp->pFrom, lpFileOp->pTo, (!b_Multi || from.multi));
+        }
+        if (!(b_MultiPaired) || !(from.file) || !(from.file[1]) || ((to.name) && !(to.file)))
+        {
+            retCode = ERROR_SHELL_INTERNAL_FILE_NOT_FOUND;
+            break;
+        }
+        if (to.name)
+        {
+            to.tailSlash = (!to.file[1]);
+            if (to.tailSlash)
+            {
+                to.file[0] = '\0';
+                if (StrChrW(to.temp,'\\'))
+                {
+                    to.file = SHFileStrCpyCatW(to.temp, NULL, NULL);
+                }
+            }
+            to.invalidTail = (NULL != StrPBrkW(&to.file[1], wWildcardChars));
+        }
+
+        switch(FuncSwitch)
+        {
+            case FO_COPY:
+                retCode = shfileop_copy(nFileOp,from,to);
+                break;
+            case FO_DELETE:
+                retCode = shfileop_delete(nFileOp,from,to);
+                break;
+            case FO_MOVE:
+                retCode = shfileop_move(nFileOp,from,to);
+                break;
+            case FO_RENAME:
+                retCode = shfileop_rename(nFileOp,from,to);
+                break;
+        }
+        if (retCode)
+            break;
+    }
+
+shfileop_end:
+    HeapFree(GetProcessHeap(), 0, from.temp);
+    if (retCode)
+        nFileOp.fAnyOperationsAborted = TRUE;
+    TRACE("%s level=%ld AnyOpsAborted=%s ret=0x%x, with %s %s%s\n",
+            debug_shfileops_action(FuncSwitch), level,
+            nFileOp.fAnyOperationsAborted ? "TRUE":"FALSE",
+            retCode, debugstr_w(from.name), to.name ? "-> ":"", debugstr_w(to.name));
+
+    lpFileOp->fAnyOperationsAborted = nFileOp.fAnyOperationsAborted;
+    return retCode;
+}
+
 #define SHDSA_GetItemCount(hdsa) (*(int*)(hdsa))
 
 /*************************************************************************


More information about the wine-patches mailing list