source: trunk/src/shell32/shlfileop.c

Last change on this file was 21916, checked in by dmik, 14 years ago

Merge branch gcc-kmk to trunk.

File size: 52.1 KB
RevLine 
[4121]1/*
2 * SHFileOperation
[9338]3 *
[9244]4 * Copyright 2000 Juergen Schmied
5 * Copyright 2002 Andriy Palamarchuk
[10314]6 * Copyright 2002-2003 Dietrich Teickner (from Odin)
7 * Copyright 2002-2003 Rolf Kalbermatter
[9244]8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
[10162]22 *
23 * !!! shlfileop.c is shared source between wine and odin, do not remove
[10314]24 * #ifdef __WIN32OS2__ .. lines
[4121]25 */
[9552]26
27#include "config.h"
28#include "wine/port.h"
29
[10314]30#include <stdarg.h>
[4121]31#include <string.h>
[9805]32#include <ctype.h>
[9552]33
[10314]34#include "windef.h"
35#include "winbase.h"
[9338]36#include "winreg.h"
[4121]37#include "shellapi.h"
[10314]38#include "wingdi.h"
39#include "winuser.h"
[4121]40#include "shlobj.h"
41#include "shresdef.h"
[10162]42#define NO_SHLWAPI_STREAM
43#include "shlwapi.h"
[5544]44#include "shell32_main.h"
[8614]45#include "undocshell.h"
[10162]46#include "wine/unicode.h"
[9338]47#include "wine/debug.h"
[4121]48
[10162]49WINE_DEFAULT_DEBUG_CHANNEL(shell);
50
[10314]51#define IsAttribFile(x) (!((x) & FILE_ATTRIBUTE_DIRECTORY))
52#define IsAttrib(x,y) ((INVALID_FILE_ATTRIBUTES != (x)) && ((x) & (y)))
53#define IsAttribDir(x) IsAttrib(x,FILE_ATTRIBUTE_DIRECTORY)
[10162]54
[9833]55#define IsDotDir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
56
[10162]57#define FO_MASK 0xFF
58#define FO_LevelShift 8
59#define HIGH_ADR (LPWSTR)0xffffffff
[4121]60
[10314]61#define FOI_NeverOverwrite 2
62
63CHAR aWildcardFile[] = {'*',0};
[10162]64WCHAR wWildcardChars[] = {'?','*',0};
65#define wWildcardFile &wWildcardChars[1]
[9833]66WCHAR wBackslash[] = {'\\',0};
[10314]67enum { none = 0, w95, w95b, nt351, nt40, w98, w98se, wMe, w2k, wXp};
68static int WOsVers = none; /* for versionsdepended conditions */
69static int retCodeIsInvalid = 0x75; /* w95?,w98se, nt <= 4? */
70static LPCSTR cFO_String [] = {"FO_????","FO_MOVE","FO_COPY","FO_DELETE","FO_RENAME"};
[9833]71
[10314]72static DWORD SHNotifyCreateDirectoryA(LPCSTR path, LPSECURITY_ATTRIBUTES sec);
73static DWORD SHNotifyCreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec);
74static DWORD SHNotifyRemoveDirectoryA(LPCSTR path);
75static DWORD SHNotifyRemoveDirectoryW(LPCWSTR path);
76static DWORD SHNotifyDeleteFileA(LPCSTR path);
77static DWORD SHNotifyDeleteFileW(LPCWSTR path);
78static DWORD SHNotifyMoveFileW(LPCWSTR src, LPCWSTR dest);
79static DWORD SHNotifyCopyFileW(LPCWSTR, LPCWSTR, BOOL);
[9833]80
81typedef struct
[5576]82{
[9808]83 UINT caption_resource_id, text_resource_id;
[9833]84} SHELL_ConfirmIDstruc;
[5576]85
[9833]86static BOOL SHELL_ConfirmIDs(int nKindOfDialog, SHELL_ConfirmIDstruc *ids)
87{
88 switch (nKindOfDialog) {
89 case ASK_DELETE_FILE:
90 ids->caption_resource_id = IDS_DELETEITEM_CAPTION;
91 ids->text_resource_id = IDS_DELETEITEM_TEXT;
92 return TRUE;
93 case ASK_DELETE_FOLDER:
94 ids->caption_resource_id = IDS_DELETEFOLDER_CAPTION;
95 ids->text_resource_id = IDS_DELETEITEM_TEXT;
96 return TRUE;
97 case ASK_DELETE_MULTIPLE_ITEM:
98 ids->caption_resource_id = IDS_DELETEITEM_CAPTION;
99 ids->text_resource_id = IDS_DELETEMULTIPLE_TEXT;
100 return TRUE;
101 case ASK_OVERWRITE_FILE:
102 ids->caption_resource_id = IDS_OVERWRITEFILE_CAPTION;
103 ids->text_resource_id = IDS_OVERWRITEFILE_TEXT;
104 return TRUE;
105 default:
106 FIXME(" Unhandled nKindOfDialog %d stub\n", nKindOfDialog);
107 }
108 return FALSE;
109}
[5576]110
[9833]111BOOL SHELL_ConfirmDialog(int nKindOfDialog, LPCSTR szDir)
112{
113 CHAR szCaption[255], szText[255], szBuffer[MAX_PATH + 256];
114 SHELL_ConfirmIDstruc ids;
[5576]115
[9833]116 if (!SHELL_ConfirmIDs(nKindOfDialog, &ids))
117 return FALSE;
[4121]118
[9833]119 LoadStringA(shell32_hInstance, ids.caption_resource_id, szCaption, sizeof(szCaption));
120 LoadStringA(shell32_hInstance, ids.text_resource_id, szText, sizeof(szText));
[4121]121
[9833]122 FormatMessageA(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
[21916]123 szText, 0, 0, szBuffer, sizeof(szBuffer), (LPDWORD)&szDir);
[9833]124
125 return (IDOK == MessageBoxA(GetActiveWindow(), szBuffer, szCaption, MB_OKCANCEL | MB_ICONEXCLAMATION));
[4121]126}
127
[9833]128BOOL SHELL_ConfirmDialogW(int nKindOfDialog, LPCWSTR szDir)
129{
130 WCHAR szCaption[255], szText[255], szBuffer[MAX_PATH + 256];
131 SHELL_ConfirmIDstruc ids;
132
133 if (!SHELL_ConfirmIDs(nKindOfDialog, &ids))
134 return FALSE;
135
136 LoadStringW(shell32_hInstance, ids.caption_resource_id, szCaption, sizeof(szCaption));
137 LoadStringW(shell32_hInstance, ids.text_resource_id, szText, sizeof(szText));
138
139 FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
[21916]140 szText, 0, 0, szBuffer, sizeof(szBuffer), (LPDWORD)&szDir);
[9833]141
142 return (IDOK == MessageBoxW(GetActiveWindow(), szBuffer, szCaption, MB_OKCANCEL | MB_ICONEXCLAMATION));
143}
144
[4121]145/**************************************************************************
[10162]146 * SHELL_DeleteDirectoryA() [internal]
[4121]147 *
148 * like rm -r
149 */
150BOOL SHELL_DeleteDirectoryA(LPCSTR pszDir, BOOL bShowUI)
151{
[9833]152 BOOL ret = TRUE;
153 HANDLE hFind;
154 WIN32_FIND_DATAA wfd;
155 char szTemp[MAX_PATH];
[4121]156
[9808]157 /* Make sure the directory exists before eventually prompting the user */
[9833]158 PathCombineA(szTemp, pszDir, aWildcardFile);
159 hFind = FindFirstFileA(szTemp, &wfd);
[9808]160 if (hFind == INVALID_HANDLE_VALUE)
161 return FALSE;
[9552]162
[9808]163 if (!bShowUI || SHELL_ConfirmDialog(ASK_DELETE_FOLDER, pszDir))
164 {
165 do
166 {
[9833]167 LPSTR lp = wfd.cAlternateFileName;
168 if (!lp[0])
169 lp = wfd.cFileName;
170 if (IsDotDir(lp))
171 continue;
172 PathCombineA(szTemp, pszDir, lp);
173 if (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
174 ret = SHELL_DeleteDirectoryA(szTemp, FALSE);
175 else
[10314]176 ret = (SHNotifyDeleteFileA(szTemp) == ERROR_SUCCESS);
[9808]177 } while (ret && FindNextFileA(hFind, &wfd));
178 }
179 FindClose(hFind);
180 if (ret)
[10314]181 ret = (SHNotifyRemoveDirectoryA(pszDir) == ERROR_SUCCESS);
[9808]182 return ret;
[4121]183}
184
[9833]185BOOL SHELL_DeleteDirectoryW(LPCWSTR pszDir, BOOL bShowUI)
186{
187 BOOL ret = TRUE;
188 HANDLE hFind;
189 WIN32_FIND_DATAW wfd;
190 WCHAR szTemp[MAX_PATH];
191
192 /* Make sure the directory exists before eventually prompting the user */
193 PathCombineW(szTemp, pszDir, wWildcardFile);
194 hFind = FindFirstFileW(szTemp, &wfd);
195 if (hFind == INVALID_HANDLE_VALUE)
196 return FALSE;
197
198 if (!bShowUI || SHELL_ConfirmDialogW(ASK_DELETE_FOLDER, pszDir))
[10314]199
[9833]200 {
201 do
202 {
203 LPWSTR lp = wfd.cAlternateFileName;
204 if (!lp[0])
205 lp = wfd.cFileName;
206 if (IsDotDir(lp))
207 continue;
208 PathCombineW(szTemp, pszDir, lp);
209 if (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
210 ret = SHELL_DeleteDirectoryW(szTemp, FALSE);
211 else
[10314]212 ret = (SHNotifyDeleteFileW(szTemp) == ERROR_SUCCESS);
[9833]213 } while (ret && FindNextFileW(hFind, &wfd));
214 }
215 FindClose(hFind);
216 if (ret)
[10314]217 ret = (SHNotifyRemoveDirectoryW(pszDir) == ERROR_SUCCESS);
[9833]218 return ret;
219}
220
[5544]221/**************************************************************************
[10162]222 * SHELL_DeleteFileA() [internal]
[5544]223 */
224BOOL SHELL_DeleteFileA(LPCSTR pszFile, BOOL bShowUI)
225{
[9808]226 if (bShowUI && !SHELL_ConfirmDialog(ASK_DELETE_FILE, pszFile))
227 return FALSE;
228
[10314]229 return (SHNotifyDeleteFileA(pszFile) == ERROR_SUCCESS);
[5544]230}
231
[9833]232BOOL SHELL_DeleteFileW(LPCWSTR pszFile, BOOL bShowUI)
233{
234 if (bShowUI && !SHELL_ConfirmDialogW(ASK_DELETE_FILE, pszFile))
235 return FALSE;
236
[10314]237 return (SHNotifyDeleteFileW(pszFile) == ERROR_SUCCESS);
[9833]238}
239
[9808]240/**************************************************************************
[10162]241 * Win32CreateDirectory [SHELL32.93]
[4121]242 *
[9808]243 * Creates a directory. Also triggers a change notify if one exists.
[10162]244 *
245 * PARAMS
246 * path [I] path to directory to create
247 *
248 * RETURNS
249 * TRUE if successful, FALSE otherwise
250 *
251 * NOTES:
252 * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be ANSI.
253 * This is Unicode on NT/2000
[4121]254 */
[10314]255static DWORD SHNotifyCreateDirectoryA(LPCSTR path, LPSECURITY_ATTRIBUTES sec)
[4121]256{
[10314]257 WCHAR wPath[MAX_PATH];
[9833]258 TRACE("(%s, %p)\n", debugstr_a(path), sec);
[9808]259
[10314]260 MultiByteToWideChar(CP_ACP, 0, path, -1, wPath, MAX_PATH);
261 return SHNotifyCreateDirectoryW(wPath, sec);
[9808]262}
263
[10314]264static DWORD SHNotifyCreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec)
[9808]265{
[9833]266 TRACE("(%s, %p)\n", debugstr_w(path), sec);
[9808]267
[10314]268 if (StrPBrkW(path, wWildcardChars))
[9808]269 {
[10314]270 /* FIXME: This test is necessary since our CreateDirectory implementation
271 does create directories with wildcard chars without objection. Once this
272 is fixed, this can go away. */
273 SetLastError(ERROR_INVALID_NAME);
274 if (w98se >= WOsVers) /* wMe ? */
275 return ERROR_FILE_NOT_FOUND; /* w98se */
276 return ERROR_INVALID_NAME; /* w2k */
277 }
278
279 if (CreateDirectoryW(path, sec))
280 {
[9808]281 SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW, path, NULL);
[10314]282 return ERROR_SUCCESS;
[9808]283 }
[10314]284 return GetLastError();
[9808]285}
286
[9833]287BOOL WINAPI Win32CreateDirectoryAW(LPCVOID path, LPSECURITY_ATTRIBUTES sec)
[9808]288{
[9805]289 if (SHELL_OsIsUnicode())
[10314]290 return (SHNotifyCreateDirectoryW(path, sec) == ERROR_SUCCESS);
291 return (SHNotifyCreateDirectoryA(path, sec) == ERROR_SUCCESS);
[9805]292}
293
[9808]294/************************************************************************
[10162]295 * Win32RemoveDirectory [SHELL32.94]
[9808]296 *
297 * Deletes a directory. Also triggers a change notify if one exists.
[10162]298 *
299 * PARAMS
300 * path [I] path to directory to delete
301 *
302 * RETURNS
[10314]303 * ERROR_SUCCESS if successful
[10162]304 *
305 * NOTES:
306 * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be ANSI.
307 * This is Unicode on NT/2000
[9805]308 */
[10314]309
310static DWORD SHNotifyRemoveDirectoryA(LPCSTR path)
[9805]311{
[10314]312 WCHAR wPath[MAX_PATH];
[9833]313 TRACE("(%s)\n", debugstr_a(path));
314
[10314]315 MultiByteToWideChar(CP_ACP, 0, path, -1, wPath, MAX_PATH);
316 return SHNotifyRemoveDirectoryW(wPath);
[4121]317}
318
[10314]319/***********************************************************************/
320
321static DWORD SHNotifyRemoveDirectoryW(LPCWSTR path)
[9805]322{
[9808]323 BOOL ret;
[9833]324 TRACE("(%s)\n", debugstr_w(path));
325
[9808]326 ret = RemoveDirectoryW(path);
327 if (!ret)
328 {
[9833]329 /* Directory may be write protected */
330 DWORD dwAttr = GetFileAttributesW(path);
[10314]331 if (IsAttrib(dwAttr,FILE_ATTRIBUTE_READONLY))
[9833]332 if (SetFileAttributesW(path, dwAttr & ~FILE_ATTRIBUTE_READONLY))
333 ret = RemoveDirectoryW(path);
[9808]334 }
[9805]335 if (ret)
[10314]336 {
[9808]337 SHChangeNotify(SHCNE_RMDIR, SHCNF_PATHW, path, NULL);
[10314]338 return ERROR_SUCCESS;
339 }
340 return GetLastError();
[9805]341}
342
[10314]343/***********************************************************************/
344
[9808]345BOOL WINAPI Win32RemoveDirectoryAW(LPCVOID path)
346{
347 if (SHELL_OsIsUnicode())
[10314]348 return (SHNotifyRemoveDirectoryW(path) == ERROR_SUCCESS);
349 return (SHNotifyRemoveDirectoryA(path) == ERROR_SUCCESS);
[9808]350}
351
[4121]352/************************************************************************
[10162]353 * Win32DeleteFile [SHELL32.164]
[4121]354 *
[9805]355 * Deletes a file. Also triggers a change notify if one exists.
[4121]356 *
[10162]357 * PARAMS
358 * path [I] path to file to delete
359 *
360 * RETURNS
361 * TRUE if successful, FALSE otherwise
362 *
[9805]363 * NOTES:
364 * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be ANSI.
365 * This is Unicode on NT/2000
[5654]366 */
[10314]367
368static DWORD SHNotifyDeleteFileA(LPCSTR path)
[4121]369{
[10314]370 WCHAR wPath[MAX_PATH];
[9808]371 TRACE("(%s)\n", debugstr_a(path));
372
[10314]373 MultiByteToWideChar(CP_ACP, 0, path, -1, wPath, MAX_PATH);
374 return SHNotifyDeleteFileW(wPath);
[4121]375}
376
[10314]377/***********************************************************************/
378
379static DWORD SHNotifyDeleteFileW(LPCWSTR path)
[9805]380{
381 BOOL ret;
[9833]382
[9808]383 TRACE("(%s)\n", debugstr_w(path));
[9805]384
[9808]385 ret = DeleteFileW(path);
386 if (!ret)
[9805]387 {
388 /* File may be write protected or a system file */
[9808]389 DWORD dwAttr = GetFileAttributesW(path);
[10314]390 if (IsAttrib(dwAttr,FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))
[9808]391 if (SetFileAttributesW(path, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
392 ret = DeleteFileW(path);
[9805]393 }
394 if (ret)
[10314]395 {
[9808]396 SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, path, NULL);
[10314]397 return ERROR_SUCCESS;
398 }
399 return GetLastError();
[9805]400}
401
[10314]402/***********************************************************************/
403
[9808]404DWORD WINAPI Win32DeleteFileAW(LPCVOID path)
[9805]405{
406 if (SHELL_OsIsUnicode())
[10314]407 return (SHNotifyDeleteFileW(path) == ERROR_SUCCESS);
408 return (SHNotifyDeleteFileA(path) == ERROR_SUCCESS);
[9805]409}
410
[10162]411/************************************************************************
412 * SHNotifyMoveFile [internal]
413 *
414 * Moves a file. Also triggers a change notify if one exists.
415 *
416 * PARAMS
417 * src [I] path to source file to move
418 * dest [I] path to target file to move to
419 *
420 * RETURNS
[10314]421 * NO_ERROR if successful, or an error code otherwise
[10162]422 */
[10314]423static DWORD SHNotifyMoveFileW(LPCWSTR src, LPCWSTR dest)
[10162]424{
[10314]425 BOOL ret = FALSE;
[10162]426
427 TRACE("(%s %s)\n", debugstr_w(src), debugstr_w(dest));
428
[10314]429 if (StrPBrkW(dest, wWildcardChars))
430 {
431 /* FIXME: This test is currently necessary since our MoveFile
432 implementation does create files with wildcard characters
433 without objection!! Once this is fixed, this here can go away. */
434 SetLastError(ERROR_INVALID_NAME);
435 if (w98se >= WOsVers) /* wMe ? */
436 return ERROR_FILE_NOT_FOUND; /* w98se */
437 return ERROR_INVALID_NAME; /* w2k,wXp */
438 }
439
[10162]440 ret = MoveFileW(src, dest);
441 if (!ret)
442 {
443 /* Source file may be write protected or a system file */
444 DWORD dwAttr = GetFileAttributesW(src);
[10314]445 if (IsAttrib(dwAttr,FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))
[10162]446 if (SetFileAttributesW(src, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
447 ret = MoveFileW(src, dest);
448 }
449 if (ret)
[10314]450 {
[10162]451 SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_PATHW, src, dest);
[10314]452 return ERROR_SUCCESS;
453 }
454 return GetLastError();
[10162]455}
456
457/************************************************************************
458 * SHNotifyCopyFile [internal]
459 *
460 * Copies a file. Also triggers a change notify if one exists.
461 *
462 * PARAMS
463 * src [I] path to source file to move
464 * dest [I] path to target file to move to
[10314]465 * b_nOverWrt [I] if TRUE, the target file will not be overwritten, if a
[10162]466 * file with this name already exists
467 *
468 * RETURNS
[10314]469 * NO_ERROR if successful, or an error code otherwise
[10162]470 */
[10314]471static DWORD SHNotifyCopyFileW(LPCWSTR src, LPCWSTR dest, BOOL b_nOverWrt)
[10162]472{
473 BOOL ret;
474
[10314]475 TRACE("(%s %s %s)\n", debugstr_w(src), debugstr_w(dest), b_nOverWrt ? "OverWriteIfExists" : "");
[10162]476
[10314]477 ret = CopyFileW(src, dest, b_nOverWrt);
478 if (ret)
[10162]479 {
[10314]480 SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, dest, NULL);
481 return ERROR_SUCCESS;
[10162]482 }
[10314]483 return GetLastError();
[10162]484}
485
[10314]486
[4121]487/*************************************************************************
[10162]488 * SHCreateDirectory [SHELL32.165]
[9338]489 *
[10162]490 * Create a directory at the specified location
491 *
492 * PARAMS
493 * hWnd [I]
494 * path [I] path of directory to create
495 *
496 * RETURNS
497 * ERRROR_SUCCESS or one of the following values:
498 * ERROR_BAD_PATHNAME if the path is relative
[10314]499 * ERROR_INVLID_NAME if the path contains invalid chars
[10162]500 * ERROR_ALREADY_EXISTS when the directory already exists
501 * ERROR_FILENAME_EXCED_RANGE if the filename was to long to process
502 *
[9808]503 * NOTES
504 * exported by ordinal
[10162]505 * Win9x exports ANSI
[9808]506 * WinNT/2000 exports Unicode
[9338]507 */
[9808]508DWORD WINAPI SHCreateDirectory(HWND hWnd, LPCVOID path)
[9805]509{
[9808]510 if (SHELL_OsIsUnicode())
511 return SHCreateDirectoryExW(hWnd, path, NULL);
512 return SHCreateDirectoryExA(hWnd, path, NULL);
513}
[9805]514
[9808]515/*************************************************************************
[10162]516 * SHCreateDirectoryExA [SHELL32.@]
517 *
518 * Create a directory at the specified location
519 *
520 * PARAMS
521 * hWnd [I]
522 * path [I] path of directory to create
523 * sec [I] security attributes to use or NULL
524 *
525 * RETURNS
526 * ERRROR_SUCCESS or one of the following values:
[10314]527 * ERROR_BAD_PATHNAME or ERROR_PATH_NOT_FOUND if the path is relative
528 * ERROR_INVLID_NAME if the path contains invalid chars
529 * ? ERROR_FILE_EXISTS when a file with that name exists
[10162]530 * ERROR_ALREADY_EXISTS when the directory already exists
531 * ERROR_FILENAME_EXCED_RANGE if the filename was to long to process
[9808]532 */
533DWORD WINAPI SHCreateDirectoryExA(HWND hWnd, LPCSTR path, LPSECURITY_ATTRIBUTES sec)
534{
535 WCHAR wPath[MAX_PATH];
536 TRACE("(%p, %s, %p)\n",hWnd, debugstr_a(path), sec);
537
538 MultiByteToWideChar(CP_ACP, 0, path, -1, wPath, MAX_PATH);
539 return SHCreateDirectoryExW(hWnd, wPath, sec);
540}
541
542/*************************************************************************
[10314]543 * SHCreateDirectoryExW [SHELL32.@]
[9808]544 */
[10314]545DWORD WINAPI SHCreateDirectoryExW(HWND hWnd, LPCWSTR path, LPSECURITY_ATTRIBUTES sec)
[10162]546{
[10314]547 DWORD ret = ERROR_BAD_PATHNAME;
548 TRACE("(%p, %s, %p)\n",hWnd, debugstr_w(path), sec);
549
550 if (PathIsRelativeW(path))
[10162]551 {
[10314]552 SetLastError(ret);
[10162]553 }
[10314]554 else
[10162]555 {
[10314]556 ret = SHNotifyCreateDirectoryW(path, sec);
557 if (ret && ret != ERROR_FILE_EXISTS &&
558 ret != ERROR_ALREADY_EXISTS &&
559 ret != ERROR_FILENAME_EXCED_RANGE)
560 {
561 /* handling network file names?
562 lstrcpynW(pathName, path, MAX_PATH);
563 lpStr = PathAddBackslashW(pathName);*/
564 FIXME("Semi-stub, creating path %s, failed with error %ld?\n", debugstr_w(path), ret);
565 }
[10162]566 }
[10314]567 return ret;
[10162]568}
[10314]569
[10162]570/*************************************************************************
[10314]571 * SHFreeNameMappings [shell32.246]
572 *
573 * Free the mapping handle returned by SHFileoperation if FOF_WANTSMAPPINGHANDLE
574 * was specified.
575 *
576 * PARAMS
577 * hNameMapping [I] handle to the name mappings used during renaming of files
578 *
[10162]579 */
[10314]580void WINAPI SHFreeNameMappings(HANDLE hNameMapping)
[9808]581{
[10314]582 if (hNameMapping)
583 {
584 LPSHNAMEMAPPINGW lp;
585 UINT i = 0;
[9808]586
[10314]587 while (NULL != (lp = DSA_GetItemPtr((struct _DSA* const)hNameMapping, i++)))
[10162]588 {
[10314]589 SHFree(lp->pszOldPath);
590 SHFree(lp->pszNewPath);
[10162]591 }
[10314]592 DSA_Destroy((struct _DSA*)hNameMapping);
[9808]593 }
[10314]594}
595
596static BOOL SetIfPointer(LPWSTR pToSlash, WCHAR x)
597{
598 if (pToSlash) *pToSlash = x;
599 return (BOOL)pToSlash;
600}
601/*************************************************************************
602 * SHFindAttrW [internal]
603 *
604 * Get the Attributes for a file or directory. The difference to GetAttributes()
605 * is that this function will also work for paths containing wildcard characters
606 * in its filename.
607
608 * PARAMS
609 * path [I] path of directory or file to check
610 * fileOnly [I] TRUE if only files should be found
611 *
612 * RETURNS
613 * INVALID_FILE_ATTRIBUTES if the path does not exist, the actual attributes of
614 * the first file or directory found otherwise
615 */
616static DWORD SHFindAttrW(LPCWSTR pName, BOOL fileOnly)
617{
618 WIN32_FIND_DATAW wfd;
619 BOOL b_FileMask = fileOnly && (NULL != StrPBrkW(pName, wWildcardChars));
620 DWORD dwAttr = INVALID_FILE_ATTRIBUTES;
621 HANDLE hFind = FindFirstFileW(pName, &wfd);
622
623 TRACE("%s %d\n", debugstr_w(pName), fileOnly);
624 if (INVALID_HANDLE_VALUE != hFind)
[9808]625 {
[10314]626 do
[9805]627 {
[10314]628 if (b_FileMask && IsAttribDir(wfd.dwFileAttributes))
629 continue;
630 dwAttr = wfd.dwFileAttributes;
631 break;
[9808]632 }
[10314]633 while (FindNextFileW(hFind, &wfd));
634 FindClose(hFind);
[9808]635 }
[10314]636 return dwAttr;
[9338]637}
638
639/*************************************************************************
[4121]640 *
[9833]641 * SHFileStrICmp HelperFunction for SHFileOperationW
[9805]642 *
643 */
[10314]644static BOOL SHFileStrICmpW(LPWSTR p1, LPWSTR p2, LPWSTR p1End, LPWSTR p2End)
[9805]645{
[9833]646 WCHAR C1 = '\0';
647 WCHAR C2 = '\0';
[10314]648 int i_Temp = 0 - (toupperW(p1[0]) == toupperW(p2[0]));
[9833]649 int i_len1 = lstrlenW(p1);
650 int i_len2 = lstrlenW(p2);
[9805]651
[10314]652 if (!i_Temp) return FALSE; /* driveletters are different */
[9833]653 if (p1End && (&p1[i_len1] >= p1End) && ('\\' == p1End[0]))
654 {
655 C1 = p1End[0];
656 p1End[0] = '\0';
657 i_len1 = lstrlenW(p1);
658 }
659 if (p2End)
660 {
661 if ((&p2[i_len2] >= p2End) && ('\\' == p2End[0]))
662 {
663 C2 = p2End[0];
664 if (C2)
665 p2End[0] = '\0';
666 }
667 }
668 else
669 {
670 if ((i_len1 <= i_len2) && ('\\' == p2[i_len1]))
671 {
672 C2 = p2[i_len1];
673 if (C2)
674 p2[i_len1] = '\0';
675 }
676 }
677 i_len2 = lstrlenW(p2);
678 if (i_len1 == i_len2)
679 i_Temp = lstrcmpiW(p1,p2);
680 if (C1)
681 p1[i_len1] = C1;
682 if (C2)
683 p2[i_len2] = C2;
684 return !(i_Temp);
[9805]685}
[9833]686
[9808]687/*************************************************************************
688 *
[9833]689 * SHFileStrCpyCat HelperFunction for SHFileOperationW
[9808]690 *
691 */
[10314]692static LPWSTR SHFileStrCpyCatW(LPWSTR pTo, LPCWSTR pFrom, LPCWSTR pCatStr)
[9833]693{
694 LPWSTR pToFile = NULL;
695 int i_len;
696 if (pTo)
697 {
698 if (pFrom)
699 lstrcpyW(pTo, pFrom);
700 if (pCatStr)
701 {
702 i_len = lstrlenW(pTo);
[10314]703 if ((i_len) && ('\\' != pTo[--i_len]))
[9833]704 i_len++;
705 pTo[i_len] = '\\';
[10314]706 if ('\\' == pCatStr[0])
[9833]707 pCatStr++; \
708 lstrcpyW(&pTo[i_len+1], pCatStr);
709 }
710 pToFile = StrRChrW(pTo,NULL,'\\');
[10314]711 /* termination of the new string-group */
[9833]712 pTo[(lstrlenW(pTo)) + 1] = '\0';
713 }
714 return pToFile;
715}
[9805]716
[9959]717/**************************************************************************
718 * SHELL_FileNamesMatch()
719 *
720 * Accepts two \0 delimited lists of the file names. Checks whether number of
[10162]721 * files in both lists is the same, and checks also if source-name exists.
[9959]722 */
[10314]723static BOOL SHELL_FileNamesMatch(LPCWSTR pszFiles1, LPCWSTR pszFiles2, BOOL bOnlySrc)
[9959]724{
[10162]725 LPWSTR pszTemp;
[10314]726
727 TRACE("%s %s %d\n", debugstr_w(pszFiles1), debugstr_w(pszFiles2), bOnlySrc);
728
[10162]729 while ((pszFiles1[0] != '\0') &&
730 (bOnlySrc || (pszFiles2[0] != '\0')))
[9959]731 {
[10162]732 pszTemp = StrChrW(pszFiles1,'\\');
733 /* root (without mask/name) is also not allowed as source, tested in W98 */
734 if (!pszTemp || !pszTemp[1])
735 return FALSE;
736 pszTemp = StrPBrkW(pszFiles1, wWildcardChars);
737 if (pszTemp)
[9959]738 {
[10314]739 WCHAR szMask [MAX_PATH];
740 pszTemp = StrRChrW(pszFiles1, pszTemp, '\\');
[10162]741 if (!pszTemp)
742 return FALSE;
[10314]743 lstrcpynW(szMask, pszFiles1, (pszTemp - pszFiles1) + 1);
744 /* we will check the root of the mask as valid dir */
[10162]745 if (!IsAttribDir(GetFileAttributesW(&szMask[0])))
746 return FALSE;
747 }
748 else
749 {
[10314]750 if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(pszFiles1))
[9959]751 return FALSE;
752 }
753 pszFiles1 += lstrlenW(pszFiles1) + 1;
[10162]754 if (!bOnlySrc)
[9959]755 pszFiles2 += lstrlenW(pszFiles2) + 1;
756 }
[10162]757 return ((pszFiles1[0] == '\0') && (bOnlySrc || (pszFiles2[0] == '\0')));
[9959]758}
759
[9805]760/*************************************************************************
[10162]761 * SHFileOperationCheck
762 */
[10314]763static DWORD SHFileOperationCheck(LPSHFILEOPSTRUCTW lpFileOp, LPCSTR* cFO_Name)
[10162]764{
765 FILEOP_FLAGS OFl = (FILEOP_FLAGS)lpFileOp->fFlags;
766 long retCode = NO_ERROR;
767 long FuncSwitch = (lpFileOp->wFunc & FO_MASK);
[9805]768
[10162]769 /* default no error */
[10314]770 if (none == WOsVers) {
771 OSVERSIONINFOA info;
772 info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
773 GetVersionExA(&info);
774 WOsVers = wMe; /* <= wMe, tested with w98se and partialy nt4,w95 */
775 /* this cases must be more different */
776 if ((VER_PLATFORM_WIN32_WINDOWS == info.dwPlatformId) &&
777 (4 == info.dwMajorVersion) && (10 == info.dwMinorVersion)) {
778 WOsVers = w98se;
779 retCodeIsInvalid = 0x75; /* <= W98se, what is with wMe? */
780 }
781 if (VER_PLATFORM_WIN32_NT == info.dwPlatformId)
782 {
783 if (4 > info.dwMajorVersion)
784 {
785 WOsVers = nt351;
786 retCodeIsInvalid = 0x75;
787 }
788 if (4 == info.dwMajorVersion)
789 {
790 WOsVers = nt40;
791 retCodeIsInvalid = 0x75;
792 }
793 if (5 == info.dwMajorVersion)
794 {
795 WOsVers = w2k;
796 if (0 < info.dwMinorVersion)
797 WOsVers = wXp; /* Longhorn also ? */
798 retCodeIsInvalid = 0x4c7; /* >= W2K */
799 }
800 if (5 < info.dwMajorVersion)
801 {
802 WOsVers = wXp; /* Longhorn ? */
803 retCodeIsInvalid = 0x4c7; /* >= W2K */
804 }
805 }
806 TRACE("WOsVers:%d Id: %ld Ver: %ld.%ld.%ld, %s\n\n", WOsVers, info.dwPlatformId,
807 info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber, info.szCSDVersion);
808 }
[10162]809 if ((FuncSwitch < FO_MOVE) || (FuncSwitch > FO_RENAME))
[9833]810 {
[10162]811 FuncSwitch = 0;
812 retCode = ERROR_INVALID_PARAMETER;
[9833]813 }
[10314]814 cFO_Name[0] = cFO_String [FuncSwitch];
[10162]815
816 if (!(~FO_MASK & lpFileOp->wFunc)) /* ? Level == 0 */
[10314]817 {
818 lpFileOp->hNameMappings = 0;
[10162]819 lpFileOp->fAnyOperationsAborted = FALSE;
820 TRACE("%s: flags (0x%04x) : %s%s%s%s%s%s%s%s%s%s%s%s%s \n",cFO_Name[0], OFl,
821 OFl & FOF_MULTIDESTFILES ? "FOF_MULTIDESTFILES " : "",
822 OFl & FOF_CONFIRMMOUSE ? "FOF_CONFIRMMOUSE " : "",
823 OFl & FOF_SILENT ? "FOF_SILENT " : "",
824 OFl & FOF_RENAMEONCOLLISION ? "FOF_RENAMEONCOLLISION " : "",
825 OFl & FOF_NOCONFIRMATION ? "FOF_NOCONFIRMATION " : "",
826 OFl & FOF_WANTMAPPINGHANDLE ? "FOF_WANTMAPPINGHANDLE " : "",
827 OFl & FOF_ALLOWUNDO ? "FOF_ALLOWUNDO " : "",
828 OFl & FOF_FILESONLY ? "FOF_FILESONLY " : "",
829 OFl & FOF_SIMPLEPROGRESS ? "FOF_SIMPLEPROGRESS " : "",
830 OFl & FOF_NOCONFIRMMKDIR ? "FOF_NOCONFIRMMKDIR " : "",
831 OFl & FOF_NOERRORUI ? "FOF_NOERRORUI " : "",
832 OFl & FOF_NOCOPYSECURITYATTRIBS ? "FOF_NOCOPYSECURITYATTRIBS" : "",
833 OFl & 0xf000 ? "MORE-UNKNOWN-Flags" : "");
[10314]834 OFl &= (~(FOF_FILESONLY | FOF_WANTMAPPINGHANDLE | FOF_NOCONFIRMATION |
835 FOF_RENAMEONCOLLISION | FOF_MULTIDESTFILES)); /* implemented */
[10162]836 OFl ^= (FOF_SILENT | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_NOCOPYSECURITYATTRIBS); /* ignored, if one */
837 OFl &= (~FOF_SIMPLEPROGRESS); /* ignored, only with FOF_SILENT */
838 if (OFl)
839 {
[10314]840 if (OFl & (~(FOF_CONFIRMMOUSE | FOF_SILENT |
[10162]841 FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_NOCOPYSECURITYATTRIBS)))
842 {
843 TRACE("%s lpFileOp->fFlags=0x%x not implemented, Aborted=TRUE, stub\n", cFO_Name[0], OFl);
844 retCode = ERROR_INVALID_PARAMETER;
845 }
846 else
847 {
848 TRACE("%s lpFileOp->fFlags=0x%x not fully implemented, stub\n", cFO_Name[0], OFl);
849 } /* endif */
850 } /* endif */
851 }
[9833]852 return retCode;
853}
[10314]854
[9833]855/*************************************************************************
856 *
[10314]857 * SHCreateMappingElement internal HelperFunction for SHRenameOnCollision
858 *
859 * Old/New MappingNameElement
860 */
861static LPWSTR SHCreateMappingElement(PINT size, LPWSTR pName)
862{
863 LPWSTR pMapName = NULL;
864 CHAR TempPath [MAX_PATH];
865 int isize = lstrlenW(pName)+1;
866 int oldsize = isize;
867 if ((w98se >= WOsVers) && (nt40 != WOsVers) && (nt351 != WOsVers))
868 {
869 isize = WideCharToMultiByte( CP_ACP, 0, pName, oldsize, TempPath, MAX_PATH, NULL, NULL );
870 if (0 < isize)
871 pName = (LPWSTR)&TempPath[0];
872 }
873 else
874 {
875 oldsize = oldsize*sizeof(WCHAR);
876 }
877 pMapName = SHAlloc(oldsize);
878 memcpy(pMapName,pName,oldsize);
879 *size = isize-1;
880 return pMapName;
881}
882
883/*************************************************************************
884 *
885 * SHRenameOnCollision internal HelperFunction for SHFileOperationW
886 *
887 * Creates new Names and Checks for existance.
888 *
889 * w98 has problems with some remames on collision, if the original target-name,
890 * or the root of the target-name is equal any former orginal target-name,
891 * and if the root-dir of the target differs from the partiell root-dir of the source.
892 * If we have different target-names or has all target the same root of hear source,
893 * we have the problem not, root of target can be shorter as the full root of source.
894 * I think, that is a mapping-problem.
895 * Move within the same or shorter root can made from fo_rename,
896 * that works every in w98, only move/copy in other roots has this problem,
897 * this must be done in fo_move/fo_copy, there we have the problem.
898 * we ignore this problem, it is solved in w2k.
899 */
900static WCHAR wStrFormat[] = {' ','(','%','d',')',' ',0};
901
902static DWORD SHRenameOnCollision(LPWSTR pTempTo, LPWSTR pToFile, LPCWSTR pFromFile, LPSHFILEOPSTRUCTW lpFileOp)
903{
904/* todo ERROR_FILENAME_EXCED_RANGE ?? */
905 static WCHAR wCopy_x_of[40] = {0};
906 LPWSTR pszTemp = wStrFormat + 5;
907 DWORD ToAttr;
908 WCHAR szNumber[16];
909 WCHAR szOldToName [MAX_PATH];
910 int number = 0;
911
912 while (ToAttr = SHFindAttrW(pTempTo, FALSE),
913 (INVALID_FILE_ATTRIBUTES != ToAttr) && (FOF_RENAMEONCOLLISION & lpFileOp->fFlags))
914 {
915 if (!number)
916 {
917 if (!*wCopy_x_of && !LoadStringW(shell32_hInstance, IDS_COPY_X_OF_TEXT, wCopy_x_of, sizeof(wCopy_x_of)-1))
918 break; /* should never be */
919 lstrcpyW(szOldToName, pTempTo);
920 }
921 number++;
922 if (1 < number)
923 {
924 pszTemp = szNumber;
925 wsprintfW(szNumber, wStrFormat, number);
926 }
927 wsprintfW(pToFile + 1,wCopy_x_of, pszTemp, pFromFile + 1);
928 pToFile[lstrlenW(pToFile)+1] = 0;
929
930 } /* endwhile */
931 if (number)
932 {
933 SHNAMEMAPPINGW shm;
934 shm.pszOldPath = SHCreateMappingElement(&shm.cchOldPath, szOldToName);
935 shm.pszNewPath = SHCreateMappingElement(&shm.cchNewPath, pTempTo);
936 if (!lpFileOp->hNameMappings)
937 lpFileOp->hNameMappings = DSA_Create(sizeof(SHNAMEMAPPINGW),1);
938 DSA_InsertItem (lpFileOp->hNameMappings, ((HDSA)lpFileOp->hNameMappings)->nItemCount, &shm);
939 }
940 return ToAttr;
941}
942/*************************************************************************
943 *
[10162]944 * SHFileOperationMove HelperFunction for SHFileOperationW
945 *
946 * Contains the common part of FO_MOVE/FO_RENAME.
947 * It is not with recursive call solvable.
948 * We have both tryed FO_RENAME contains not FO_MOVE and
949 * also FO_RENAME does not contains FO_MOVE, only common Parts.
[9833]950 */
[10314]951static DWORD SHFileOperationMove(LPSHFILEOPSTRUCTW lpFileOp)
[10162]952{
953 WCHAR szToName [MAX_PATH + 4];
[10314]954 LPCWSTR pTo = lpFileOp->pTo;
955 int i_lenTo;
956 if (NULL != StrPBrkW(pTo, wWildcardChars))
[10162]957 {
[10314]958 if (w98se >= WOsVers) /* wMe? */
959 return ERROR_FILE_NOT_FOUND; /* w98se */
960 return ERROR_INVALID_NAME; /* w2k */
[10162]961 }
[10314]962
[10162]963 /* FOF_NOCONFIRMATION, FOF_RENAMEONCOLLISION ? */
[10314]964 i_lenTo = lstrlenW(pTo);
965 if (i_lenTo && ('\\' == pTo[--i_lenTo]))
[10162]966 {
[10314]967 lstrcpynW(szToName, pTo, i_lenTo + 1);
968 pTo = &szToName[0];
[10162]969 }
[10314]970 if (INVALID_FILE_ATTRIBUTES != SHFindAttrW(pTo, FALSE))
[10162]971 {
[10314]972 return ERROR_ALREADY_EXISTS;
[10162]973 }
[10314]974 /* we use SHNotifyMoveFile() instead of MoveFileW */
975 return SHNotifyMoveFileW(lpFileOp->pFrom, pTo);
[10162]976}
977/*************************************************************************
978 * SHFileOperationW [SHELL32.@]
979 *
980 * See SHFileOperationA
981 */
[9833]982DWORD WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
983{
[10162]984 SHFILEOPSTRUCTW nFileOp = lpFileOp ? *(lpFileOp):nFileOp;
[9805]985
[9833]986 LPCWSTR pNextFrom = nFileOp.pFrom;
987 LPCWSTR pNextTo = nFileOp.pTo;
988 LPCWSTR pFrom = pNextFrom;
989 LPCWSTR pTo = NULL;
990 HANDLE hFind = INVALID_HANDLE_VALUE;
991 WIN32_FIND_DATAW wfd;
992 LPWSTR pTempFrom = NULL;
993 LPWSTR pTempTo = NULL;
994 LPWSTR pFromFile;
[10162]995 LPWSTR pToFile = NULL;
996 LPWSTR pToTailSlash; /* points also to ToMask, we know not what is this */
997 LPWSTR lpFileName; /* temporary for pFromFile,pToFile,pToTailSlash */
[9833]998 DWORD ToAttr;
999 DWORD ToPathAttr;
[10162]1000 DWORD FromAttr;
1001
[10314]1002 long FuncSwitch = (nFileOp.wFunc & FO_MASK);
1003 long level= nFileOp.wFunc>>FO_LevelShift;
[9833]1004
[10314]1005 BOOL b_Multi = (0 != (nFileOp.fFlags & FOF_MULTIDESTFILES));
1006
1007 BOOL b_MultiTo = (FO_DELETE != FuncSwitch);
1008 BOOL b_MultiPaired = (0 < level) /* FALSE only at Level 0 */;
[9833]1009 BOOL b_MultiFrom = FALSE;
[10314]1010 BOOL b_RenamOnColl = (0 != (nFileOp.fFlags & FOF_RENAMEONCOLLISION));
1011 BOOL b_NoConfirm = (0 != (nFileOp.fFlags & FOF_NOCONFIRMATION));
1012 BOOL b_ask_overwrite = (!b_NoConfirm && !b_RenamOnColl);
1013 BOOL b_not_overwrite = (!b_NoConfirm || b_RenamOnColl);
1014 BOOL b_DELETE = (FO_DELETE == FuncSwitch);
[9833]1015 BOOL b_SameRoot;
[10162]1016 BOOL b_SameRoot_MOVE;
1017 BOOL b_ToMask = FALSE;
1018 BOOL b_FromMask;
[10314]1019 BOOL b_FileMask;
[9833]1020
[10162]1021 LPCSTR cFO_Name;
1022 long retCode = SHFileOperationCheck(&nFileOp,&cFO_Name);
[9805]1023
[10162]1024 if (retCode)
1025 {
[10314]1026 if (('?' == cFO_Name[3]) && (/* wMe ?*/ w98se >= WOsVers))
1027 retCode = NO_ERROR; /* nt4.0, w95, w98se returns no error, w2k, wXp not */
[10162]1028 goto shfileop_exit; /* no valid FunctionCode */
1029 }
[9805]1030
[10162]1031 /* establish when pTo is interpreted as the name of the destination file
1032 * or the directory where the Fromfile should be copied to.
1033 * This depends on:
1034 * (1) pTo points to the name of an existing directory;
1035 * (2) the flag FOF_MULTIDESTFILES is present;
1036 * (3) whether pFrom point to multiple filenames.
1037 *
1038 * Some experiments:
1039 *
1040 * destisdir 1 1 1 1 0 0 0 0
1041 * FOF_MULTIDESTFILES 1 1 0 0 1 1 0 0
1042 * multiple from filenames 1 0 1 0 1 0 1 0
1043 * ---------------
1044 * copy files to dir 1 0 1 1 0 0 1 0
1045 * create dir 0 0 0 0 0 0 1 0
1046 */
[9808]1047/*
[10162]1048 * FOF_MULTIDESTFILES, FOF_NOCONFIRMATION, FOF_FILESONLY are implemented
1049 * FOF_CONFIRMMOUSE, FOF_SILENT, FOF_NOCONFIRMMKDIR,
1050 * FOF_SIMPLEPROGRESS, FOF_NOCOPYSECURITYATTRIBS are not implemented and ignored
1051 * FOF_RENAMEONCOLLISION are implemented partially and breaks if file exist
1052 * FOF_ALLOWUNDO, FOF_WANTMAPPINGHANDLE are not implemented and breaks
[5576]1053 * if any other flag set, an error occurs
[9808]1054 */
[10162]1055 TRACE(" %s level=%ld nFileOp.fFlags=0x%x\n", cFO_Name, level, nFileOp.fFlags);
[5576]1056
[9833]1057 if ((pNextFrom) && (!(b_MultiTo) || (pNextTo)))
1058 {
[10314]1059 nFileOp.pFrom = pTempFrom = SHAlloc(((1 + 2 * (b_MultiTo)) * MAX_PATH + 6) * sizeof(WCHAR));
[9959]1060 if (!pTempFrom)
1061 {
1062 retCode = ERROR_OUTOFMEMORY;
1063 SetLastError(retCode);
[10162]1064 goto shfileop_exit;
[9959]1065 }
[9833]1066 if (b_MultiTo)
1067 pTempTo = &pTempFrom[MAX_PATH + 4];
1068 nFileOp.pTo = pTempTo;
[10314]1069 if (0 == level)
1070 {
1071 if ((0 != (nFileOp.fFlags & FOF_NOCONFIRMATION)) && b_RenamOnColl)
1072 nFileOp.fAnyOperationsAborted |= FOI_NeverOverwrite;
1073 }
[9833]1074 }
1075 else
1076 {
[10314]1077 retCode = 0x402; /* 1026 */
[10162]1078 goto shfileop_exit;
[9833]1079 }
[10162]1080 /* need break at error before change sourcepointer */
1081 while(!retCode && (pNextFrom[0]))
[9833]1082 {
[10162]1083 nFileOp.wFunc = ((level + 1) << FO_LevelShift) + FuncSwitch;
[9833]1084 nFileOp.fFlags = lpFileOp->fFlags;
[5576]1085
[9833]1086 if (b_MultiTo)
1087 {
1088 pTo = pNextTo;
1089 pNextTo = &pNextTo[lstrlenW(pTo)+1];
[10314]1090 b_MultiTo = (b_Multi && (0 != pNextTo[0]));
[9833]1091 }
[5576]1092
[9833]1093 pFrom = pNextFrom;
[10314]1094 pNextFrom = &pNextFrom[lstrlenW(pNextFrom) + 1];
[9833]1095 if (!b_MultiFrom && !b_MultiTo)
[10314]1096 b_MultiFrom = (0 != pNextFrom[0]);
[5623]1097
[9833]1098 pFromFile = SHFileStrCpyCatW(pTempFrom, pFrom, NULL);
[10162]1099 b_FromMask = (pFromFile && (NULL != StrPBrkW(&pFromFile[1], wWildcardChars)));
[9552]1100
[9833]1101 if (pTo)
1102 {
1103 pToFile = SHFileStrCpyCatW(pTempTo, pTo, NULL);
[10162]1104 b_ToMask = (NULL != StrPBrkW(pTempTo, wWildcardChars));
[9833]1105 }
[9552]1106
[9833]1107 if (FO_RENAME == FuncSwitch)
1108 {
[10162]1109 if (b_MultiTo || b_MultiFrom || (b_FromMask && !b_ToMask))
[9833]1110 {
[10314]1111 if (/* wMe? */ w2k <= WOsVers)
1112 retCode = ERROR_GEN_FAILURE; /* W2K ERROR_GEN_FAILURE, w95,W98,NT40 returns no error */
[10162]1113 goto shfileop_exit;
[9833]1114 }
1115 }
[9808]1116
[10162]1117 if (!b_MultiPaired)
[9833]1118 {
[10162]1119 b_MultiPaired =
1120 SHELL_FileNamesMatch(lpFileOp->pFrom,
[10314]1121 lpFileOp->pTo, (b_DELETE || !b_Multi || b_MultiFrom));
[9833]1122 } /* endif */
[10162]1123 if (!(b_MultiPaired) || !(pFromFile) || !(pFromFile[1]) || ((pTo) && !(pToFile)))
1124 {
[10314]1125 if (nt40 < WOsVers)
1126 retCode = 0x402; /* 1026, nt40 returns 0 */
[10162]1127 goto shfileop_exit;
1128 }
[5623]1129
[10314]1130 b_FileMask = b_FromMask && (FOF_FILESONLY & nFileOp.fFlags);
1131 if (!b_DELETE)
[9833]1132 {
[10314]1133 b_SameRoot = (toupperW(pTempFrom[0]) == toupperW(pTempTo[0]));
1134 b_SameRoot_MOVE = (b_SameRoot && (FO_MOVE == FuncSwitch));
1135 }
1136 FromAttr = SHFindAttrW(pTempFrom, b_FileMask);
1137 if (INVALID_FILE_ATTRIBUTES == FromAttr)
1138 {
1139 retCode = GetLastError();
1140 if (ERROR_FILE_NOT_FOUND == retCode || ERROR_NO_MORE_FILES == retCode) /* only tested in wXp,w2k,w98 */
1141 {
1142 if (b_FromMask)
1143 {
1144 retCode = NO_ERROR;
1145 }
1146 else
1147 if (!b_SameRoot_MOVE && !b_DELETE)
1148 goto shfileop_IsInvalid;
1149 }
1150 continue;
1151 } /* endif */
1152
1153 if (b_DELETE)
1154 {
[10162]1155 hFind = FindFirstFileW(pFrom, &wfd);
1156 if (INVALID_HANDLE_VALUE == hFind)
[10314]1157 {
1158 goto shfileop_IsInvalid;
1159 }
[9833]1160 do
1161 {
[10162]1162 lpFileName = wfd.cAlternateFileName;
1163 if (!lpFileName[0])
[9833]1164 lpFileName = wfd.cFileName;
[9959]1165 if (IsDotDir(lpFileName) ||
[10314]1166 (b_FileMask && IsAttribDir(wfd.dwFileAttributes)))
[10162]1167 continue;
[9833]1168 SHFileStrCpyCatW(&pFromFile[1], lpFileName, NULL);
1169 /* TODO: Check the SHELL_DeleteFileOrDirectoryW() function in shell32.dll */
1170 if (IsAttribFile(wfd.dwFileAttributes))
1171 {
[10314]1172 if (SHNotifyDeleteFileW(pTempFrom))
[10162]1173 retCode = 0x78; /* value unknown */
[9833]1174 }
1175 else
1176 {
[10162]1177 if (!SHELL_DeleteDirectoryW(pTempFrom, (!(nFileOp.fFlags & FOF_NOCONFIRMATION))))
1178 retCode = 0x79; /* value unknown */
[9833]1179 }
[10162]1180 } while (!retCode && FindNextFileW(hFind, &wfd));
[9833]1181 FindClose(hFind);
1182 continue;
1183 } /* FO_DELETE ends, pTo must be always valid from here */
[5636]1184
[10162]1185 pToTailSlash = NULL;
1186 if ((pToFile) && !(pToFile[1]))
1187 {
1188 pToFile[0] = '\0';
[10314]1189 lpFileName = StrRChrW(pTempTo, NULL, '\\');
[10162]1190 if (lpFileName)
1191 {
1192 pToTailSlash = pToFile;
1193 pToFile = lpFileName;
1194 }
1195 }
[9833]1196 ToPathAttr = ToAttr = GetFileAttributesW(pTempTo);
[10314]1197 if (!IsAttribDir(ToAttr) && SetIfPointer(pToFile, '\0'))
[9833]1198 {
1199 ToPathAttr = GetFileAttributesW(pTempTo);
[10314]1200 *pToFile = '\\';
[9833]1201 }
[10314]1202 SetIfPointer(pToTailSlash,'\\');
1203
[10162]1204 /* FO_RENAME is not only a Filter for FO_MOVE */
[9833]1205 if (FO_RENAME == FuncSwitch)
1206 {
[10314]1207 if (!(b_FromMask) && (b_SameRoot) && SHFileStrICmpW(pTempFrom, pTempTo, NULL, NULL))
1208 {
1209 if (!b_RenamOnColl) /* neu */
1210 /* target is the same as source ? W98 has 0x71, W2K also */
1211 retCode = 0x71;
1212 goto shfileop_exit;
1213 } /* endif */
1214 if ((nt40 == WOsVers) && (b_FromMask))
1215 {
1216 retCode = 0x72; /* only found in nt40, not w98se or w2k */
1217 goto shfileop_exit;
1218 } /* endif */
1219 if ((b_FromMask && !b_ToMask) || !b_SameRoot || \
[10162]1220 !SHFileStrICmpW(pTempFrom, pTempTo, pFromFile, NULL) || \
1221 SHFileStrICmpW(pTempFrom, pTempTo, pFromFile, HIGH_ADR))
[9833]1222 {
[10162]1223 retCode = 0x73; /* must be here or before, not later */
1224 goto shfileop_exit;
[9833]1225 }
[10314]1226 if (!(b_ToMask) && !(pToTailSlash) && IsAttribDir(ToAttr))
[9833]1227 {
[10314]1228 if (IsAttribDir(FromAttr))
1229 ToAttr = SHRenameOnCollision(pTempTo, pToFile, pFromFile, &nFileOp);
1230 if ((w98se >= WOsVers) && IsAttribDir(ToAttr /* can be changed from SHRenameOnCollision */))
1231 {
1232 /* never in W2K */
1233 retCode = ERROR_INVALID_NAME; /* FOF_NOCONFIRMATION ? */
1234 goto shfileop_exit;
1235 }
1236 } /* endif */
1237 if ((INVALID_FILE_ATTRIBUTES != ToPathAttr) || (b_ToMask && (w98se >= WOsVers)))
1238 /* w2k only (INVALID_FILE_ATTRIBUTES != ToPathAttr) */
[9833]1239 {
[10162]1240 retCode = SHFileOperationMove(&nFileOp);
[9833]1241 }
[10314]1242 else
1243 /* W98 returns 0x75, W2K 0x4c7 */
1244 retCode = retCodeIsInvalid;
1245 goto shfileop_exit;
1246 } /* FO_RENAME ends, only FO_MOVE or FO_COPY valid from here */
1247
1248 if (b_FromMask)
[9833]1249 {
[10314]1250 /* FO_COPY/FO_MOVE with mask, FO_DELETE are solved long before */
1251 hFind = FindFirstFileW(pFrom, &wfd);
1252 if (INVALID_HANDLE_VALUE == hFind)
[9833]1253 {
[10314]1254 /* the retcode for this case is unknown */
1255 goto shfileop_IsInvalid;
1256 }
1257 SetIfPointer(pToTailSlash, '\0');
1258 ToAttr = SHFindAttrW(pTempTo, FALSE);
1259 if (b_ToMask && !b_Multi)
1260 nFileOp.fFlags &= ~FOF_RENAMEONCOLLISION;
1261 nFileOp.fFlags &= ~FOF_MULTIDESTFILES;
1262 pToFile = SHFileStrCpyCatW(pTempTo, NULL, wBackslash);
1263 do
1264 {
1265 lpFileName = wfd.cAlternateFileName;
1266 if (!lpFileName[0])
1267 lpFileName = wfd.cFileName;
1268 if (IsDotDir(lpFileName) ||
1269 (b_FileMask && IsAttribDir(wfd.dwFileAttributes)))
1270 continue; /* next name in pTempFrom(dir) */
1271 if (INVALID_FILE_ATTRIBUTES == ToAttr)
[10162]1272 {
[10314]1273 if (b_MultiTo)
1274 {
1275 retCode = retCodeIsInvalid;
1276 break; /* we need the FindClose */
1277 }
1278 if (b_ToMask)
1279 {
1280 if ((w2k <= WOsVers) && IsAttribDir(wfd.dwFileAttributes)) /* w2k,wXp is tested */
1281 {
1282 if (w2k == WOsVers)
1283 retCode = ERROR_ALREADY_EXISTS; /* no root for target exist, and w2k send this, kind of mad */
1284 else
1285 retCode = ERROR_INVALID_NAME; /* wXp */
1286 nFileOp.fAnyOperationsAborted |= TRUE;
1287 break;
1288 }
1289 }
1290 else
1291 {
1292 nFileOp.fFlags |= FOF_MULTIDESTFILES;
1293 SHFileStrCpyCatW(&pToFile[1], lpFileName, NULL);
1294 }
[10162]1295 }
[10314]1296 SHFileStrCpyCatW(&pFromFile[1], lpFileName, NULL);
1297 retCode = SHFileOperationW (&nFileOp);
1298 } while(!retCode && FindNextFileW(hFind, &wfd));
1299 FindClose(hFind);
1300 if (retCodeIsInvalid == retCode)
1301 goto shfileop_IsInvalid;
1302 continue;
1303 }
1304
1305 if ((INVALID_FILE_ATTRIBUTES != (FromAttr | ToAttr)) && b_RenamOnColl && !(b_ToMask) && !(pToTailSlash))
1306 {
1307 if (b_SameRoot_MOVE && !(b_Multi) && IsAttribDir(FromAttr & ToAttr) && SHFileStrICmpW(pTempFrom, pTempTo, HIGH_ADR, HIGH_ADR))
1308 {
1309 retCode = ERROR_FILENAME_EXCED_RANGE; /* in w98/w2k recursive move, we does this not */
1310 if (w98se >= WOsVers) retCode |= 0x10000; /* unexpected, seen in w98se and nt40sp6 */
1311 goto shfileop_exit;
[9833]1312 }
[10314]1313 if ( /* i-means indifferent */
1314// /*bad*/ ( b_SameRoot_MOVE && !(b_Multi) && !(b_MultiFrom) && IsAttribDir(ToAttr) && !(SHFileStrICmpW(pTempFrom, pTempTo, NULL, NULL)))
1315// /*bad*/ ( b_SameRoot_MOVE && !(b_Multi) && (b_MultiFrom) && IsAttribDir(ToAttr) && !(SHFileStrICmpW(pTempFrom, pTempTo, NULL, NULL)))
1316 /*ok*/ ( b_SameRoot_MOVE && (b_Multi) && !(b_MultiFrom) && IsAttribDir(ToAttr) && !(SHFileStrICmpW(pTempFrom, pTempTo, NULL, NULL)))
1317// /*bad*/|| ( b_SameRoot_MOVE && (b_Multi) && (b_MultiFrom) && IsAttribDir(ToAttr) && !(SHFileStrICmpW(pTempFrom, pTempTo, NULL, NULL)))
1318//
1319// /*bad*/|| (!b_SameRoot_MOVE && !(b_Multi) && !(b_MultiFrom) && IsAttribDir(ToAttr) && IsAttribDir(FromAttr))
1320// /*i*/ || (!b_SameRoot_MOVE && !(b_Multi) && (b_MultiFrom) && IsAttribDir(ToAttr) && IsAttribDir(FromAttr))
1321 /*ok*/ || (!b_SameRoot_MOVE && (b_Multi) && !(b_MultiFrom) && IsAttribDir(ToAttr) && IsAttribDir(FromAttr))
1322// /*i*/ || (!b_SameRoot_MOVE && (b_Multi) && (b_MultiFrom) && IsAttribDir(ToAttr) && IsAttribDir(FromAttr))
1323//
1324 /*ok*/ || ( b_SameRoot_MOVE && IsAttribFile(FromAttr | ToAttr) && !(SHFileStrICmpW(pTempFrom, pTempTo, NULL, NULL)))
1325//
1326 /*ok*/ || (!b_SameRoot_MOVE && IsAttribFile(FromAttr | ToAttr))
1327 /*ok*/ || ((FO_COPY == FuncSwitch) && IsAttribDir(FromAttr) && IsAttribFile(ToAttr))
1328 )
1329 {
1330 ToAttr = SHRenameOnCollision(pTempTo, pToFile, pFromFile, &nFileOp);
1331 } /* endif */
1332 } /* endif */
1333 if ((b_SameRoot) && !(b_MultiFrom) && (!b_RenamOnColl || b_SameRoot_MOVE)
1334 && SHFileStrICmpW(pTempFrom, pTempTo, NULL, NULL))
1335 {
1336 if (!b_RenamOnColl && (IsAttribFile(FromAttr))) /* neu */
1337 /* target is the same as source ? W98 has 0x71, W2K also */
1338 retCode = 0x71;
1339 goto shfileop_exit;
1340 } /* endif */
[9552]1341
[10314]1342 /* Analycing for moving SourceName to as TargetName */
1343 if ((b_MultiFrom || !b_Multi) && (
1344 (IsAttribFile(FromAttr) && IsAttribDir(ToAttr))
1345 || (!b_MultiTo && IsAttribDir(ToAttr))
1346 || (!b_MultiTo && IsAttribDir(FromAttr) && b_MultiFrom && !b_SameRoot_MOVE)
1347 ))
1348 {
1349 SHFileStrCpyCatW(pTempTo, NULL, pFromFile);
1350 /* without FOF_MULTIDESTFILES shlfileop will create dir's recursive */
1351 nFileOp.fFlags |= FOF_MULTIDESTFILES;
1352 retCode = SHFileOperationW(&nFileOp);
1353 continue;
1354 }
1355 /* What can we do with one pair and FO_MOVE/FO_COPY ? */
1356 /* w98se, nt40 can create recursive dirs, W2K not! wMe ist not tested */
1357 if (IsAttribDir(ToPathAttr) || !b_SameRoot_MOVE || (w98se >= WOsVers))
1358 if (!b_MultiFrom)
1359 {
1360 if (IsAttribFile(FromAttr))
[9833]1361 {
[10314]1362 if (b_SameRoot_MOVE &&
1363 /* IsAttribDir(ToPathAttr) && !pToTailSlash && (bug) */
1364 /* windows-bug, MOVE for File also with pToTailSlash, COPY not for this */
1365 (!(IsAttribFile(ToAttr)) || (!(b_ask_overwrite) && b_not_overwrite)) &&
1366 (IsAttribDir(ToPathAttr) || b_ToMask))
[9833]1367 {
[10314]1368 /* At the same drive, we can move for FO_MOVE, dir to dir is solved later */
1369 retCode = SHFileOperationMove(&nFileOp);
1370 if ((nt40 == WOsVers) && (ERROR_ALREADY_EXISTS == retCode))
1371 retCode = 0x10005;
1372 continue;
1373 } /* endif */
1374 if (IsAttribDir(ToPathAttr) && !pToTailSlash && !IsAttribDir(ToAttr))
1375 {
1376 if (!(FOI_NeverOverwrite & nFileOp.fAnyOperationsAborted))
[9833]1377 {
[10314]1378 if (!b_not_overwrite)
1379 ToAttr = INVALID_FILE_ATTRIBUTES;
1380 if (IsAttribFile(ToAttr) && b_not_overwrite)
1381 {
1382 if (b_ask_overwrite)
1383 {
1384 /* we must change the dialog in the future for return 3-4 cases,
1385 * 'Yes','No','Yes for all','Never ?' */
1386 if ((INVALID_FILE_ATTRIBUTES != ToAttr) && !SHELL_ConfirmDialogW(ASK_OVERWRITE_FILE, pTempTo))
1387 {
1388 retCode = 0x10008;
1389 nFileOp.fAnyOperationsAborted |= TRUE;
1390 continue;
1391 }
1392 ToAttr = INVALID_FILE_ATTRIBUTES;
1393 } /* endif */
1394 } /* endif */
[10162]1395 } /* endif */
[10314]1396 if (INVALID_FILE_ATTRIBUTES == ToAttr)
[10162]1397 {
[10314]1398 if (SHNotifyCopyFileW(pTempFrom, pTempTo, nFileOp.fFlags & FOF_RENAMEONCOLLISION))
[10162]1399 {
[10314]1400 /* the retcode for this case is unknown */
[10162]1401 retCode = 0x10009;
1402 }
1403 if ((FO_COPY == FuncSwitch) || retCode)
1404 continue;
1405 nFileOp.wFunc = ((level+1) << FO_LevelShift) + FO_DELETE;
1406 retCode = SHFileOperationW(&nFileOp);
1407 continue;
[9833]1408 }
1409 }
[10314]1410 }
1411 if (IsAttribDir(FromAttr))
1412 {
1413 if (INVALID_FILE_ATTRIBUTES == ToAttr)
[9833]1414 {
[10314]1415 SetIfPointer(pToTailSlash, '\0');
1416 if (w2k <= WOsVers) /* if w2k,wxp not in w98se, nt40sp6 */
[10162]1417 {
[10314]1418 ToAttr = SHRenameOnCollision(pTempTo, pToFile, pFromFile, &nFileOp);
1419 if (INVALID_FILE_ATTRIBUTES != ToAttr)
[10162]1420 {
[10314]1421 if (w2k == WOsVers)
1422 retCode = ERROR_ALREADY_EXISTS; /* w2k */
1423 else
1424 retCode = ERROR_INVALID_NAME; /* wXp */
1425 nFileOp.fAnyOperationsAborted |= TRUE;
1426 goto shfileop_exit;
[10162]1427 }
[10314]1428 lpFileName = &pToFile[lstrlenW(pToFile)];
1429 if (pToTailSlash)
[10162]1430 {
[10314]1431 pToTailSlash = lpFileName;
1432 lpFileName++;
[10162]1433 }
[10314]1434 ((DWORD*)lpFileName)[0] = '\0';
1435 retCode = SHCreateDirectoryExW(NULL,pTempTo, NULL);
1436 }
1437 else
1438 { /* only for < w2k, tested with w98se,nt40sp6 */
1439 if (IsAttribDir(ToPathAttr) || !b_SameRoot_MOVE || b_ToMask)
1440 { /* tested with w98se, partially nt40sp6, w2k(wXp). only w98se,nt40sp6 can loop */
1441 lpFileName = NULL;
1442 while ((lpFileName = StrRChrW(pTempTo,lpFileName, '\\'),
1443 INVALID_FILE_ATTRIBUTES == ToAttr) &&
1444 (':' != lpFileName[-1])) /* not begin with root, or end with root */
1445 {
1446 SetIfPointer(lpFileName, '\0');
1447 ToAttr = GetFileAttributesW(pTempTo); /* for continuing */
1448 SetIfPointer(lpFileName--, '\\');
1449 }
1450 while (lpFileName && lpFileName[1] && !retCode)
1451 {
1452 SetIfPointer(lpFileName,'\\');
1453 lpFileName = StrChrW(++lpFileName,'\\');
1454 SetIfPointer(lpFileName,'\0');
1455 retCode = SHCreateDirectoryExW(NULL,pTempTo, NULL);
1456 }
[10162]1457 }
1458 }
[10314]1459 if (retCode)
[10162]1460 {
[10314]1461 retCode = (ERROR_PATH_NOT_FOUND | 0x10000); /* nt40,w98se,w2k,wxp */
1462 goto shfileop_exit;
[10162]1463 }
[10314]1464 ToAttr = GetFileAttributesW(pTempTo);
1465 SetIfPointer(pToTailSlash,'\\');
1466 nFileOp.fFlags &= ~FOF_RENAMEONCOLLISION;
[9833]1467 }
[10314]1468 if (IsAttribDir(ToAttr))
1469 {
1470 /* both are existing dirs, we (FO_)MOVE/COPY the content */
1471 pToFile = SHFileStrCpyCatW(pTempFrom, NULL, wWildcardFile);
1472 nFileOp.fFlags &= ~FOF_MULTIDESTFILES;
1473 retCode = SHFileOperationW(&nFileOp);
1474 if ((FO_COPY == FuncSwitch) || retCode)
1475 continue;
1476 ((DWORD*)pToFile)[0] = '\0';
1477 nFileOp.wFunc = ((level+1) << FO_LevelShift) + FO_DELETE;
1478 retCode = SHFileOperationW(&nFileOp);
1479 continue;
1480 }
1481 }
1482 } /* end-!b_MultiFrom */
[9808]1483
[10314]1484shfileop_IsInvalid:
1485 if (!(b_SameRoot_MOVE) && (nt40 < WOsVers))
[9833]1486 {
[10314]1487 nFileOp.fAnyOperationsAborted |= TRUE;
1488 } /* endif */
1489 /* NT4.0,W98 returns 0x75, W2K,WXP 0x4c7 */
[10162]1490 retCode = retCodeIsInvalid;
1491 break;
[9552]1492
[10162]1493 } /* end-while */
[9552]1494
[10162]1495shfileop_exit:
[9833]1496 if (pTempFrom)
[10314]1497 SHFree(pTempFrom);
[10162]1498 TRACE("%s level=%ld AnyOpsAborted=%s ret=0x%lx, with %s %s%s\n",
[10314]1499 cFO_Name, level, (TRUE & nFileOp.fAnyOperationsAborted) ? "TRUE":"FALSE",
[9959]1500 retCode, debugstr_w(pFrom), pTo ? "-> ":"", debugstr_w(pTo));
[9552]1501
[10314]1502 if (0 == level)
1503 {
1504 nFileOp.fAnyOperationsAborted &= TRUE;
1505 if (!(nFileOp.fFlags & FOF_WANTMAPPINGHANDLE))
1506 {
1507 SHFreeNameMappings((HANDLE)nFileOp.hNameMappings);
1508 nFileOp.hNameMappings = 0;
1509 }
1510 }
[10162]1511 lpFileOp->hNameMappings = nFileOp.hNameMappings;
[9833]1512 lpFileOp->fAnyOperationsAborted = nFileOp.fAnyOperationsAborted;
1513 return retCode;
[4121]1514}
1515
1516/*************************************************************************
[10314]1517 *
1518 * SHNameTranslate HelperFunction for SHFileOperationA
1519 *
1520 * Translates a list of 0 terminated ASCII strings into Unicode. If *wString
1521 * is NULL, only the necessary size of the string is determined and returned,
1522 * otherwise the ASCII strings are copied into it and the buffer is increased
1523 * to point to the location after the final 0 termination char.
1524 */
1525DWORD SHNameTranslate(LPWSTR* wString, LPCWSTR* pWToFrom, BOOL more)
1526{
1527 DWORD size = 0, aSize = 0;
1528 LPCSTR aString = (LPCSTR)*pWToFrom;
1529
1530 if (aString)
1531 {
1532 do
1533 {
1534 size = lstrlenA(aString) + 1;
1535 aSize += size;
1536 aString += size;
1537 } while ((size != 1) && more);
1538 /* The two sizes might be different in the case of multibyte chars */
1539 size = MultiByteToWideChar(CP_ACP, 0, aString, aSize, *wString, 0);
1540 if (*wString) /* only in the second loop */
1541 {
1542 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)*pWToFrom, aSize, *wString, size);
1543 *pWToFrom = *wString;
1544 *wString += size;
1545 }
1546 }
1547 return size;
1548}
1549
1550/*************************************************************************
1551 * SHFileOperationA [SHELL32.@]
1552 *
1553 * Function to copy, move, delete and create one or more files with optional
1554 * user prompts.
1555 *
1556 * PARAMS
1557 * lpFileOp [I/O] pointer to a structure containing all the necessary information
1558 *
1559 * NOTES
1560 * exported by name
1561 */
1562DWORD WINAPI SHFileOperationA(LPSHFILEOPSTRUCTA lpFileOp)
1563{
1564 SHFILEOPSTRUCTW nFileOp = *((LPSHFILEOPSTRUCTW)lpFileOp);
1565 DWORD retCode = 0, size;
1566 LPWSTR ForFree = NULL, /* we change wString in SHNameTranslate and can't use it for freeing */
1567 wString = NULL; /* we change this in SHNameTranslate */
1568
1569 TRACE("\n");
1570 for (;;) /* every loop calculate size, second translate also, if we have storage for this */
1571 {
1572 size = SHNameTranslate(&wString, &nFileOp.pFrom, TRUE); /* internal loop */
1573 if (FO_DELETE != (nFileOp.wFunc & FO_MASK))
1574 size += SHNameTranslate(&wString, &nFileOp.pTo, TRUE); /* internal loop */
1575 if ((nFileOp.fFlags & FOF_SIMPLEPROGRESS))
1576 size += SHNameTranslate(&wString, &nFileOp.lpszProgressTitle, FALSE); /* no loop */
1577
1578 if (ForFree)
1579 {
1580 retCode = SHFileOperationW(&nFileOp);
1581 SHFree(ForFree); /* we can not use wString, it was changed */
1582 lpFileOp->hNameMappings = nFileOp.hNameMappings;
1583 lpFileOp->fAnyOperationsAborted = nFileOp.fAnyOperationsAborted;
1584 }
1585 else
1586 {
1587 wString = ForFree = SHAlloc(size * sizeof(WCHAR));
1588 if (ForFree) continue;
1589 retCode = ERROR_OUTOFMEMORY;
1590 SetLastError(retCode);
1591 }
1592 return retCode;
1593 }
1594}
1595
1596/*************************************************************************
[9805]1597 * SHFileOperation [SHELL32.@]
[4121]1598 *
1599 */
1600DWORD WINAPI SHFileOperationAW(LPVOID lpFileOp)
1601{
[9833]1602 if (SHELL_OsIsUnicode())
1603 return SHFileOperationW(lpFileOp);
1604 return SHFileOperationA(lpFileOp);
[4121]1605}
1606
1607/*************************************************************************
1608 * SheGetDirW [SHELL32.281]
1609 *
1610 */
1611HRESULT WINAPI SheGetDirW(LPWSTR u, LPWSTR v)
[9808]1612{ FIXME("%p %p stub\n",u,v);
[9833]1613 return 0;
[4121]1614}
1615
1616/*************************************************************************
[9808]1617 * SheChangeDirW [SHELL32.274]
[4121]1618 *
1619 */
[9808]1620HRESULT WINAPI SheChangeDirW(LPWSTR u)
1621{ FIXME("(%s),stub\n",debugstr_w(u));
[6709]1622 return 0;
[4121]1623}
1624
[5544]1625/*************************************************************************
[9805]1626 * IsNetDrive [SHELL32.66]
1627 */
[5544]1628BOOL WINAPI IsNetDrive(DWORD drive)
1629{
[9833]1630 char root[4];
1631 strcpy(root, "A:\\");
1632 root[0] += (char)drive;
1633 return (GetDriveTypeA(root) == DRIVE_REMOTE);
[5544]1634}
[9808]1635
Note: See TracBrowser for help on using the repository browser.