diff --git a/dlls/shell32/shlfileop.c b/dlls/shell32/shlfileop.c index df71a12f254..c786667a47a 100644 --- a/dlls/shell32/shlfileop.c +++ b/dlls/shell32/shlfileop.c @@ -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 @@ -29,9 +31,11 @@ #include "shellapi.h" #include "shlobj.h" #include "shresdef.h" +#define NO_SHLWAPI_STREAM +#include "shlwapi.h" #include "shell32_main.h" #include "undocshell.h" -#include "shlwapi.h" +#include "wine/unicode.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(shell); @@ -41,6 +45,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell); #define IsDotDir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0)))) +#define FO_MASK 0xF + CHAR aWildcardFile[] = {'*','.','*',0}; WCHAR wWildcardFile[] = {'*','.','*',0}; WCHAR wWildcardChars[] = {'*','?',0}; @@ -228,7 +234,6 @@ BOOL SHELL_DeleteFileW(LPCWSTR pszFile, BOOL bShowUI) * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be ANSI. * This is Unicode on NT/2000 */ - static BOOL SHNotifyCreateDirectoryA(LPCSTR path, LPSECURITY_ATTRIBUTES sec) { BOOL ret; @@ -457,8 +462,8 @@ static BOOL SHNotifyCopyFileW(LPCWSTR src, LPCWSTR dest, BOOL bRename) * Create a directory at the specified location * * PARAMS - * hWnd [I] - * path [I] path of directory to create + * hWnd [I] + * path [I] path of directory to create * * RETURNS * ERRROR_SUCCESS or one of the following values: @@ -537,288 +542,690 @@ DWORD WINAPI SHCreateDirectoryExW(HWND hWnd, LPCWSTR path, LPSECURITY_ATTRIBUTES return ret; } +/************************************************************************* + * + * SHFileStrICmp HelperFunction for SHFileOperationW + * + */ +BOOL SHFileStrICmpW(LPWSTR p1, LPWSTR p2, LPWSTR p1End, LPWSTR p2End) +{ + WCHAR C1 = '\0'; + WCHAR C2 = '\0'; + int i_Temp = -1; + int i_len1 = lstrlenW(p1); + int i_len2 = lstrlenW(p2); + + if (p1End && (&p1[i_len1] >= p1End) && ('\\' == p1End[0])) + { + C1 = p1End[0]; + p1End[0] = '\0'; + i_len1 = lstrlenW(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 = lstrlenW(p2); + if (i_len1 == i_len2) + i_Temp = lstrcmpiW(p1,p2); + if (C1) + p1[i_len1] = C1; + if (C2) + p2[i_len2] = C2; + return !(i_Temp); +} + +/************************************************************************* + * + * SHFileStrCpyCat HelperFunction for SHFileOperationW + * + */ +LPWSTR SHFileStrCpyCatW(LPWSTR pTo, LPCWSTR pFrom, LPCWSTR pCatStr) +{ + LPWSTR pToFile = NULL; + int i_len; + if (pTo) + { + if (pFrom) + lstrcpyW(pTo, pFrom); + if (pCatStr) + { + i_len = lstrlenW(pTo); + if ((i_len) && (pTo[--i_len] != '\\')) + i_len++; + pTo[i_len] = '\\'; + if (pCatStr[0] == '\\') + pCatStr++; \ + lstrcpyW(&pTo[i_len+1], pCatStr); + } + pToFile = StrRChrW(pTo,NULL,'\\'); +/* !! termination of the new string-group */ + pTo[(lstrlenW(pTo)) + 1] = '\0'; + } + return pToFile; +} + /************************************************************************** * SHELL_FileNamesMatch() * * Accepts two \0 delimited lists of the file names. Checks whether number of - * files in the both lists is the same. + * files in both lists is the same, and checks also if source-name exists. */ -BOOL SHELL_FileNamesMatch(LPCSTR pszFiles1, LPCSTR pszFiles2) +BOOL SHELL_FileNamesMatch(LPCWSTR pszFiles1, LPCWSTR pszFiles2, BOOL bOnlySrc) { - 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')); -} - -/************************************************************************* - * SHFileOperationA [SHELL32.@] - * - * NOTES - * exported by name - */ -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: + while ((pszFiles1[0] != '\0') && + (bOnlySrc || (pszFiles2[0] != '\0'))) { - 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; + if (NULL == StrPBrkW(pszFiles1, wWildcardChars)) + { + if (-1 == GetFileAttributesW(pszFiles1)) + return FALSE; + } + pszFiles1 += lstrlenW(pszFiles1) + 1; + if (!bOnlySrc) + pszFiles2 += lstrlenW(pszFiles2) + 1; } - - 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); - - default: - FIXME("Unhandled shell file operation %d\n", lpFileOp->wFunc); - } - - return 1; + return ((pszFiles1[0] == '\0') && (bOnlySrc || (pszFiles2[0] == '\0'))); } /************************************************************************* - * SHFileOperationW [SHELL32.@] + * + * SHName(s)Translate HelperFunction for SHFileOperationA + * + * Translates a list of 0 terminated ASCI strings into Unicode. If *wString + * is NULL, only the necessary size of the string is determined and returned, + * otherwise the ASCII strings are copied into it and the buffer is increased + * to point to the location after the final 0 termination char. + */ +DWORD SHNameTranslate(LPWSTR* wString, LPCWSTR* pWToFrom, BOOL more) +{ + DWORD size = 0, aSize = 0; + LPCSTR aString = (LPCSTR)*pWToFrom; + + if (aString) + { + do + { + size = lstrlenA(aString) + 1; + aSize += size; + aString += size; + } while ((size != 1) && more); + /* The two sizes might be different in the case of multibyte chars */ + size = MultiByteToWideChar(CP_ACP, 0, aString, aSize, *wString, 0); + if (*wString) /* only in the second loop */ + { + MultiByteToWideChar(CP_ACP, 0, (LPCSTR)*pWToFrom, aSize, *wString, size); + *pWToFrom = *wString; + *wString += size; + } + } + return size; +} +/************************************************************************* + * SHFileOperationA [SHELL32.@] + * + * Function to copy, move, delete and create one or more files with optional + * user prompts. + * + * PARAMS + * lpFileOp [I/O] pointer to a structure containing all the necessary information * * NOTES - * exported by name + * exported by name */ -DWORD WINAPI SHFileOperationW (LPSHFILEOPSTRUCTW lpFileOp) +DWORD WINAPI SHFileOperationA(LPSHFILEOPSTRUCTA lpFileOp) { - FIXME("(%p):stub.\n", lpFileOp); - return 1; + SHFILEOPSTRUCTW nFileOp = *((LPSHFILEOPSTRUCTW)lpFileOp); + DWORD retCode = 0, size = 0; + LPWSTR wString = NULL; /* we change this in SHNameTranslate */ + + TRACE("SHFileOperationA"); + if (FO_DELETE == (nFileOp.wFunc & FO_MASK)) + nFileOp.pTo = NULL; /* we need a NULL or a valid pointer for translation */ + if (!(nFileOp.fFlags & FOF_SIMPLEPROGRESS)) + nFileOp.lpszProgressTitle = NULL; /* we need a NULL or a valid pointer for translation */ + do + { + if (size) + { + wString = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)); + if (!wString) + { + retCode = ERROR_OUTOFMEMORY; + nFileOp.fAnyOperationsAborted = TRUE; + SetLastError(retCode); + return retCode; + } + } + size = SHNameTranslate(&wString, &nFileOp.lpszProgressTitle, FALSE); /* no loop */ + size += SHNameTranslate(&wString, &nFileOp.pFrom, TRUE); /* internal loop */ + size += SHNameTranslate(&wString, &nFileOp.pTo, TRUE); /* internal loop */ + /* first loop only for calculate size, no translation, we hav a NULL-pointer */ + } while (!wString); /* second loop calculate size, also translation. We have a valid pointer */ + + retCode = SHFileOperationW(&nFileOp); + + if (wString) + HeapFree(GetProcessHeap(), 0, wString); + + if (retCode) + { + nFileOp.fAnyOperationsAborted = TRUE; + } + lpFileOp->hNameMappings = nFileOp.hNameMappings; + lpFileOp->fAnyOperationsAborted = nFileOp.fAnyOperationsAborted; + return retCode; } /************************************************************************* - * SHFileOperation [SHELL32.@] + * SHFileOperationW [SHELL32.@] + * + * See SHFileOperationA + */ +DWORD WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp) +{ + SHFILEOPSTRUCTW nFileOp = *(lpFileOp); + + 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; + LPCSTR cFO_Name [] = {"FO_????","FO_MOVE","FO_COPY","FO_DELETE","FO_RENAME"}; + + 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 */ + + cFO_Name[0] = cFO_Name [FuncSwitch]; + if (level == 0) + TRACE("%s: flags (0x%04x) : %s%s%s%s%s%s%s%s%s%s%s%s%s \n",cFO_Name[0], nFileOp.fFlags, + nFileOp.fFlags & FOF_MULTIDESTFILES ? "FOF_MULTIDESTFILES " : "", + nFileOp.fFlags & FOF_CONFIRMMOUSE ? "FOF_CONFIRMMOUSE " : "", + nFileOp.fFlags & FOF_SILENT ? "FOF_SILENT " : "", + nFileOp.fFlags & FOF_RENAMEONCOLLISION ? "FOF_RENAMEONCOLLISION " : "", + nFileOp.fFlags & FOF_NOCONFIRMATION ? "FOF_NOCONFIRMATION " : "", + nFileOp.fFlags & FOF_WANTMAPPINGHANDLE ? "FOF_WANTMAPPINGHANDLE " : "", + nFileOp.fFlags & FOF_ALLOWUNDO ? "FOF_ALLOWUNDO " : "", + nFileOp.fFlags & FOF_FILESONLY ? "FOF_FILESONLY " : "", + nFileOp.fFlags & FOF_SIMPLEPROGRESS ? "FOF_SIMPLEPROGRESS " : "", + nFileOp.fFlags & FOF_NOCONFIRMMKDIR ? "FOF_NOCONFIRMMKDIR " : "", + nFileOp.fFlags & FOF_NOERRORUI ? "FOF_NOERRORUI " : "", + nFileOp.fFlags & FOF_NOCOPYSECURITYATTRIBS ? "FOF_NOCOPYSECURITYATTRIBS" : "", + nFileOp.fFlags & 0xf000 ? "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, 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", cFO_Name[0], 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", cFO_Name[0], 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", cFO_Name[0], level, OFl); + } /* endif */ + } /* endif */ + + if ((pNextFrom) && (!(b_MultiTo) || (pNextTo))) + { + 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)); + } + 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 (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)); + } /* endif */ + 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; + } + } + + 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; + } /* endif */ + } /* endif */ + /* root (without mask) is also not allowed as source, tested in W98 */ + retCode = 0x402; /* 1026 */ + goto shfileop_error; + } /* endif */ + +/* for all */ +#define HIGH_ADR (LPWSTR)0xffffffff + +/* ??? 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 */ + + 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) + { + 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; + } /* endif */ + /* 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; + } + + /* 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; + + /* 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; + } + } + } + + /* 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 + { + pToFile[0] = '\0'; + ToPathAttr = GetFileAttributesW(pTempTo); + pToFile[0] = '\\'; + if (IsAttribFile(ToPathAttr)) + { + /* error, is this tested ? */ + retCode = 0x777402; + goto shfileop_error; + } /* endif */ + } + + /* 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; + } /* endif */ + 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 (!(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; + } + } + } /* end-switch */ + } /* end-while */ + +shfileop_normal: + 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", + cFO_Name[0], level, nFileOp.fAnyOperationsAborted ? "TRUE":"FALSE", + retCode, debugstr_w(pFrom), pTo ? "-> ":"", debugstr_w(pTo)); + + lpFileOp->fAnyOperationsAborted = nFileOp.fAnyOperationsAborted; + return retCode; +} + +/************************************************************************* + * SHFileOperation [SHELL32.@] * */ DWORD WINAPI SHFileOperationAW(LPVOID lpFileOp) diff --git a/include/shellapi.h b/include/shellapi.h index 907368dd9b7..1644a6b6f64 100644 --- a/include/shellapi.h +++ b/include/shellapi.h @@ -176,6 +176,7 @@ DWORD WINAPI SHGetFileInfoW(LPCWSTR,DWORD,SHFILEINFOW*,UINT,UINT); #define FOF_SIMPLEPROGRESS 0x0100 #define FOF_NOCONFIRMMKDIR 0x0200 #define FOF_NOERRORUI 0x0400 +#define FOF_NOCOPYSECURITYATTRIBS 0x0800 typedef WORD FILEOP_FLAGS;