Changelog * dlls/shell32/shlfileop.c - Reimplement SHFileOperationA to be more complete. This code comes from Dietrich Teickner [Dietrich_Teickner@t-online.de] from Odin who did most of the work. The Unicode version is going to follow. - Fix Win32DeleteFile to retry to delete the file on initial error after removing the write protection and system flag and also return a success status rather than always TRUE. Verified in Win2000 to work in this way. License: X11/LGPL Rolf Kalbermatter Index: dlls/shell32/shlfileop.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/shlfileop.c,v retrieving revision 1.24 diff -u -r1.24 shlfileop.c --- dlls/shell32/shlfileop.c 1 Feb 2003 00:41:30 -0000 1.24 +++ dlls/shell32/shlfileop.c 6 Feb 2003 22:18:27 -0000 @@ -3,6 +3,8 @@ * * Copyright 2000 Juergen Schmied * Copyright 2002 Andriy Palamarchuk + * Copyright 2002 Dietrich Teickner (from Odin) + * Copyright 2002 Rolf Kalbermatter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -77,7 +79,6 @@ * * like rm -r */ - BOOL SHELL_DeleteDirectoryA(LPCSTR pszDir, BOOL bShowUI) { BOOL ret = FALSE; @@ -92,27 +93,25 @@ if (bShowUI && !SHELL_ConfirmDialog(ASK_DELETE_FOLDER, pszDir)) return FALSE; - if(INVALID_HANDLE_VALUE != (hFind = FindFirstFileA(szTemp, &wfd))) + if (INVALID_HANDLE_VALUE != (hFind = FindFirstFileA(szTemp, &wfd))) { do { - if(strcasecmp(wfd.cFileName, ".") && strcasecmp(wfd.cFileName, "..")) + if (strcasecmp(wfd.cFileName, ".") && strcasecmp(wfd.cFileName, "..")) { strcpy(szTemp, pszDir); PathAddBackslashA(szTemp); strcat(szTemp, wfd.cFileName); - if(FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes) + if (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes) SHELL_DeleteDirectoryA(szTemp, FALSE); else DeleteFileA(szTemp); } } while(FindNextFileA(hFind, &wfd)); - FindClose(hFind); ret = RemoveDirectoryA(pszDir); } - return ret; } @@ -123,9 +122,8 @@ BOOL SHELL_DeleteFileA(LPCSTR pszFile, BOOL bShowUI) { if (bShowUI && !SHELL_ConfirmDialog(ASK_DELETE_FILE, pszFile)) - return FALSE; - - return DeleteFileA(pszFile); + return FALSE; + return DeleteFileA(pszFile); } /************************************************************************* @@ -185,23 +183,41 @@ */ static BOOL Win32DeleteFileA(LPCSTR fName) { + DWORD ret; TRACE("%p(%s)\n", fName, fName); - DeleteFileA(fName); - SHChangeNotify(SHCNE_DELETE, SHCNF_PATHA, fName, NULL); - return TRUE; + if (!(ret = DeleteFileA(fName))) + { + /* File may be write protected or a system file */ + DWORD dwAttr = GetFileAttributesA(fName); + if ((dwAttr != -1) && (dwAttr & FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)) + if (SetFileAttributesA(fName, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))) + ret = DeleteFileA(fName); + } + if (ret) + SHChangeNotify(SHCNE_DELETE, SHCNF_PATHA, fName, NULL); + return ret; } static BOOL Win32DeleteFileW(LPCWSTR fName) { + BOOL ret; TRACE("%p(%s)\n", fName, debugstr_w(fName)); - DeleteFileW(fName); - SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, fName, NULL); - return TRUE; + if (!(ret = DeleteFileW(fName))) + { + /* File may be write protected or a system file */ + DWORD dwAttr = GetFileAttributesW(fName); + if ((dwAttr != -1) && (dwAttr & FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)) + if (SetFileAttributesW(fName, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))) + ret = DeleteFileW(fName); + } + if (ret) + SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, fName, NULL); + return ret; } -DWORD WINAPI Win32DeleteFileAW(LPCVOID fName) +BOOL WINAPI Win32DeleteFileAW(LPCVOID fName) { if (SHELL_OsIsUnicode()) return Win32DeleteFileW(fName); @@ -209,27 +225,82 @@ } /************************************************************************** - * SHELL_FileNamesMatch() * - * Accepts two \0 delimited lists of the file names. Checks whether number of - * files in the both lists is the same. + * SHFileStrCpyCatA HelperFunction for SHFileOperationA + * + */ +LPSTR SHFileStrCpyCatA(LPSTR pTo, LPCSTR pFrom, LPCSTR pCatStr) +{ + LPSTR pToFile = NULL; + int i_len; + + if (pTo) + { + if (pFrom) + lstrcpyA(pTo, pFrom); + if (pCatStr) /* lstrcatA(pTo, pCatStr); ?? */ + { + i_len = lstrlenA(pTo); + if ((i_len) && (pCatStr[0] == '\\') && (pTo[--i_len] == '\\')) + pTo[i_len] = '\0'; + lstrcatA(pTo, pCatStr); + } + pToFile = strrchr(pTo, '\\'); + /* !! termination of the new string-group */ + pTo[(lstrlenA(pTo)) + 1] = '\0'; + } + return pToFile; +} + +/************************************************************************* + * + * SHFileStrICmpA HelperFunction for SHFileOperationA + * */ -BOOL SHELL_FileNamesMatch(LPCSTR pszFiles1, LPCSTR pszFiles2) +BOOL SHFileStrICmpA(LPSTR p1,LPSTR p2, LPSTR p1End, LPSTR p2End) { - while ((pszFiles1[strlen(pszFiles1) + 1] != '\0') && - (pszFiles2[strlen(pszFiles2) + 1] != '\0')) - { - pszFiles1 += strlen(pszFiles1) + 1; - pszFiles2 += strlen(pszFiles2) + 1; - } - - return - ((pszFiles1[strlen(pszFiles1) + 1] == '\0') && - (pszFiles2[strlen(pszFiles2) + 1] == '\0')) || - ((pszFiles1[strlen(pszFiles1) + 1] != '\0') && - (pszFiles2[strlen(pszFiles2) + 1] != '\0')); + CHAR C1 = '\0'; + CHAR C2 = '\0'; + int i_Temp = -1; + int i_len1 = lstrlenA(p1); + int i_len2 = lstrlenA(p2); + + if (p1End && (&p1[i_len1] >= p1End) && ('\\' == p1End[0])) + { + C1 = p1End[0]; + p1End[0] = '\0'; + i_len1 = lstrlenA(p1); + } + if (p2End) + { + if ((&p2[i_len2] >= p2End) && ('\\' == p2End[0])) + { + C2 = p2End[0]; + if (C2) + p2End[0] = '\0'; + } + } + else + { + if ((i_len1 <= i_len2) && ('\\' == p2[i_len1])) + { + C2 = p2[i_len1]; + if (C2) + p2[i_len1] = '\0'; + } + } + i_len2 = lstrlenA(p2); + if (i_len1 == i_len2) + i_Temp = lstrcmpiA(p1,p2); + if (C1) + p1[i_len1] = C1; + if (C2) + p2[i_len2] = C2; + return !(i_Temp); } +#define IsAttribFile(x) (!(x == -1) && !(x & FILE_ATTRIBUTE_DIRECTORY)) +#define IsAttribDir(x) (!(x == -1) && (x & FILE_ATTRIBUTE_DIRECTORY)) /************************************************************************* * SHFileOperationA [SHELL32.@] * @@ -238,242 +309,499 @@ */ DWORD WINAPI SHFileOperationA (LPSHFILEOPSTRUCTA lpFileOp) { - LPSTR pFrom = (LPSTR)lpFileOp->pFrom; - LPSTR pTo = (LPSTR)lpFileOp->pTo; - LPSTR pTempTo; - TRACE("flags (0x%04x) : %s%s%s%s%s%s%s%s%s%s%s%s \n", lpFileOp->fFlags, - lpFileOp->fFlags & FOF_MULTIDESTFILES ? "FOF_MULTIDESTFILES " : "", - lpFileOp->fFlags & FOF_CONFIRMMOUSE ? "FOF_CONFIRMMOUSE " : "", - lpFileOp->fFlags & FOF_SILENT ? "FOF_SILENT " : "", - lpFileOp->fFlags & FOF_RENAMEONCOLLISION ? "FOF_RENAMEONCOLLISION " : "", - lpFileOp->fFlags & FOF_NOCONFIRMATION ? "FOF_NOCONFIRMATION " : "", - lpFileOp->fFlags & FOF_WANTMAPPINGHANDLE ? "FOF_WANTMAPPINGHANDLE " : "", - lpFileOp->fFlags & FOF_ALLOWUNDO ? "FOF_ALLOWUNDO " : "", - lpFileOp->fFlags & FOF_FILESONLY ? "FOF_FILESONLY " : "", - lpFileOp->fFlags & FOF_SIMPLEPROGRESS ? "FOF_SIMPLEPROGRESS " : "", - lpFileOp->fFlags & FOF_NOCONFIRMMKDIR ? "FOF_NOCONFIRMMKDIR " : "", - lpFileOp->fFlags & FOF_NOERRORUI ? "FOF_NOERRORUI " : "", - lpFileOp->fFlags & 0xf800 ? "MORE-UNKNOWN-Flags" : ""); - switch(lpFileOp->wFunc) { - case FO_COPY: - case FO_MOVE: - { - /* 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 - */ - int multifrom = pFrom[strlen(pFrom) + 1] != '\0'; - int destisdir = PathIsDirectoryA( pTo ); - int todir = 0; - - if (lpFileOp->wFunc == FO_COPY) - TRACE("File Copy:\n"); - else - TRACE("File Move:\n"); - - if( destisdir ) { - if ( !((lpFileOp->fFlags & FOF_MULTIDESTFILES) && !multifrom)) - todir = 1; - } else { - if ( !(lpFileOp->fFlags & FOF_MULTIDESTFILES) && multifrom) - todir = 1; - } - - if ((pTo[strlen(pTo) + 1] != '\0') && - !(lpFileOp->fFlags & FOF_MULTIDESTFILES)) - { - WARN("Attempt to use multiple file names as a destination " - "without specifying FOF_MULTIDESTFILES\n"); - return 1; - } - - if ((lpFileOp->fFlags & FOF_MULTIDESTFILES) && - !SHELL_FileNamesMatch(pTo, pFrom)) - { - WARN("Attempt to use multiple file names as a destination " - "with mismatching number of files in the source and " - "destination lists\n"); - return 1; - } - - if ( todir ) { - char szTempFrom[MAX_PATH]; - char *fromfile; - int lenPTo; - if ( ! destisdir) { - TRACE(" creating directory %s\n",pTo); - SHCreateDirectoryExA(NULL, pTo, NULL); - } - lenPTo = strlen(pTo); - while(1) { - HANDLE hFind; - WIN32_FIND_DATAA wfd; - - if(!pFrom[0]) break; - TRACE(" From Pattern='%s'\n", pFrom); - if(INVALID_HANDLE_VALUE != (hFind = FindFirstFileA(pFrom, &wfd))) - { - do - { - if(strcasecmp(wfd.cFileName, ".") && strcasecmp(wfd.cFileName, "..")) - { - strcpy(szTempFrom, pFrom); - - pTempTo = HeapAlloc(GetProcessHeap(), 0, - lenPTo + strlen(wfd.cFileName) + 5); - if (pTempTo) { - strcpy(pTempTo,pTo); - PathAddBackslashA(pTempTo); - strcat(pTempTo,wfd.cFileName); - - fromfile = PathFindFileNameA(szTempFrom); - fromfile[0] = '\0'; - PathAddBackslashA(szTempFrom); - strcat(szTempFrom, wfd.cFileName); - TRACE(" From='%s' To='%s'\n", szTempFrom, pTempTo); - if(lpFileOp->wFunc == FO_COPY) - { - if(FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes) - { - /* copy recursively */ - if(!(lpFileOp->fFlags & FOF_FILESONLY)) - { - SHFILEOPSTRUCTA shfo; - - SHCreateDirectoryExA(NULL, pTempTo, NULL); - PathAddBackslashA(szTempFrom); - strcat(szTempFrom, "*.*"); - szTempFrom[strlen(szTempFrom) + 1] = '\0'; - pTempTo[strlen(pTempTo) + 1] = '\0'; - memcpy(&shfo, lpFileOp, sizeof(shfo)); - shfo.pFrom = szTempFrom; - shfo.pTo = pTempTo; - SHFileOperationA(&shfo); - - szTempFrom[strlen(szTempFrom) - 4] = '\0'; - } - } - else - CopyFileA(szTempFrom, pTempTo, FALSE); - } - else - { - /* move file/directory */ - MoveFileA(szTempFrom, pTempTo); - } - HeapFree(GetProcessHeap(), 0, pTempTo); - } - } - } while(FindNextFileA(hFind, &wfd)); - FindClose(hFind); - } - else - { - /* can't find file with specified name */ - break; - } - pFrom += strlen(pFrom) + 1; - } - } else { - while (1) { - if(!pFrom[0]) break; - if(!pTo[0]) break; - TRACE(" From='%s' To='%s'\n", pFrom, pTo); - - pTempTo = HeapAlloc(GetProcessHeap(), 0, strlen(pTo)+1); - if (pTempTo) - { - strcpy( pTempTo, pTo ); - PathRemoveFileSpecA(pTempTo); - TRACE(" Creating Directory '%s'\n", pTempTo); - SHCreateDirectoryExA(NULL, pTempTo, NULL); - HeapFree(GetProcessHeap(), 0, pTempTo); - } - if (lpFileOp->wFunc == FO_COPY) - CopyFileA(pFrom, pTo, FALSE); - else - MoveFileA(pFrom, pTo); - - pFrom += strlen(pFrom) + 1; - pTo += strlen(pTo) + 1; - } - } - TRACE("Setting AnyOpsAborted=FALSE\n"); - lpFileOp->fAnyOperationsAborted=FALSE; - return 0; - } - - case FO_DELETE: - { - HANDLE hFind; - WIN32_FIND_DATAA wfd; - char szTemp[MAX_PATH]; - char *file_name; - - TRACE("File Delete:\n"); - while(1) { - if(!pFrom[0]) break; - TRACE(" Pattern='%s'\n", pFrom); - if(INVALID_HANDLE_VALUE != (hFind = FindFirstFileA(pFrom, &wfd))) - { - do - { - if(strcasecmp(wfd.cFileName, ".") && strcasecmp(wfd.cFileName, "..")) - { - strcpy(szTemp, pFrom); - file_name = PathFindFileNameA(szTemp); - file_name[0] = '\0'; - PathAddBackslashA(szTemp); - strcat(szTemp, wfd.cFileName); - - TRACE(" File='%s'\n", szTemp); - if(FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes) - { - if(!(lpFileOp->fFlags & FOF_FILESONLY)) - SHELL_DeleteDirectoryA(szTemp, FALSE); - } - else - DeleteFileA(szTemp); - } - } while(FindNextFileA(hFind, &wfd)); - - FindClose(hFind); - } - pFrom += strlen(pFrom) + 1; - } - TRACE("Setting AnyOpsAborted=FALSE\n"); - lpFileOp->fAnyOperationsAborted=FALSE; - return 0; - } - - case FO_RENAME: - TRACE("File Rename:\n"); - if (pFrom[strlen(pFrom) + 1] != '\0') - { - WARN("Attempt to rename more than one file\n"); - return 1; - } - lpFileOp->fAnyOperationsAborted = FALSE; - TRACE("From %s, To %s\n", pFrom, pTo); - return !MoveFileA(pFrom, pTo); + SHFILEOPSTRUCTA nlpFileOp = *(lpFileOp); - default: - FIXME("Unhandled shell file operation %d\n", lpFileOp->wFunc); + LPCSTR pNextFrom = nlpFileOp.pFrom; + LPCSTR pNextTo = nlpFileOp.pTo; + LPCSTR pFrom = pNextFrom; + LPCSTR pTo = NULL; + HANDLE hFind = INVALID_HANDLE_VALUE; + WIN32_FIND_DATAA wfd; + LPSTR pTempFrom = NULL; + LPSTR pTempTo = NULL; + LPSTR pFromFile; + LPSTR pToFile; + long retCode = 0; + DWORD ToAttr; + DWORD ToPathAttr; + FILEOP_FLAGS OFl = ((FILEOP_FLAGS)lpFileOp->fFlags & 0x7ff); + + BOOL b_Multi = (nlpFileOp.fFlags & FOF_MULTIDESTFILES); + + BOOL b_MultiTo = (FO_DELETE != (lpFileOp->wFunc & 0xf)); + BOOL b_MultiFrom = FALSE; + BOOL not_overwrite; + BOOL ask_overwrite; + BOOL b_SameRoot; + BOOL b_SameTailName; + BOOL b_ToInvalidTail; + BOOL b_ToValid; /* for W98-Bug ? for FO_MOVE with source and target in same rootdrive */ + BOOL b_Mask; /* wird als Schalter benutzt, vieleicht finde ich die richtige bitposition */ + BOOL b_ToTailSlash; + LPSTR pToFuncTXT; + + long FuncSwitch = (nlpFileOp.wFunc & 0xf); + long level= nlpFileOp.wFunc>>4; + +/* default no error */ + nlpFileOp.fAnyOperationsAborted=FALSE; + + switch(FuncSwitch) + { + case FO_MOVE: + pToFuncTXT = "FO_MOVE";break; + case FO_COPY: + pToFuncTXT = "FO_COPY";break; + case FO_DELETE: + pToFuncTXT = "FO_DELETE";break; + case FO_RENAME: + pToFuncTXT = "FO_RENAME";break; + default: + pToFuncTXT = "FO_????"; + goto shfileop_normal; } - return 1; + if (level == 0) + TRACE("%s: flags (0x%04x) : %s%s%s%s%s%s%s%s%s%s%s%s \n", pToFuncTXT, nlpFileOp.fFlags, + nlpFileOp.fFlags & FOF_MULTIDESTFILES ? "FOF_MULTIDESTFILES " : "", + nlpFileOp.fFlags & FOF_CONFIRMMOUSE ? "FOF_CONFIRMMOUSE " : "", + nlpFileOp.fFlags & FOF_SILENT ? "FOF_SILENT " : "", + nlpFileOp.fFlags & FOF_RENAMEONCOLLISION ? "FOF_RENAMEONCOLLISION " : "", + nlpFileOp.fFlags & FOF_NOCONFIRMATION ? "FOF_NOCONFIRMATION " : "", + nlpFileOp.fFlags & FOF_WANTMAPPINGHANDLE ? "FOF_WANTMAPPINGHANDLE " : "", + nlpFileOp.fFlags & FOF_ALLOWUNDO ? "FOF_ALLOWUNDO " : "", + nlpFileOp.fFlags & FOF_FILESONLY ? "FOF_FILESONLY " : "", + nlpFileOp.fFlags & FOF_SIMPLEPROGRESS ? "FOF_SIMPLEPROGRESS " : "", + nlpFileOp.fFlags & FOF_NOCONFIRMMKDIR ? "FOF_NOCONFIRMMKDIR " : "", + nlpFileOp.fFlags & FOF_NOERRORUI ? "FOF_NOERRORUI " : "", + nlpFileOp.fFlags & 0xf800 ? "MORE-UNKNOWN-Flags" : ""); + ; + /* 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 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=%d nlpFileOp.fFlags=0x%x\n", pToFuncTXT, 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); /* ignored, if one */ + OFl &= (~FOF_SIMPLEPROGRESS); /* ignored, only wit FOF_SILEN */ + if (OFl) + { + if (OFl & (~ (FOF_CONFIRMMOUSE | FOF_SILENT | FOF_RENAMEONCOLLISION | FOF_NOCONFIRMMKDIR))) + { + TRACE("%s level=%d lpFileOp->fFlags=0x%x not implemented, Aborted=TRUE, stub\n", pToFuncTXT, level, OFl); + retCode = 0x403; /* we need a extension of shlfileop */ + nlpFileOp.fAnyOperationsAborted = TRUE; + } + else + { + TRACE("%s level=%d lpFileOp->fFlags=0x%x not full implemented ,stub\n", pToFuncTXT, level, OFl); + } /* endif */ + } /* endif */ + + if ((pNextFrom) && (!(b_MultiTo) || (pNextTo))) + { + nlpFileOp.pFrom = pTempFrom = HeapAlloc(GetProcessHeap(), 0, (1 + 2 * (b_MultiTo)) * MAX_PATH + 6); + if (b_MultiTo) pTempTo = &pTempFrom[MAX_PATH+4]; + nlpFileOp.pTo = pTempTo; + ask_overwrite = (!(nlpFileOp.fFlags & FOF_NOCONFIRMATION) && !(nlpFileOp.fFlags & FOF_RENAMEONCOLLISION)); + not_overwrite = (!(nlpFileOp.fFlags & FOF_NOCONFIRMATION) || (nlpFileOp.fFlags & FOF_RENAMEONCOLLISION)); + } + else + { + nlpFileOp.fAnyOperationsAborted = TRUE; + retCode = 0x402; + goto shfileop_error; + } + /* need break at error before change sourcepointer */ + while(!nlpFileOp.fAnyOperationsAborted && (pNextFrom[0])) + { + nlpFileOp.wFunc = ((level+1)<<4) + FuncSwitch; + nlpFileOp.fFlags = lpFileOp->fFlags; + + if (b_MultiTo) + { + pTo = pNextTo; + pNextTo = &pNextTo[lstrlenA(pTo)+1]; + b_MultiTo = (b_Multi && pNextTo[0]); + } + + pFrom = pNextFrom; + pNextFrom = &pNextFrom[lstrlenA(pNextFrom)+1]; + if (!b_MultiFrom && !b_MultiTo) + b_MultiFrom = (pNextFrom[0]); + + pFromFile = SHFileStrCpyCatA(pTempFrom, pFrom, NULL); + + if (pTo) + { + pToFile = SHFileStrCpyCatA(pTempTo, pTo, NULL); + if (!(pToFile)) + { + nlpFileOp.fAnyOperationsAborted = TRUE; + retCode = 0x402; + goto shfileop_error; + } /* endif */ + b_ToTailSlash = (!pToFile[1]); + if (b_ToTailSlash) + { + pToFile[0] = '\0'; + if (strchr(pTempTo, '\\')) + { + pToFile = SHFileStrCpyCatA(pTempTo, NULL, NULL); + } + } + b_ToInvalidTail = (NULL != strpbrk(&pToFile[1], "*?")); + } + + if (FO_RENAME == FuncSwitch) + { + /* temporary only for FO_RENAME */ + b_Mask = (NULL != strpbrk(pFrom, "*?")); + if (b_MultiTo || (pNextFrom[0]) || (b_Mask && !b_ToInvalidTail)) + { + /* no work, only RC=0 */ + /* ???? nlpFileOp.fAnyOperationsAborted = TRUE; */ + goto shfileop_normal; + } + } + + hFind = (!(pFromFile) || !(pFromFile[1])) ? INVALID_HANDLE_VALUE : FindFirstFileA(pFrom, &wfd); + if (INVALID_HANDLE_VALUE == hFind) + { + /* root (without mask) is also not allowed as source, tested in W98 */ + nlpFileOp.fAnyOperationsAborted = TRUE; + retCode = 0x402; + goto shfileop_error; + } /* endif */ + + /* for all */ + #define HIGH_ADR (LPSTR)0xffffffff + b_Mask = (!SHFileStrICmpA(&pFromFile[1], &wfd.cFileName[0], HIGH_ADR, HIGH_ADR)); + + if (!pTo) /* FO_DELETE */ + { + do + { + if (wfd.cFileName[0] == '.') + { + if (wfd.cFileName[1] == '\0') + continue; + if (wfd.cFileName[1] == '.' && wfd.cFileName[2] == '\0') + continue; + } + SHFileStrCpyCatA(&pFromFile[1], &wfd.cFileName[0], NULL); + if (IsAttribFile(wfd.dwFileAttributes)) + { + nlpFileOp.fAnyOperationsAborted = (!Win32DeleteFileA(pTempFrom)); + retCode = 0x78; /* value unknown */ + } + else + { + nlpFileOp.fAnyOperationsAborted = (!SHELL_DeleteDirectoryA(pTempFrom, FALSE)); + retCode = 0x78; /* value unknown */ + } + } while (!nlpFileOp.fAnyOperationsAborted && FindNextFileA(hFind, &wfd)); + FindClose(hFind); + hFind = INVALID_HANDLE_VALUE; + if (nlpFileOp.fAnyOperationsAborted) + { + goto shfileop_error; + } + continue; + } /* FO_DELETE ends, pTo must be every valid from here */ + + b_SameRoot = (toupper(pTempFrom[0]) == toupper(pTempTo[0])); + b_SameTailName = SHFileStrICmpA(pToFile, pFromFile, NULL, NULL); + + /* W98 Bug with FO_MOVE(/RENAME ?) 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)); + + ToPathAttr = ToAttr = GetFileAttributesA(pTempTo); + if (!b_Mask && /* IsAttribDir(wfd.dwFileAttributes) && */(ToAttr -1)) + { + if (pToFile) + { + pToFile[0] = '\0'; + ToPathAttr = GetFileAttributesA(pTempTo); + /* if (-1 != ToPathAttr) */ pToFile[0] = '\\'; + } + } + if (FO_RENAME == FuncSwitch) + { + if (!b_SameRoot || b_Mask /* FO_RENAME works not with Mask */ + || !SHFileStrICmpA(pTempFrom, pTempTo, pFromFile, NULL) + || (SHFileStrICmpA(pTempFrom, pTempTo, pFromFile, HIGH_ADR) && !b_ToTailSlash)) + { + retCode = 0x73; + nlpFileOp.fAnyOperationsAborted = TRUE; + goto shfileop_error; + } + if (b_ToInvalidTail) + { + nlpFileOp.fAnyOperationsAborted = TRUE; + retCode=0x2; + goto shfileop_error; + } + if (IsAttribDir(wfd.dwFileAttributes) && IsAttribDir(ToAttr)) + { + if (b_ToTailSlash) + { + retCode = 0xb7; + } + else + { + retCode = 0x7b; + } + nlpFileOp.fAnyOperationsAborted = TRUE; + goto shfileop_error; + } + if (-1 == ToPathAttr) + { + nlpFileOp.fAnyOperationsAborted = TRUE; + retCode = 0x75; + goto shfileop_error; + } /* endif */ + if (!MoveFileA(pTempFrom, pTempTo)) + { + nlpFileOp.fAnyOperationsAborted = TRUE; + /* we need still the value for the returncode, we use the mostly assumed */ + retCode = 0xb7; + goto shfileop_error; + } + goto shfileop_normal; + } + + if (!b_Mask && IsAttribDir(wfd.dwFileAttributes) && (ToAttr -1)) + { + if (pToFile) + { + pToFile[0] = '\0'; + ToPathAttr = GetFileAttributesA(pTempTo); + if ((ToPathAttr == -1) && b_ToValid) + { + /* create dir could be used here, sample target D:\y\ *.* create with RC=10003 */ + if(!SHCreateDirectory(NULL, pTempTo)) + { + nlpFileOp.fAnyOperationsAborted = TRUE; + retCode = 0x73;/* value unknown */ + goto shfileop_error; + } + ToPathAttr = GetFileAttributesA(pTempTo); + } + pToFile[0] = '\\'; + if (b_ToInvalidTail) + { + nlpFileOp.fAnyOperationsAborted = TRUE; + retCode=0x10003; + goto shfileop_error; + } + } + } + + /* handle mask in source */ + if (b_Mask) + { + if (!IsAttribDir(ToAttr)) + { + nlpFileOp.fAnyOperationsAborted = TRUE; + if (b_ToInvalidTail && b_SameTailName && (FO_MOVE == FuncSwitch)) + { + retCode = 0x2; + } + else + { + retCode = 0x75; + } + goto shfileop_error; + } + pToFile = SHFileStrCpyCatA(pTempTo,NULL, "\\"); + nlpFileOp.fFlags = (nlpFileOp.fFlags | FOF_MULTIDESTFILES); + do + { + if (wfd.cFileName[0] == '.') + { + if (wfd.cFileName[1] == '\0') + continue; + if (wfd.cFileName[1] == '.' && wfd.cFileName[2] == '\0') continue; + } + if (IsAttribDir(wfd.dwFileAttributes) && (nlpFileOp.fFlags & FOF_FILESONLY)) + { + continue; /* next name in pTempFrom(dir) */ + } + SHFileStrCpyCatA(&pToFile[1], &wfd.cFileName[0], NULL); + SHFileStrCpyCatA(&pFromFile[1], &wfd.cFileName[0], NULL); + retCode = SHFileOperationA (&nlpFileOp); + } while (!nlpFileOp.fAnyOperationsAborted && FindNextFileA(hFind, &wfd)); + } + FindClose(hFind); + hFind = INVALID_HANDLE_VALUE; + /* only FO_COPY/FO_MOVE without mask, FO_DELETE and FO_RENAME are solved */ + if (b_Mask) + continue; + + /* tailling 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 ?? */ + nlpFileOp.fAnyOperationsAborted = TRUE; + goto shfileop_error; + } + } + pToFile = SHFileStrCpyCatA(&pTempTo[strlen(pTempTo)], "\\", wfd.cFileName); + ToAttr = GetFileAttributesA(pTempTo); + } + + if (IsAttribDir(ToAttr)) + { + if (IsAttribFile(wfd.dwFileAttributes)) + { + if (FO_COPY == FuncSwitch) + { + retCode = 0x75; + } + else + { + retCode = 0xb7; + } + nlpFileOp.fAnyOperationsAborted = TRUE; + goto shfileop_error; + } + } + else + { + pToFile[0] = '\0'; + ToPathAttr = GetFileAttributesA(pTempTo); + pToFile[0] = '\\'; + if (IsAttribFile(ToPathAttr)) + { + /* error, is this already tested? */ + nlpFileOp.fAnyOperationsAborted = TRUE; + retCode = 0x777402; + goto shfileop_error; + } /* endif */ + } + + /* singlesource + no mask */ + if (-1 == (ToAttr & ToPathAttr)) + { + /* Target-dir does not exist, and can not created */ + nlpFileOp.fAnyOperationsAborted = TRUE; + retCode = 0x75; + goto shfileop_error; + } + + switch (FuncSwitch) + { + case FO_MOVE: + if ((ToAttr == -1) && SHFileStrICmpA(pTempFrom, pTempTo, pFromFile, NULL)) + { + nlpFileOp.wFunc = ((level + 1) << 4) + FO_RENAME; + retCode = SHFileOperationA(&nlpFileOp); + } + else + { + if (b_SameRoot && IsAttribDir(ToAttr) && IsAttribDir(wfd.dwFileAttributes)) + { + pToFile = SHFileStrCpyCatA(pTempFrom, NULL, "\\*.*"); + retCode = SHFileOperationA(&nlpFileOp); + ((WORD*)pToFile)[0] = '\0'; + } + else + { + nlpFileOp.wFunc = ((level + 1) << 4) + FO_COPY; + retCode = SHFileOperationA(&nlpFileOp); + } /* endif */ + if (!nlpFileOp.fAnyOperationsAborted) + { + nlpFileOp.wFunc = ((level+1)<<4) + FO_DELETE; + retCode = SHFileOperationA(&nlpFileOp); + } /* endif */ + } + continue; + case FO_COPY: + if (SHFileStrICmpA(pTempFrom, pTempTo, NULL, NULL)) + { /* target is the same as source ? */ + nlpFileOp.fAnyOperationsAborted = TRUE; + /* we need still the value for the returncode, we assume 0x71 */ + retCode = 0x71; + goto shfileop_error; + } /* endif */ + if (IsAttribDir((ToAttr & wfd.dwFileAttributes))) + { + if (IsAttribDir(ToAttr) || SHCreateDirectory(NULL, pTempTo)) + { + /* ??? nlpFileOp.fFlags = (nlpFileOp.fFlags | FOF_MULTIDESTFILES); */ + SHFileStrCpyCatA(pTempFrom, NULL, "\\*.*"); + retCode = SHFileOperationA(&nlpFileOp); + } + else + { + nlpFileOp.fAnyOperationsAborted = TRUE; + retCode = 0x750;/* value unknown */ + } + } + else + { + if (!(ask_overwrite && SHELL_ConfirmDialog (ASK_OVERWRITE_FILE, pTempTo)) + && (not_overwrite)) + { + nlpFileOp.fAnyOperationsAborted = TRUE; + /* we need still the value for the returncode, we use the mostly assumed */ + retCode = 0x73; + goto shfileop_error; + } + if (!(CopyFileA(pTempFrom, pTempTo, FALSE))) + { + nlpFileOp.fAnyOperationsAborted=TRUE; + retCode = 0x77; /* value unknown */ + } + } + } /* end-switch */ + } /* end-while */ +shfileop_normal: + if (!(nlpFileOp.fAnyOperationsAborted)) + retCode = 0; +shfileop_error: + if (hFind != INVALID_HANDLE_VALUE) + FindClose(hFind); + hFind = INVALID_HANDLE_VALUE; + if (pTempFrom) + HeapFree(GetProcessHeap(), 0, pTempFrom); + + TRACE("%s level=%d AnyOpsAborted=%s ret=0x%x, with %s%s%s\n", + pToFuncTXT, level, nlpFileOp.fAnyOperationsAborted ? "TRUE":"FALSE", + retCode, debugstr_a(pFrom), pTo ? " -> ":"", debugstr_a(pTo)); + + lpFileOp->fAnyOperationsAborted = nlpFileOp.fAnyOperationsAborted; + return retCode; } /*************************************************************************