source: trunk/src/shell32/shlfileop.c@ 10229

Last change on this file since 10229 was 10162, checked in by sandervl, 22 years ago

DT: Shell folder updates

File size: 40.6 KB
Line 
1/*
2 * SHFileOperation
3 *
4 * Copyright 2000 Juergen Schmied
5 * Copyright 2002 Andriy Palamarchuk
6 * Copyright 2002 Dietrich Teickner (from Odin)
7 * Copyright 2002 Rolf Kalbermatter
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
22 *
23 * !!! shlfileop.c is shared source between wine and odin, do not remove
24 * #ifdef __win32os2__ .. lines
25 */
26
27#include "config.h"
28#include "wine/port.h"
29
30#include <string.h>
31#include <ctype.h>
32
33#include "winreg.h"
34#include "shellapi.h"
35#include "shlobj.h"
36#include "shresdef.h"
37#define NO_SHLWAPI_STREAM
38#include "shlwapi.h"
39#include "shell32_main.h"
40#include "undocshell.h"
41#include "wine/unicode.h"
42#include "wine/debug.h"
43
44WINE_DEFAULT_DEBUG_CHANNEL(shell);
45
46#define W98_FO_FUNCTION /* makes shlfileop W98-like */
47#undef W98_FO_FUNCTION /* makes shlfileop W2K-like */
48#define W98_FO_FUNCTION /* makes shlfileop W98-like */
49
50#define IsAttribFile(x) (!(x & FILE_ATTRIBUTE_DIRECTORY))
51#define IsAttribDir(x) (!(x == -1) && (x & FILE_ATTRIBUTE_DIRECTORY))
52
53#define IsDotDir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
54
55#define FO_MASK 0xFF
56#define FO_LevelShift 8
57#define HIGH_ADR (LPWSTR)0xffffffff
58
59CHAR aWildcardFile[] = {'*','.','*',0};
60WCHAR wWildcardChars[] = {'?','*',0};
61#define wWildcardFile &wWildcardChars[1]
62WCHAR wBackslash[] = {'\\',0};
63
64static BOOL SHNotifyCreateDirectoryA(LPCSTR path, LPSECURITY_ATTRIBUTES sec);
65static BOOL SHNotifyCreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec);
66static BOOL SHNotifyRemoveDirectoryA(LPCSTR path);
67static BOOL SHNotifyRemoveDirectoryW(LPCWSTR path);
68static BOOL SHNotifyDeleteFileA(LPCSTR path);
69static BOOL SHNotifyDeleteFileW(LPCWSTR path);
70static BOOL SHNotifyMoveFileW(LPCWSTR src, LPCWSTR dest);
71static BOOL SHNotifyCopyFileW(LPCWSTR src, LPCWSTR dest, BOOL bRenameIfExists);
72
73typedef struct
74{
75 UINT caption_resource_id, text_resource_id;
76} SHELL_ConfirmIDstruc;
77
78static BOOL SHELL_ConfirmIDs(int nKindOfDialog, SHELL_ConfirmIDstruc *ids)
79{
80 switch (nKindOfDialog) {
81 case ASK_DELETE_FILE:
82 ids->caption_resource_id = IDS_DELETEITEM_CAPTION;
83 ids->text_resource_id = IDS_DELETEITEM_TEXT;
84 return TRUE;
85 case ASK_DELETE_FOLDER:
86 ids->caption_resource_id = IDS_DELETEFOLDER_CAPTION;
87 ids->text_resource_id = IDS_DELETEITEM_TEXT;
88 return TRUE;
89 case ASK_DELETE_MULTIPLE_ITEM:
90 ids->caption_resource_id = IDS_DELETEITEM_CAPTION;
91 ids->text_resource_id = IDS_DELETEMULTIPLE_TEXT;
92 return TRUE;
93 case ASK_OVERWRITE_FILE:
94 ids->caption_resource_id = IDS_OVERWRITEFILE_CAPTION;
95 ids->text_resource_id = IDS_OVERWRITEFILE_TEXT;
96 return TRUE;
97 default:
98 FIXME(" Unhandled nKindOfDialog %d stub\n", nKindOfDialog);
99 }
100 return FALSE;
101}
102
103BOOL SHELL_ConfirmDialog(int nKindOfDialog, LPCSTR szDir)
104{
105 CHAR szCaption[255], szText[255], szBuffer[MAX_PATH + 256];
106 SHELL_ConfirmIDstruc ids;
107
108 if (!SHELL_ConfirmIDs(nKindOfDialog, &ids))
109 return FALSE;
110
111 LoadStringA(shell32_hInstance, ids.caption_resource_id, szCaption, sizeof(szCaption));
112 LoadStringA(shell32_hInstance, ids.text_resource_id, szText, sizeof(szText));
113
114 FormatMessageA(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
115 szText, 0, 0, szBuffer, sizeof(szBuffer), (va_list*)&szDir);
116
117 return (IDOK == MessageBoxA(GetActiveWindow(), szBuffer, szCaption, MB_OKCANCEL | MB_ICONEXCLAMATION));
118}
119
120BOOL SHELL_ConfirmDialogW(int nKindOfDialog, LPCWSTR szDir)
121{
122 WCHAR szCaption[255], szText[255], szBuffer[MAX_PATH + 256];
123 SHELL_ConfirmIDstruc ids;
124
125 if (!SHELL_ConfirmIDs(nKindOfDialog, &ids))
126 return FALSE;
127
128 LoadStringW(shell32_hInstance, ids.caption_resource_id, szCaption, sizeof(szCaption));
129 LoadStringW(shell32_hInstance, ids.text_resource_id, szText, sizeof(szText));
130
131 FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
132 szText, 0, 0, szBuffer, sizeof(szBuffer), (va_list*)&szDir);
133
134 return (IDOK == MessageBoxW(GetActiveWindow(), szBuffer, szCaption, MB_OKCANCEL | MB_ICONEXCLAMATION));
135}
136
137/**************************************************************************
138 * SHELL_DeleteDirectoryA() [internal]
139 *
140 * like rm -r
141 */
142BOOL SHELL_DeleteDirectoryA(LPCSTR pszDir, BOOL bShowUI)
143{
144 BOOL ret = TRUE;
145 HANDLE hFind;
146 WIN32_FIND_DATAA wfd;
147 char szTemp[MAX_PATH];
148
149 /* Make sure the directory exists before eventually prompting the user */
150 PathCombineA(szTemp, pszDir, aWildcardFile);
151 hFind = FindFirstFileA(szTemp, &wfd);
152 if (hFind == INVALID_HANDLE_VALUE)
153 return FALSE;
154
155 if (!bShowUI || SHELL_ConfirmDialog(ASK_DELETE_FOLDER, pszDir))
156 {
157 do
158 {
159 LPSTR lp = wfd.cAlternateFileName;
160 if (!lp[0])
161 lp = wfd.cFileName;
162 if (IsDotDir(lp))
163 continue;
164 PathCombineA(szTemp, pszDir, lp);
165 if (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
166 ret = SHELL_DeleteDirectoryA(szTemp, FALSE);
167 else
168 ret = SHNotifyDeleteFileA(szTemp);
169 } while (ret && FindNextFileA(hFind, &wfd));
170 }
171 FindClose(hFind);
172 if (ret)
173 ret = SHNotifyRemoveDirectoryA(pszDir);
174 return ret;
175}
176
177BOOL SHELL_DeleteDirectoryW(LPCWSTR pszDir, BOOL bShowUI)
178{
179 BOOL ret = TRUE;
180 HANDLE hFind;
181 WIN32_FIND_DATAW wfd;
182 WCHAR szTemp[MAX_PATH];
183
184 /* Make sure the directory exists before eventually prompting the user */
185 PathCombineW(szTemp, pszDir, wWildcardFile);
186 hFind = FindFirstFileW(szTemp, &wfd);
187 if (hFind == INVALID_HANDLE_VALUE)
188 return FALSE;
189
190 if (!bShowUI || SHELL_ConfirmDialogW(ASK_DELETE_FOLDER, pszDir))
191 {
192 do
193 {
194 LPWSTR lp = wfd.cAlternateFileName;
195 if (!lp[0])
196 lp = wfd.cFileName;
197 if (IsDotDir(lp))
198 continue;
199 PathCombineW(szTemp, pszDir, lp);
200 if (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
201 ret = SHELL_DeleteDirectoryW(szTemp, FALSE);
202 else
203 ret = SHNotifyDeleteFileW(szTemp);
204 } while (ret && FindNextFileW(hFind, &wfd));
205 }
206 FindClose(hFind);
207 if (ret)
208 ret = SHNotifyRemoveDirectoryW(pszDir);
209 return ret;
210}
211
212/**************************************************************************
213 * SHELL_DeleteFileA() [internal]
214 */
215BOOL SHELL_DeleteFileA(LPCSTR pszFile, BOOL bShowUI)
216{
217 if (bShowUI && !SHELL_ConfirmDialog(ASK_DELETE_FILE, pszFile))
218 return FALSE;
219
220 return SHNotifyDeleteFileA(pszFile);
221}
222
223BOOL SHELL_DeleteFileW(LPCWSTR pszFile, BOOL bShowUI)
224{
225 if (bShowUI && !SHELL_ConfirmDialogW(ASK_DELETE_FILE, pszFile))
226 return FALSE;
227
228 return SHNotifyDeleteFileW(pszFile);
229}
230
231/**************************************************************************
232 * Win32CreateDirectory [SHELL32.93]
233 *
234 * Creates a directory. Also triggers a change notify if one exists.
235 *
236 * PARAMS
237 * path [I] path to directory to create
238 *
239 * RETURNS
240 * TRUE if successful, FALSE otherwise
241 *
242 * NOTES:
243 * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be ANSI.
244 * This is Unicode on NT/2000
245 */
246static BOOL SHNotifyCreateDirectoryA(LPCSTR path, LPSECURITY_ATTRIBUTES sec)
247{
248 BOOL ret;
249 TRACE("(%s, %p)\n", debugstr_a(path), sec);
250
251 ret = CreateDirectoryA(path, sec);
252 if (ret)
253 {
254 SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHA, path, NULL);
255 }
256 return ret;
257}
258
259static BOOL SHNotifyCreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec)
260{
261 BOOL ret;
262 TRACE("(%s, %p)\n", debugstr_w(path), sec);
263
264 ret = CreateDirectoryW(path, sec);
265 if (ret)
266 {
267 SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW, path, NULL);
268 }
269 return ret;
270}
271
272BOOL WINAPI Win32CreateDirectoryAW(LPCVOID path, LPSECURITY_ATTRIBUTES sec)
273{
274 if (SHELL_OsIsUnicode())
275 return SHNotifyCreateDirectoryW(path, sec);
276 return SHNotifyCreateDirectoryA(path, sec);
277}
278
279/************************************************************************
280 * Win32RemoveDirectory [SHELL32.94]
281 *
282 * Deletes a directory. Also triggers a change notify if one exists.
283 *
284 * PARAMS
285 * path [I] path to directory to delete
286 *
287 * RETURNS
288 * TRUE if successful, FALSE otherwise
289 *
290 * NOTES:
291 * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be ANSI.
292 * This is Unicode on NT/2000
293 */
294static BOOL SHNotifyRemoveDirectoryA(LPCSTR path)
295{
296 BOOL ret;
297 TRACE("(%s)\n", debugstr_a(path));
298
299 ret = RemoveDirectoryA(path);
300 if (!ret)
301 {
302 /* Directory may be write protected */
303 DWORD dwAttr = GetFileAttributesA(path);
304 if (dwAttr != -1 && dwAttr & FILE_ATTRIBUTE_READONLY)
305 if (SetFileAttributesA(path, dwAttr & ~FILE_ATTRIBUTE_READONLY))
306 ret = RemoveDirectoryA(path);
307 }
308 if (ret)
309 SHChangeNotify(SHCNE_RMDIR, SHCNF_PATHA, path, NULL);
310 return ret;
311}
312
313static BOOL SHNotifyRemoveDirectoryW(LPCWSTR path)
314{
315 BOOL ret;
316 TRACE("(%s)\n", debugstr_w(path));
317
318 ret = RemoveDirectoryW(path);
319 if (!ret)
320 {
321 /* Directory may be write protected */
322 DWORD dwAttr = GetFileAttributesW(path);
323 if (dwAttr != -1 && dwAttr & FILE_ATTRIBUTE_READONLY)
324 if (SetFileAttributesW(path, dwAttr & ~FILE_ATTRIBUTE_READONLY))
325 ret = RemoveDirectoryW(path);
326 }
327 if (ret)
328 SHChangeNotify(SHCNE_RMDIR, SHCNF_PATHW, path, NULL);
329 return ret;
330}
331
332BOOL WINAPI Win32RemoveDirectoryAW(LPCVOID path)
333{
334 if (SHELL_OsIsUnicode())
335 return SHNotifyRemoveDirectoryW(path);
336 return SHNotifyRemoveDirectoryA(path);
337}
338
339/************************************************************************
340 * Win32DeleteFile [SHELL32.164]
341 *
342 * Deletes a file. Also triggers a change notify if one exists.
343 *
344 * PARAMS
345 * path [I] path to file to delete
346 *
347 * RETURNS
348 * TRUE if successful, FALSE otherwise
349 *
350 * NOTES:
351 * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be ANSI.
352 * This is Unicode on NT/2000
353 */
354static BOOL SHNotifyDeleteFileA(LPCSTR path)
355{
356 BOOL ret;
357
358 TRACE("(%s)\n", debugstr_a(path));
359
360 ret = DeleteFileA(path);
361 if (!ret)
362 {
363 /* File may be write protected or a system file */
364 DWORD dwAttr = GetFileAttributesA(path);
365 if ((dwAttr != -1) && (dwAttr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
366 if (SetFileAttributesA(path, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
367 ret = DeleteFileA(path);
368 }
369 if (ret)
370 SHChangeNotify(SHCNE_DELETE, SHCNF_PATHA, path, NULL);
371 return ret;
372}
373
374static BOOL SHNotifyDeleteFileW(LPCWSTR path)
375{
376 BOOL ret;
377
378 TRACE("(%s)\n", debugstr_w(path));
379
380 ret = DeleteFileW(path);
381 if (!ret)
382 {
383 /* File may be write protected or a system file */
384 DWORD dwAttr = GetFileAttributesW(path);
385 if ((dwAttr != -1) && (dwAttr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
386 if (SetFileAttributesW(path, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
387 ret = DeleteFileW(path);
388 }
389 if (ret)
390 SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, path, NULL);
391 return ret;
392}
393
394DWORD WINAPI Win32DeleteFileAW(LPCVOID path)
395{
396 if (SHELL_OsIsUnicode())
397 return SHNotifyDeleteFileW(path);
398 return SHNotifyDeleteFileA(path);
399}
400
401/************************************************************************
402 * SHNotifyMoveFile [internal]
403 *
404 * Moves a file. Also triggers a change notify if one exists.
405 *
406 * PARAMS
407 * src [I] path to source file to move
408 * dest [I] path to target file to move to
409 *
410 * RETURNS
411 * TRUE if successful, FALSE otherwise
412 */
413static BOOL SHNotifyMoveFileW(LPCWSTR src, LPCWSTR dest)
414{
415 BOOL ret;
416
417 TRACE("(%s %s)\n", debugstr_w(src), debugstr_w(dest));
418
419 ret = MoveFileW(src, dest);
420 if (!ret)
421 {
422 /* Source file may be write protected or a system file */
423 DWORD dwAttr = GetFileAttributesW(src);
424 if ((dwAttr != -1) && (dwAttr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
425 if (SetFileAttributesW(src, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
426 ret = MoveFileW(src, dest);
427 }
428 if (ret)
429 SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_PATHW, src, dest);
430 return ret;
431}
432
433/************************************************************************
434 * SHNotifyCopyFile [internal]
435 *
436 * Copies a file. Also triggers a change notify if one exists.
437 *
438 * PARAMS
439 * src [I] path to source file to move
440 * dest [I] path to target file to move to
441 * bRename [I] if TRUE, the target file will be renamed if a
442 * file with this name already exists
443 *
444 * RETURNS
445 * TRUE if successful, FALSE otherwise
446 */
447static BOOL SHNotifyCopyFileW(LPCWSTR src, LPCWSTR dest, BOOL bRename)
448{
449 BOOL ret;
450
451 TRACE("(%s %s %s)\n", debugstr_w(src), debugstr_w(dest), bRename ? "renameIfExists" : "");
452
453 ret = CopyFileW(src, dest, TRUE);
454 if (!ret && bRename)
455 {
456 /* Destination file probably exists */
457 DWORD dwAttr = GetFileAttributesW(dest);
458 if (dwAttr != -1)
459 {
460 FIXME("Rename on copy to existing file not implemented!");
461 }
462 }
463 if (ret)
464 SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, dest, NULL);
465 return ret;
466}
467
468/*************************************************************************
469 * SHCreateDirectory [SHELL32.165]
470 *
471 * Create a directory at the specified location
472 *
473 * PARAMS
474 * hWnd [I]
475 * path [I] path of directory to create
476 *
477 * RETURNS
478 * ERRROR_SUCCESS or one of the following values:
479 * ERROR_BAD_PATHNAME if the path is relative
480 * ERROR_FILE_EXISTS when a file with that name exists
481 * ERROR_ALREADY_EXISTS when the directory already exists
482 * ERROR_FILENAME_EXCED_RANGE if the filename was to long to process
483 *
484 * NOTES
485 * exported by ordinal
486 * Win9x exports ANSI
487 * WinNT/2000 exports Unicode
488 */
489DWORD WINAPI SHCreateDirectory(HWND hWnd, LPCVOID path)
490{
491 if (SHELL_OsIsUnicode())
492 return SHCreateDirectoryExW(hWnd, path, NULL);
493 return SHCreateDirectoryExA(hWnd, path, NULL);
494}
495
496/*************************************************************************
497 * SHCreateDirectoryExA [SHELL32.@]
498 *
499 * Create a directory at the specified location
500 *
501 * PARAMS
502 * hWnd [I]
503 * path [I] path of directory to create
504 * sec [I] security attributes to use or NULL
505 *
506 * RETURNS
507 * ERRROR_SUCCESS or one of the following values:
508 * ERROR_BAD_PATHNAME if the path is relative
509 * ERROR_FILE_EXISTS when a file with that name exists
510 * ERROR_ALREADY_EXISTS when the directory already exists
511 * ERROR_FILENAME_EXCED_RANGE if the filename was to long to process
512 */
513DWORD WINAPI SHCreateDirectoryExA(HWND hWnd, LPCSTR path, LPSECURITY_ATTRIBUTES sec)
514{
515 WCHAR wPath[MAX_PATH];
516 TRACE("(%p, %s, %p)\n",hWnd, debugstr_a(path), sec);
517
518 MultiByteToWideChar(CP_ACP, 0, path, -1, wPath, MAX_PATH);
519 return SHCreateDirectoryExW(hWnd, wPath, sec);
520}
521
522/*************************************************************************
523 * SHTryFind [intern]
524 * Try for existing names
525 */
526DWORD SHTryFindW(LPCWSTR pName)
527{
528 WCHAR szName [MAX_PATH + 4];
529 DWORD retCode = ERROR_FILE_NOT_FOUND;
530 WIN32_FIND_DATAW wfd;
531 HANDLE hFind;
532 LPCWSTR pFileName = StrRChrW(pName,NULL,'\\');
533 if ((!pFileName) || \
534 /* we must isolate the part befor '\' */
535 (lstrcpynW(&szName[0],pName,(pFileName-pName)),
536 NULL != StrPBrkW(&szName[0], wWildcardChars)))
537 {
538 return ERROR_INVALID_NAME;
539 }
540 hFind = FindFirstFileW(pName, &wfd);
541 if (INVALID_HANDLE_VALUE != hFind)
542 {
543 FindClose(hFind);
544 retCode = ERROR_ALREADY_EXISTS;
545 }
546 return retCode;
547}
548/*************************************************************************
549 * SHCreateDirectoryExW [SHELL32.@]
550 */
551DWORD WINAPI SHCreateDirectoryExW(HWND hWnd, LPCWSTR path, LPSECURITY_ATTRIBUTES sec)
552{
553 DWORD ret = SHTryFindW(path);
554 TRACE("(%p, %s, %p)\n",hWnd, debugstr_w(path), sec);
555
556 if (ERROR_FILE_NOT_FOUND == ret)
557 {
558 ret = ERROR_SUCCESS;
559 if (PathIsRelativeW(path))
560 {
561 ret = ERROR_BAD_PATHNAME;
562 }
563 }
564 SetLastError(ret);
565 if (ERROR_SUCCESS == ret)
566 {
567 if (!SHNotifyCreateDirectoryW(path, sec))
568 {
569 ret = GetLastError();
570 if (ret != ERROR_FILE_EXISTS &&
571 ret != ERROR_ALREADY_EXISTS &&
572 ret != ERROR_FILENAME_EXCED_RANGE)
573 {
574 /* handling network file names?
575 lstrcpynW(pathName, path, MAX_PATH);
576 lpStr = PathAddBackslashW(pathName);*/
577 FIXME("Semi-stub, non zero hWnd should be used somehow?");
578 }
579 }
580 }
581 return ret;
582}
583
584/*************************************************************************
585 *
586 * SHFileStrICmp HelperFunction for SHFileOperationW
587 *
588 */
589BOOL SHFileStrICmpW(LPWSTR p1, LPWSTR p2, LPWSTR p1End, LPWSTR p2End)
590{
591 WCHAR C1 = '\0';
592 WCHAR C2 = '\0';
593 int i_Temp = -1;
594 int i_len1 = lstrlenW(p1);
595 int i_len2 = lstrlenW(p2);
596
597 if (p1End && (&p1[i_len1] >= p1End) && ('\\' == p1End[0]))
598 {
599 C1 = p1End[0];
600 p1End[0] = '\0';
601 i_len1 = lstrlenW(p1);
602 }
603 if (p2End)
604 {
605 if ((&p2[i_len2] >= p2End) && ('\\' == p2End[0]))
606 {
607 C2 = p2End[0];
608 if (C2)
609 p2End[0] = '\0';
610 }
611 }
612 else
613 {
614 if ((i_len1 <= i_len2) && ('\\' == p2[i_len1]))
615 {
616 C2 = p2[i_len1];
617 if (C2)
618 p2[i_len1] = '\0';
619 }
620 }
621 i_len2 = lstrlenW(p2);
622 if (i_len1 == i_len2)
623 i_Temp = lstrcmpiW(p1,p2);
624 if (C1)
625 p1[i_len1] = C1;
626 if (C2)
627 p2[i_len2] = C2;
628 return !(i_Temp);
629}
630
631/*************************************************************************
632 *
633 * SHFileStrCpyCat HelperFunction for SHFileOperationW
634 *
635 */
636LPWSTR SHFileStrCpyCatW(LPWSTR pTo, LPCWSTR pFrom, LPCWSTR pCatStr)
637{
638 LPWSTR pToFile = NULL;
639 int i_len;
640 if (pTo)
641 {
642 if (pFrom)
643 lstrcpyW(pTo, pFrom);
644 if (pCatStr)
645 {
646 i_len = lstrlenW(pTo);
647 if ((i_len) && (pTo[--i_len] != '\\'))
648 i_len++;
649 pTo[i_len] = '\\';
650 if (pCatStr[0] == '\\')
651 pCatStr++; \
652 lstrcpyW(&pTo[i_len+1], pCatStr);
653 }
654 pToFile = StrRChrW(pTo,NULL,'\\');
655/* !! termination of the new string-group */
656 pTo[(lstrlenW(pTo)) + 1] = '\0';
657 }
658 return pToFile;
659}
660
661/**************************************************************************
662 * SHELL_FileNamesMatch()
663 *
664 * Accepts two \0 delimited lists of the file names. Checks whether number of
665 * files in both lists is the same, and checks also if source-name exists.
666 */
667BOOL SHELL_FileNamesMatch(LPCWSTR pszFiles1, LPCWSTR pszFiles2, BOOL bOnlySrc)
668{
669 LPWSTR pszTemp;
670 while ((pszFiles1[0] != '\0') &&
671 (bOnlySrc || (pszFiles2[0] != '\0')))
672 {
673 pszTemp = StrChrW(pszFiles1,'\\');
674 /* root (without mask/name) is also not allowed as source, tested in W98 */
675 if (!pszTemp || !pszTemp[1])
676 return FALSE;
677 pszTemp = StrPBrkW(pszFiles1, wWildcardChars);
678 if (pszTemp)
679 {
680 WCHAR szMask [MAX_PATH + 4];
681 strncpyW(&szMask[0],pszFiles1,(pszTemp - pszFiles1));
682 pszTemp = StrRChrW(&szMask[0],NULL,'\\');
683 if (!pszTemp)
684 return FALSE;
685 pszTemp[0] = '\0';
686 /* we will check the root of the mask as valid dir */
687 if (!IsAttribDir(GetFileAttributesW(&szMask[0])))
688 return FALSE;
689 }
690 else
691 {
692 if (-1 == GetFileAttributesW(pszFiles1))
693 return FALSE;
694 }
695 pszFiles1 += lstrlenW(pszFiles1) + 1;
696 if (!bOnlySrc)
697 pszFiles2 += lstrlenW(pszFiles2) + 1;
698 }
699 return ((pszFiles1[0] == '\0') && (bOnlySrc || (pszFiles2[0] == '\0')));
700}
701
702/*************************************************************************
703 *
704 * SHName(s)Translate HelperFunction for SHFileOperationW
705 *
706 * Translates a list of 0 terminated ASCI strings into Unicode. If *wString
707 * is NULL, only the necessary size of the string is determined and returned,
708 * otherwise the ASCII strings are copied into it and the buffer is increased
709 * to point to the location after the final 0 termination char.
710 */
711DWORD SHNameTranslate(LPWSTR* wString, LPCWSTR* pWToFrom, BOOL more)
712{
713 DWORD size = 0, aSize = 0;
714 LPCSTR aString = (LPCSTR)*pWToFrom;
715
716 if (aString)
717 {
718 do
719 {
720 size = lstrlenA(aString) + 1;
721 aSize += size;
722 aString += size;
723 } while ((size != 1) && more);
724 /* The two sizes might be different in the case of multibyte chars */
725 size = MultiByteToWideChar(CP_ACP, 0, aString, aSize, *wString, 0);
726 if (*wString) /* only in the second loop */
727 {
728 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)*pWToFrom, aSize, *wString, size);
729 *pWToFrom = *wString;
730 *wString += size;
731 }
732 }
733 return size;
734}
735/*************************************************************************
736 * SHFileOperationA [SHELL32.@]
737 *
738 * Function to copy, move, delete and create one or more files with optional
739 * user prompts.
740 *
741 * PARAMS
742 * lpFileOp [I/O] pointer to a structure containing all the necessary information
743 *
744 * NOTES
745 * exported by name
746 */
747DWORD WINAPI SHFileOperationA(LPSHFILEOPSTRUCTA lpFileOp)
748{
749 SHFILEOPSTRUCTW nFileOp = *((LPSHFILEOPSTRUCTW)lpFileOp);
750 DWORD retCode = 0, size;
751 LPWSTR ForFree = NULL, /* we change wString in SHNameTranslate and can't use it for freeing */
752 wString = NULL; /* we change this in SHNameTranslate */
753
754#ifdef __WIN32OS2__
755 TRACE("SHFileOperationA");
756#else
757 TRACE("\n");
758#endif
759 if (FO_DELETE == (nFileOp.wFunc & FO_MASK))
760 nFileOp.pTo = NULL; /* we need a NULL or a valid pointer for translation */
761 if (!(nFileOp.fFlags & FOF_SIMPLEPROGRESS))
762 nFileOp.lpszProgressTitle = NULL; /* we need a NULL or a valid pointer for translation */
763 while (1) /* every loop calculate size, second translate also, if we have storage for this */
764 {
765 size = SHNameTranslate(&wString, &nFileOp.lpszProgressTitle, FALSE); /* no loop */
766 size += SHNameTranslate(&wString, &nFileOp.pFrom, TRUE); /* internal loop */
767 size += SHNameTranslate(&wString, &nFileOp.pTo, TRUE); /* internal loop */
768
769 if (ForFree)
770 {
771 retCode = SHFileOperationW(&nFileOp);
772 HeapFree(GetProcessHeap(), 0, ForFree); /* we can not use wString, it was changed */
773 lpFileOp->hNameMappings = nFileOp.hNameMappings;
774 lpFileOp->fAnyOperationsAborted = nFileOp.fAnyOperationsAborted;
775 return retCode;
776 }
777 else
778 {
779 wString = ForFree = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
780 if (ForFree) continue;
781 retCode = ERROR_OUTOFMEMORY;
782 SetLastError(retCode);
783 return retCode;
784 }
785 }
786}
787/*************************************************************************
788 * SHFileOperationCheck
789 */
790DWORD SHFileOperationCheck(LPSHFILEOPSTRUCTW lpFileOp, LPCSTR* cFO_Name)
791{
792 FILEOP_FLAGS OFl = (FILEOP_FLAGS)lpFileOp->fFlags;
793 LPCSTR cFO_Temp [] = {"FO_????","FO_MOVE","FO_COPY","FO_DELETE","FO_RENAME"};
794 long retCode = NO_ERROR;
795 long FuncSwitch = (lpFileOp->wFunc & FO_MASK);
796
797 /* default no error */
798
799 if ((FuncSwitch < FO_MOVE) || (FuncSwitch > FO_RENAME))
800 {
801 FuncSwitch = 0;
802 retCode = ERROR_INVALID_PARAMETER;
803 }
804 cFO_Name[0] = cFO_Temp [FuncSwitch];
805
806 if (!(~FO_MASK & lpFileOp->wFunc)) /* ? Level == 0 */
807 {
808 lpFileOp->fAnyOperationsAborted = FALSE;
809 TRACE("%s: flags (0x%04x) : %s%s%s%s%s%s%s%s%s%s%s%s%s \n",cFO_Name[0], OFl,
810 OFl & FOF_MULTIDESTFILES ? "FOF_MULTIDESTFILES " : "",
811 OFl & FOF_CONFIRMMOUSE ? "FOF_CONFIRMMOUSE " : "",
812 OFl & FOF_SILENT ? "FOF_SILENT " : "",
813 OFl & FOF_RENAMEONCOLLISION ? "FOF_RENAMEONCOLLISION " : "",
814 OFl & FOF_NOCONFIRMATION ? "FOF_NOCONFIRMATION " : "",
815 OFl & FOF_WANTMAPPINGHANDLE ? "FOF_WANTMAPPINGHANDLE " : "",
816 OFl & FOF_ALLOWUNDO ? "FOF_ALLOWUNDO " : "",
817 OFl & FOF_FILESONLY ? "FOF_FILESONLY " : "",
818 OFl & FOF_SIMPLEPROGRESS ? "FOF_SIMPLEPROGRESS " : "",
819 OFl & FOF_NOCONFIRMMKDIR ? "FOF_NOCONFIRMMKDIR " : "",
820 OFl & FOF_NOERRORUI ? "FOF_NOERRORUI " : "",
821 OFl & FOF_NOCOPYSECURITYATTRIBS ? "FOF_NOCOPYSECURITYATTRIBS" : "",
822 OFl & 0xf000 ? "MORE-UNKNOWN-Flags" : "");
823 OFl &= (~(FOF_MULTIDESTFILES | FOF_NOCONFIRMATION | FOF_FILESONLY)); /* implemented */
824 OFl ^= (FOF_SILENT | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_NOCOPYSECURITYATTRIBS); /* ignored, if one */
825 OFl &= (~FOF_SIMPLEPROGRESS); /* ignored, only with FOF_SILENT */
826 if (OFl)
827 {
828 if (OFl & (~(FOF_CONFIRMMOUSE | FOF_SILENT | FOF_RENAMEONCOLLISION |
829 FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_NOCOPYSECURITYATTRIBS)))
830 {
831 TRACE("%s lpFileOp->fFlags=0x%x not implemented, Aborted=TRUE, stub\n", cFO_Name[0], OFl);
832 retCode = ERROR_INVALID_PARAMETER;
833 }
834 else
835 {
836 TRACE("%s lpFileOp->fFlags=0x%x not fully implemented, stub\n", cFO_Name[0], OFl);
837 } /* endif */
838 } /* endif */
839 }
840 return retCode;
841}
842/*************************************************************************
843 *
844 * SHFileOperationMove HelperFunction for SHFileOperationW
845 *
846 * Contains the common part of FO_MOVE/FO_RENAME.
847 * It is not with recursive call solvable.
848 * We have both tryed FO_RENAME contains not FO_MOVE and
849 * also FO_RENAME does not contains FO_MOVE, only common Parts.
850 */
851DWORD SHFileOperationMove(LPSHFILEOPSTRUCTW lpFileOp)
852{
853 DWORD retCode;
854 WCHAR szToName [MAX_PATH + 4];
855 LPWSTR pToFile;
856 if (NULL != StrPBrkW(lpFileOp->pTo, wWildcardChars))
857 {
858#ifdef W98_FO_FUNCTION /* W98 */
859 return ERROR_FILE_NOT_FOUND;
860#else
861 return ERROR_INVALID_NAME;
862#endif
863 }
864 retCode = SHTryFindW(lpFileOp->pTo);
865 /* FOF_NOCONFIRMATION, FOF_RENAMEONCOLLISION ? */
866 if (retCode != ERROR_FILE_NOT_FOUND)
867 {
868 return retCode;
869 }
870 lstrcpyW(&szToName[0],lpFileOp->pTo);
871 pToFile = StrRChrW(&szToName[0],NULL,'\\');
872 if (!pToFile[1])
873 {
874 pToFile[0] = '\0';
875 }
876 /* we use SHNotifyMoveFile() instead MoveFileW */
877 if (!SHNotifyMoveFileW(lpFileOp->pFrom, &szToName[0]))
878 {
879 /* we need still the value for the returncode */
880 return GetLastError();
881 }
882 return ERROR_SUCCESS;
883}
884/*************************************************************************
885 * SHFileOperationW [SHELL32.@]
886 *
887 * See SHFileOperationA
888 */
889DWORD WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
890{
891 SHFILEOPSTRUCTW nFileOp = lpFileOp ? *(lpFileOp):nFileOp;
892
893 LPCWSTR pNextFrom = nFileOp.pFrom;
894 LPCWSTR pNextTo = nFileOp.pTo;
895 LPCWSTR pFrom = pNextFrom;
896 LPCWSTR pTo = NULL;
897#ifdef __WIN32OS2__
898#ifdef DEBUG
899#define _SHFO_DEBUGSTR_
900#ifdef _SHFO_DEBUGSTR_
901 CHAR Debug_pFrom [MAX_PATH + 4];
902 CHAR Debug_pTo [MAX_PATH + 4];
903#endif
904#endif
905#endif
906 HANDLE hFind = INVALID_HANDLE_VALUE;
907 WIN32_FIND_DATAW wfd;
908 LPWSTR pTempFrom = NULL;
909 LPWSTR pTempTo = NULL;
910 LPWSTR pFromFile;
911 LPWSTR pToFile = NULL;
912 LPWSTR pToTailSlash; /* points also to ToMask, we know not what is this */
913 LPWSTR lpFileName; /* temporary for pFromFile,pToFile,pToTailSlash */
914 DWORD ToAttr;
915 DWORD ToPathAttr;
916 DWORD FromAttr;
917 DWORD FromPathAttr;
918
919 BOOL b_Multi = (nFileOp.fFlags & FOF_MULTIDESTFILES);
920
921 BOOL b_MultiTo = (FO_DELETE != (lpFileOp->wFunc & FO_MASK));
922 BOOL b_MultiPaired = FALSE;
923 BOOL b_MultiFrom = FALSE;
924 BOOL not_overwrite;
925 BOOL ask_overwrite;
926 BOOL b_SameRoot;
927 BOOL b_SameRoot_MOVE;
928 BOOL b_ToMask = FALSE;
929 BOOL b_FromMask;
930
931 long FuncSwitch = (nFileOp.wFunc & FO_MASK);
932 long level= nFileOp.wFunc>>FO_LevelShift;
933
934 LPCSTR cFO_Name;
935 long retCode = SHFileOperationCheck(&nFileOp,&cFO_Name);
936
937 if (retCode)
938 {
939#ifdef W98_FO_FUNCTION /* W98 returns NO_ERROR */
940 if ('?' == cFO_Name[3])
941 retCode = NO_ERROR;
942#endif
943 goto shfileop_exit; /* no valid FunctionCode */
944 }
945
946 /* establish when pTo is interpreted as the name of the destination file
947 * or the directory where the Fromfile should be copied to.
948 * This depends on:
949 * (1) pTo points to the name of an existing directory;
950 * (2) the flag FOF_MULTIDESTFILES is present;
951 * (3) whether pFrom point to multiple filenames.
952 *
953 * Some experiments:
954 *
955 * destisdir 1 1 1 1 0 0 0 0
956 * FOF_MULTIDESTFILES 1 1 0 0 1 1 0 0
957 * multiple from filenames 1 0 1 0 1 0 1 0
958 * ---------------
959 * copy files to dir 1 0 1 1 0 0 1 0
960 * create dir 0 0 0 0 0 0 1 0
961 */
962/*
963 * FOF_MULTIDESTFILES, FOF_NOCONFIRMATION, FOF_FILESONLY are implemented
964 * FOF_CONFIRMMOUSE, FOF_SILENT, FOF_NOCONFIRMMKDIR,
965 * FOF_SIMPLEPROGRESS, FOF_NOCOPYSECURITYATTRIBS are not implemented and ignored
966 * FOF_RENAMEONCOLLISION are implemented partially and breaks if file exist
967 * FOF_ALLOWUNDO, FOF_WANTMAPPINGHANDLE are not implemented and breaks
968 * if any other flag set, an error occurs
969 */
970 TRACE(" %s level=%ld nFileOp.fFlags=0x%x\n", cFO_Name, level, nFileOp.fFlags);
971
972 if ((pNextFrom) && (!(b_MultiTo) || (pNextTo)))
973 {
974 nFileOp.pFrom = pTempFrom = HeapAlloc(GetProcessHeap(), 0, ((1 + 2 * (b_MultiTo)) * MAX_PATH + 6) * sizeof(WCHAR));
975 if (!pTempFrom)
976 {
977 retCode = ERROR_OUTOFMEMORY;
978 SetLastError(retCode);
979 goto shfileop_exit;
980 }
981 if (b_MultiTo)
982 pTempTo = &pTempFrom[MAX_PATH + 4];
983 nFileOp.pTo = pTempTo;
984 ask_overwrite = (!(nFileOp.fFlags & FOF_NOCONFIRMATION) && !(nFileOp.fFlags & FOF_RENAMEONCOLLISION));
985 not_overwrite = (!(nFileOp.fFlags & FOF_NOCONFIRMATION) || (nFileOp.fFlags & FOF_RENAMEONCOLLISION));
986 }
987 else
988 {
989 retCode = 0x402; /* 1026 */
990 goto shfileop_exit;
991 }
992 /* need break at error before change sourcepointer */
993 while(!retCode && (pNextFrom[0]))
994 {
995 nFileOp.wFunc = ((level + 1) << FO_LevelShift) + FuncSwitch;
996 nFileOp.fFlags = lpFileOp->fFlags;
997
998 if (b_MultiTo)
999 {
1000 pTo = pNextTo;
1001 pNextTo = &pNextTo[lstrlenW(pTo)+1];
1002 b_MultiTo = (b_Multi && pNextTo[0]);
1003 }
1004
1005 pFrom = pNextFrom;
1006#ifdef _SHFO_DEBUGSTR_
1007 /* for seeing in debugger outside from TRACE */
1008 strcpy(&Debug_pFrom[0],debugstr_w(pFrom));
1009 strcpy(&Debug_pTo[0],debugstr_w(pTo));
1010 TRACE("%s level=%ld with %s %s%s\n",
1011 cFO_Name, level, &Debug_pFrom[0], pTo ? "-> ":"", &Debug_pTo[0]);
1012#endif
1013 pNextFrom = &pNextFrom[lstrlenW(pNextFrom)+1];
1014 if (!b_MultiFrom && !b_MultiTo)
1015 b_MultiFrom = (pNextFrom[0]);
1016
1017 pFromFile = SHFileStrCpyCatW(pTempFrom, pFrom, NULL);
1018 b_FromMask = (pFromFile && (NULL != StrPBrkW(&pFromFile[1], wWildcardChars)));
1019
1020 if (pTo)
1021 {
1022 pToFile = SHFileStrCpyCatW(pTempTo, pTo, NULL);
1023 b_ToMask = (NULL != StrPBrkW(pTempTo, wWildcardChars));
1024 }
1025
1026 if (FO_RENAME == FuncSwitch)
1027 {
1028 if (b_MultiTo || b_MultiFrom || (b_FromMask && !b_ToMask))
1029 {
1030#define retCodeIsInvalid 0x075 /* W98 */
1031#ifndef W98_FO_FUNCTION /* is W2K */
1032#undef retCodeIsInvalid
1033#define retCodeIsInvalid 0x4c7 /* W2K */
1034 retCode = 0x1f; /* value 31 in W2K, W98 no work, only RC=0 */
1035#endif
1036 goto shfileop_exit;
1037 }
1038 }
1039
1040 if (!b_MultiPaired)
1041 {
1042 b_MultiPaired =
1043 SHELL_FileNamesMatch(lpFileOp->pFrom,
1044 lpFileOp->pTo, ((FO_DELETE == FuncSwitch) || !b_Multi || b_MultiFrom));
1045 } /* endif */
1046 if (!(b_MultiPaired) || !(pFromFile) || !(pFromFile[1]) || ((pTo) && !(pToFile)))
1047 {
1048 retCode = 0x402; /* 1026 */
1049 goto shfileop_exit;
1050 }
1051
1052 if (FO_DELETE == FuncSwitch)
1053 {
1054 hFind = FindFirstFileW(pFrom, &wfd);
1055 if (INVALID_HANDLE_VALUE == hFind)
1056 /* no problem for FO_DELETE */
1057 continue;
1058 do
1059 {
1060 lpFileName = wfd.cAlternateFileName;
1061 if (!lpFileName[0])
1062 lpFileName = wfd.cFileName;
1063 if (IsDotDir(lpFileName) ||
1064 ((b_FromMask) && IsAttribDir(wfd.dwFileAttributes) && (nFileOp.fFlags & FOF_FILESONLY)))
1065 continue;
1066 SHFileStrCpyCatW(&pFromFile[1], lpFileName, NULL);
1067 /* TODO: Check the SHELL_DeleteFileOrDirectoryW() function in shell32.dll */
1068 if (IsAttribFile(wfd.dwFileAttributes))
1069 {
1070 if (!SHNotifyDeleteFileW(pTempFrom))
1071 retCode = 0x78; /* value unknown */
1072 }
1073 else
1074 {
1075 if (!SHELL_DeleteDirectoryW(pTempFrom, (!(nFileOp.fFlags & FOF_NOCONFIRMATION))))
1076 retCode = 0x79; /* value unknown */
1077 }
1078 } while (!retCode && FindNextFileW(hFind, &wfd));
1079 FindClose(hFind);
1080 continue;
1081 } /* FO_DELETE ends, pTo must be always valid from here */
1082
1083 pToTailSlash = NULL;
1084 if ((pToFile) && !(pToFile[1]))
1085 {
1086 pToFile[0] = '\0';
1087 lpFileName = StrRChrW(pTempTo,NULL,'\\');
1088 if (lpFileName)
1089 {
1090 pToTailSlash = pToFile;
1091 pToFile = lpFileName;
1092 }
1093 }
1094 ToPathAttr = ToAttr = GetFileAttributesW(pTempTo);
1095 FromAttr = GetFileAttributesW(pTempFrom);
1096 b_SameRoot = (toupperW(pTempFrom[0]) == toupperW(pTempTo[0]));
1097 b_SameRoot_MOVE = (b_SameRoot && (FO_MOVE == FuncSwitch));
1098 if ((pToFile) && !IsAttribDir(ToAttr))
1099 {
1100 pToFile[0] = '\0';
1101 ToPathAttr = GetFileAttributesW(pTempTo);
1102 pToFile[0] = '\\';
1103 }
1104 if (pToTailSlash)
1105 {
1106 pToTailSlash[0] = '\\';
1107 }
1108 if (!b_FromMask && b_SameRoot && \
1109 SHFileStrICmpW(pTempFrom, pTempTo, NULL, NULL))
1110 { /* target is the same as source ? W98 has 0x71, W2K also */
1111 retCode = 0x71; /* is no error with FOF_RENAMEONCOLLISION */
1112 goto shfileop_exit;
1113 } /* endif */
1114 /* FO_RENAME is not only a Filter for FO_MOVE */
1115 if (FO_RENAME == FuncSwitch)
1116 {
1117 if (b_FromMask || !b_SameRoot || \
1118 !SHFileStrICmpW(pTempFrom, pTempTo, pFromFile, NULL) || \
1119 SHFileStrICmpW(pTempFrom, pTempTo, pFromFile, HIGH_ADR))
1120 {
1121 retCode = 0x73; /* must be here or before, not later */
1122 goto shfileop_exit;
1123 }
1124#ifdef W98_FO_FUNCTION /* not in W2K, later in SHFileOperationMove */
1125 if (!pToTailSlash && IsAttribDir(ToAttr))
1126 {
1127 retCode = ERROR_INVALID_NAME; /* FOF_NOCONFIRMATION, FOF_RENAMEONCOLLISION ? */
1128 goto shfileop_exit;
1129 }
1130 if (-1 != ToPathAttr || b_ToMask)
1131#else
1132 if (-1 != ToPathAttr)
1133#endif
1134 {
1135 retCode = SHFileOperationMove(&nFileOp);
1136 goto shfileop_exit;
1137 }
1138 }
1139 else \
1140 if (!b_FromMask)
1141 {
1142 if (IsAttribDir(ToPathAttr))
1143 {
1144 /* Analycing for moving SourceName to as TargetName */
1145 if (!b_MultiTo && IsAttribDir(ToAttr) && (b_MultiFrom || !b_Multi))
1146 {
1147 SHFileStrCpyCatW(pTempTo, NULL, pFromFile);
1148 /* without FOF_MULTIDESTFILES shlfileop will create dir's recursive */
1149 nFileOp.fFlags |= FOF_MULTIDESTFILES;
1150 retCode = SHFileOperationW(&nFileOp);
1151 continue;
1152 }
1153 }
1154 /* What can we do with one pair and FO_MOVE/FO_COPY ? */
1155#ifndef W98_FO_FUNCTION /* W2K */
1156 if (!b_SameRoot_MOVE || IsAttribDir(ToPathAttr))
1157#endif
1158 if (!b_MultiFrom)
1159// if ((!b_MultiFrom) && (!b_SameRoot_MOVE || IsAttribDir(ToPathAttr) || b_ToMask))
1160
1161 {
1162 if (IsAttribFile(FromAttr))
1163 {
1164 if (b_SameRoot_MOVE \
1165 /* IsAttribDir(ToPathAttr) && !pToTailSlash && (bug) */
1166 /* windos-bug, MOVE for File also with pToTailSlash, COPY not for this */
1167// && IsAttribDir(ToPathAttr))
1168 && (IsAttribDir(ToPathAttr) || b_ToMask))
1169 {
1170 /* At the same drive, we can move for FO_MOVE, dir to dir was solved later */
1171 retCode = SHFileOperationMove(&nFileOp);
1172 continue;
1173 } /* endif */
1174 if (IsAttribDir(ToPathAttr) && !pToTailSlash && !IsAttribDir(ToAttr))
1175 {
1176 if (IsAttribFile(ToAttr) && not_overwrite)
1177 {
1178 /* Rename on Collision ? */
1179 if (!ask_overwrite)
1180 {
1181 /* the retcode for this case is unknown */
1182 retCode = 0x10008;
1183 goto shfileop_exit;
1184 }
1185 /* we must change the dialog in the future for return 3-4 cases,
1186 * 'Yes','No','Yes for all','Never ?' */
1187 if (!SHELL_ConfirmDialogW(ASK_OVERWRITE_FILE, pTempTo))
1188 {
1189 nFileOp.fAnyOperationsAborted = TRUE;
1190 continue;
1191 }
1192 }
1193 if (!(SHNotifyCopyFileW(pTempFrom, pTempTo, nFileOp.fFlags & FOF_RENAMEONCOLLISION)))
1194 {
1195 /* the retcode for this case is unknown */
1196 retCode = 0x10009;
1197 }
1198 if ((FO_COPY == FuncSwitch) || retCode)
1199 continue;
1200 nFileOp.wFunc = ((level+1) << FO_LevelShift) + FO_DELETE;
1201 retCode = SHFileOperationW(&nFileOp);
1202 continue;
1203 }
1204 }
1205 if (IsAttribDir(FromAttr))
1206 {
1207#ifdef W98_FO_FUNCTION /* not in W2K, -> retCode = retCodeIsInvalid */
1208 if ((-1 == ToAttr) && (!b_SameRoot_MOVE || IsAttribDir(ToPathAttr) || b_ToMask))
1209 {
1210 if (-1 == ToPathAttr)
1211 {
1212 pToFile[0] = '\0';
1213 retCode = SHCreateDirectoryExW(NULL,pTempTo, NULL);
1214 ToPathAttr = GetFileAttributesW(pTempTo);
1215 pToFile[0] = '\\';
1216 if (retCode)
1217 goto shfileop_exit;
1218 }
1219#else
1220 if (-1 == ToAttr)
1221 {
1222#endif
1223 if (IsAttribDir(ToPathAttr))
1224 {
1225 if (pToTailSlash)
1226 pToTailSlash[0] = '\0';
1227 retCode = SHCreateDirectoryExW(NULL,pTempTo, NULL);
1228 ToAttr = GetFileAttributesW(pTempTo); /* for continuing */
1229 if (pToTailSlash)
1230 pToTailSlash[0] = '\\';
1231#ifndef W98_FO_FUNCTION /* W2k */
1232 if (retCode)
1233 goto shfileop_exit;
1234#endif
1235 }
1236 if (-1 == ToAttr)
1237 {
1238 retCode = 0x10003;
1239 goto shfileop_exit;
1240 }
1241 }
1242 if (IsAttribDir(ToAttr))
1243 {
1244 /* both are existing dirs, we (FO_)MOVE/COPY the contence */
1245 pToFile = SHFileStrCpyCatW(pTempFrom, NULL, wWildcardFile);
1246 nFileOp.fFlags &= ~FOF_MULTIDESTFILES;
1247 retCode = SHFileOperationW(&nFileOp);
1248 if ((FO_COPY == FuncSwitch) || retCode)
1249 continue;
1250 ((DWORD*)pToFile)[0] = '\0';
1251 nFileOp.wFunc = ((level+1) << FO_LevelShift) + FO_DELETE;
1252 retCode = SHFileOperationW(&nFileOp);
1253 continue;
1254 }
1255 }
1256 } /* end-!b_MultiFrom */
1257 }
1258
1259#ifdef W98_FO_FUNCTION /* not in W2K, -> retCode = retCodeIsInvalid */
1260 if (b_FromMask && \
1261 /* we do not know, why (b_SameRoot_MOVE && b_ToMask && (-1 != ToPathAttr)) */
1262 (IsAttribDir(ToAttr) || (b_SameRoot_MOVE && b_ToMask)))
1263#else
1264 if (b_FromMask && (-1 != ToPathAttr) && \
1265 /* we do not know, why (b_SameRoot_MOVE && b_ToMask && (-1 != ToPathAttr)) */
1266 (IsAttribDir(ToAttr) || (b_SameRoot_MOVE && b_ToMask)))
1267#endif
1268 {
1269 /* FO_COPY/FO_MOVE with mask, FO_DELETE are solved lang before */
1270 hFind = FindFirstFileW(pFrom, &wfd);
1271 if (INVALID_HANDLE_VALUE == hFind)
1272 {
1273 /* the retcode for this case is unknown */
1274 retCode = 0x10007;
1275 goto shfileop_exit;
1276 }
1277 nFileOp.fFlags &= ~FOF_MULTIDESTFILES;
1278 pToFile = SHFileStrCpyCatW(pTempTo, NULL, wBackslash);
1279 do
1280 {
1281 lpFileName = wfd.cAlternateFileName;
1282 if (!lpFileName[0])
1283 lpFileName = wfd.cFileName;
1284 if (IsDotDir(lpFileName) ||
1285 (IsAttribDir(wfd.dwFileAttributes) && (nFileOp.fFlags & FOF_FILESONLY)))
1286 continue; /* next name in pTempFrom(dir) */
1287 SHFileStrCpyCatW(&pFromFile[1], lpFileName, NULL);
1288 retCode = SHFileOperationW (&nFileOp);
1289 } while(!retCode && FindNextFileW(hFind, &wfd));
1290 FindClose(hFind);
1291 continue;
1292 }
1293 /* W98 returns 0x75, W2K 0x4c7 */
1294 retCode = retCodeIsInvalid;
1295 break;
1296
1297 } /* end-while */
1298
1299shfileop_exit:
1300 if (pTempFrom)
1301 HeapFree(GetProcessHeap(), 0, pTempFrom);
1302 TRACE("%s level=%ld AnyOpsAborted=%s ret=0x%lx, with %s %s%s\n",
1303 cFO_Name, level, nFileOp.fAnyOperationsAborted ? "TRUE":"FALSE",
1304 retCode, debugstr_w(pFrom), pTo ? "-> ":"", debugstr_w(pTo));
1305
1306 lpFileOp->hNameMappings = nFileOp.hNameMappings;
1307 lpFileOp->fAnyOperationsAborted = nFileOp.fAnyOperationsAborted;
1308 return retCode;
1309}
1310
1311/*************************************************************************
1312 * SHFileOperation [SHELL32.@]
1313 *
1314 */
1315DWORD WINAPI SHFileOperationAW(LPVOID lpFileOp)
1316{
1317 if (SHELL_OsIsUnicode())
1318 return SHFileOperationW(lpFileOp);
1319 return SHFileOperationA(lpFileOp);
1320}
1321
1322/*************************************************************************
1323 * SheGetDirW [SHELL32.281]
1324 *
1325 */
1326HRESULT WINAPI SheGetDirW(LPWSTR u, LPWSTR v)
1327{ FIXME("%p %p stub\n",u,v);
1328 return 0;
1329}
1330
1331/*************************************************************************
1332 * SheChangeDirW [SHELL32.274]
1333 *
1334 */
1335HRESULT WINAPI SheChangeDirW(LPWSTR u)
1336{ FIXME("(%s),stub\n",debugstr_w(u));
1337 return 0;
1338}
1339
1340/*************************************************************************
1341 * IsNetDrive [SHELL32.66]
1342 */
1343BOOL WINAPI IsNetDrive(DWORD drive)
1344{
1345 char root[4];
1346 strcpy(root, "A:\\");
1347 root[0] += (char)drive;
1348 return (GetDriveTypeA(root) == DRIVE_REMOTE);
1349}
1350
Note: See TracBrowser for help on using the repository browser.