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

Last change on this file since 9552 was 9552, checked in by sandervl, 23 years ago

DT: FO_RENAME updates for shell file operations

File size: 27.0 KB
Line 
1/*
2 * SHFileOperation
3 *
4 * Copyright 2000 Juergen Schmied
5 * Copyright 2002 Andriy Palamarchuk
6 * Copyright 2002 Dietrich Teickner (from Odin)
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include "config.h"
24#include "wine/port.h"
25
26#include <string.h>
27
28#include "winreg.h"
29#include "shellapi.h"
30#include "shlobj.h"
31#include "shresdef.h"
32#include "shell32_main.h"
33#include "undocshell.h"
34#include "shlwapi.h"
35#include "wine/debug.h"
36
37WINE_DEFAULT_DEBUG_CHANNEL(shell);
38
39BOOL SHELL_ConfirmDialog (int nKindOfDialog, LPCSTR szDir)
40{
41 char szCaption[255], szText[255], szBuffer[MAX_PATH + 256];
42 UINT caption_resource_id, text_resource_id;
43
44 switch(nKindOfDialog) {
45
46 case ASK_DELETE_FILE:
47 caption_resource_id = IDS_DELETEITEM_CAPTION;
48 text_resource_id = IDS_DELETEITEM_TEXT;
49 break;
50 case ASK_DELETE_FOLDER:
51 caption_resource_id = IDS_DELETEFOLDER_CAPTION;
52 text_resource_id = IDS_DELETEITEM_TEXT;
53 break;
54 case ASK_DELETE_MULTIPLE_ITEM:
55 caption_resource_id = IDS_DELETEITEM_CAPTION;
56 text_resource_id = IDS_DELETEMULTIPLE_TEXT;
57 break;
58 case ASK_OVERWRITE_FILE:
59 caption_resource_id = IDS_OVERWRITEFILE_CAPTION;
60 text_resource_id = IDS_OVERWRITEFILE_TEXT;
61 break;
62 default:
63 FIXME(" Unhandled nKindOfDialog %d stub\n", nKindOfDialog);
64 return FALSE;
65 }
66
67 LoadStringA(shell32_hInstance, caption_resource_id, szCaption, sizeof(szCaption));
68 LoadStringA(shell32_hInstance, text_resource_id, szText, sizeof(szText));
69
70 FormatMessageA(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
71 szText, 0, 0, szBuffer, sizeof(szBuffer), (va_list*)&szDir);
72
73 return (IDOK == MessageBoxA(GetActiveWindow(), szBuffer, szCaption, MB_OKCANCEL | MB_ICONEXCLAMATION));
74}
75
76/**************************************************************************
77 * SHELL_DeleteDirectoryA()
78 *
79 * like rm -r
80 */
81
82BOOL SHELL_DeleteDirectoryA(LPCSTR pszDir, BOOL bShowUI)
83{
84 BOOL ret = FALSE;
85 HANDLE hFind;
86 WIN32_FIND_DATAA wfd;
87 char szTemp[MAX_PATH];
88
89 strcpy(szTemp, pszDir);
90 PathAddBackslashA(szTemp);
91 strcat(szTemp, "*.*");
92
93 if (bShowUI && !SHELL_ConfirmDialog(ASK_DELETE_FOLDER, pszDir))
94 return FALSE;
95
96 if(INVALID_HANDLE_VALUE != (hFind = FindFirstFileA(szTemp, &wfd)))
97 {
98 do
99 {
100 if(strcasecmp(wfd.cFileName, ".") && strcasecmp(wfd.cFileName, ".."))
101 {
102 strcpy(szTemp, pszDir);
103 PathAddBackslashA(szTemp);
104 strcat(szTemp, wfd.cFileName);
105
106 if(FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
107 SHELL_DeleteDirectoryA(szTemp, FALSE);
108 else
109 DeleteFileA(szTemp);
110 }
111 } while(FindNextFileA(hFind, &wfd));
112
113 FindClose(hFind);
114 ret = RemoveDirectoryA(pszDir);
115 }
116
117 return ret;
118}
119
120/**************************************************************************
121 * SHELL_DeleteFileA()
122 */
123
124BOOL SHELL_DeleteFileA(LPCSTR pszFile, BOOL bShowUI)
125{
126 if (bShowUI && !SHELL_ConfirmDialog(ASK_DELETE_FILE, pszFile))
127 return FALSE;
128
129 return DeleteFileA(pszFile);
130}
131
132/*************************************************************************
133 * SHCreateDirectory [SHELL32.165]
134 *
135 * NOTES
136 * exported by ordinal
137 * not sure about LPSECURITY_ATTRIBUTES
138 */
139DWORD WINAPI SHCreateDirectory(LPSECURITY_ATTRIBUTES sec,LPCSTR path)
140{
141 DWORD ret;
142 TRACE("(%p,%s)\n",sec,path);
143 if ((ret = CreateDirectoryA(path,sec)))
144 {
145 SHChangeNotifyA(SHCNE_MKDIR, SHCNF_PATHA, path, NULL);
146 }
147 return ret;
148}
149
150/************************************************************************
151 * Win32DeleteFile [SHELL32.164]
152 *
153 * Deletes a file. Also triggers a change notify if one exists.
154 *
155 * FIXME:
156 * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be
157 * ANSI. Is this Unicode on NT?
158 *
159 */
160
161BOOL WINAPI Win32DeleteFile(LPSTR fName)
162{
163 TRACE("%p(%s)\n", fName, fName);
164
165 DeleteFileA(fName);
166 SHChangeNotifyA(SHCNE_DELETE, SHCNF_PATHA, fName, NULL);
167 return TRUE;
168}
169
170/*************************************************************************
171 *
172 * SHFileTyp (internal)
173 *
174 */
175DWORD SHFileTyp(LPSTR pFromTo, long *len, LPSTR pTemp, long *lenTemp, LPSTR *pFile, DWORD *PathAttr, DWORD *Attr) {
176#define TypisBad -1
177#define TypisUnkn 0
178#define TypisNewPath 1
179#define TypisRoot 2
180#define TypisDirS 3
181#define TypisDir 4
182#define TypisFile 5
183#define TypisName 6
184#define TypisNameS 7
185#define TypisMask 8
186 DWORD Typ = TypisUnkn;
187 DWORD i_Attr;
188 DWORD i_PathAttr;
189 BOOL i_Slash = FALSE;
190 LPSTR i_pFile;
191 LPSTR i_pLastSlash;
192 long i_lenTemp;
193 long i_len = *len = strlen(pFromTo);
194 if (i_len == 0) return TypisUnkn;
195 strcpy(pTemp,pFromTo);
196 if (pTemp[1] == ':' && pTemp[0] >= 'a' && pTemp[0] <= 'z') pTemp[0] = (pTemp[0] & (0xff - ' '));
197 *pFile = i_pFile = &pTemp[i_len];
198 pTemp[i_len+1] = '\0';
199 if (i_len == 0) return /* ?? */ TypisBad;
200 if (PathIsRootA(pFromTo)) Typ = TypisRoot;
201 i_pLastSlash = strrchr(pTemp,'\\');
202 if (i_pLastSlash == NULL) return TypisBad /* ??? */;
203 i_Slash = (i_pLastSlash[1] == '\0');
204 *lenTemp = i_lenTemp = strlen(pTemp) - i_Slash;
205 *pFile = i_pFile = &pTemp[i_lenTemp];
206 if (Typ != TypisRoot) i_pFile[0] = '\0';
207 *PathAttr = *Attr = i_PathAttr = i_Attr = GetFileAttributesA(pTemp);
208 if (i_Attr == -1) {
209 if (Typ == TypisRoot) Typ = TypisBad;
210 } else {
211 if (i_Attr & FILE_ATTRIBUTE_DIRECTORY) {
212 if (Typ == TypisUnkn) {
213 Typ = TypisDir;
214 if (i_Slash) Typ = TypisDirS;
215 }
216 } else {
217 if (Typ == TypisUnkn && !i_Slash) {
218 Typ = TypisFile;
219 } else Typ = TypisBad;
220 }
221 }
222// is the directory exists with \*.* ?
223 i_pFile = strrchr(pTemp,'\\');
224 if (NULL == i_pFile) Typ = TypisBad;
225 if (Typ == TypisUnkn || Typ == TypisFile) {
226 PathRemoveFileSpecA(pTemp);
227// mask in Path ?
228 if (NULL != strpbrk(pTemp,"*?\0")) {
229 Typ = TypisBad;
230 } else {
231 while (-1 == (i_PathAttr = GetFileAttributesA(pTemp)) && !PathIsRootA(pTemp)) {
232 PathRemoveFileSpecA(pTemp);
233 Typ = TypisNewPath;
234 }
235 i_lenTemp = strlen(pTemp);
236 if (pTemp[i_lenTemp - 1] == '\\') i_lenTemp--;
237 *lenTemp = i_lenTemp;
238 *pFile = i_pFile = &pTemp[i_lenTemp];
239 *PathAttr = i_PathAttr;
240 if (-1 == i_PathAttr || !(i_PathAttr & FILE_ATTRIBUTE_DIRECTORY)) Typ = TypisBad;
241 }
242 strcpy(&pTemp[1],&pFromTo[1]);
243 if (Typ == TypisUnkn && i_PathAttr != -1 && (i_PathAttr & FILE_ATTRIBUTE_DIRECTORY)) {
244 if (NULL == strpbrk(i_pFile,"*?\0")) {
245 if (!i_Slash) {
246 *lenTemp = i_lenTemp = strlen(pTemp);
247 *pFile = &pTemp[i_lenTemp];
248 Typ = TypisName;
249 } else Typ = TypisNameS;
250 } else Typ = TypisMask;
251 }
252 }
253 i_pLastSlash[0] = '\\';
254 return Typ;
255}
256
257/*************************************************************************
258 * SHFileOperationA [SHELL32.@]
259 *
260 * NOTES
261 * exported by name
262 */
263DWORD WINAPI SHFileOperationA (LPSHFILEOPSTRUCTA lpFileOp)
264{
265 LPSTR pFrom = (LPSTR)lpFileOp->pFrom;
266 LPSTR pTo = (LPSTR)lpFileOp->pTo;
267 LPSTR pTempTo;
268 DWORD FromAttr;
269 DWORD ToAttr;
270 DWORD FromPathAttr;
271 DWORD ToPathAttr;
272 DWORD FromTyp;
273 DWORD ToTyp = TypisBad;
274 DWORD zTyp;
275 LPSTR pFromFile;
276 LPSTR pToFile;
277 LPSTR pTempFrom = NULL;
278 LPSTR pToSlash;
279 LPSTR pToFuncTXT = "FO_COPY";
280 FILEOP_FLAGS OFl = ((FILEOP_FLAGS)lpFileOp->fFlags & 0x7ff);
281 BOOL Multi = ((lpFileOp->fFlags & FOF_MULTIDESTFILES) != 0);
282 BOOL MakeDir = FALSE;
283 BOOL not_overwrite;
284 BOOL TargetisDir;
285 BOOL ask_overwrite;
286 BOOL ToSingle;
287 BOOL recurseinto;
288 long lenFrom = -1;
289 long lenTo = -1;
290 long lenTempFrom;
291 long lenTempTo;
292 long retCode = 0x75;
293 long TempretCode = 0;
294 long where = 0;
295 long FuncSwitch = (lpFileOp->wFunc & 15);
296 SHFILEOPSTRUCTA nlpFileOp = *(lpFileOp);
297 long level= nlpFileOp.wFunc>>4;
298 HANDLE hFind;
299 WIN32_FIND_DATAA wfd;
300
301/* default no error
302*/
303 lpFileOp->fAnyOperationsAborted=FALSE;
304 nlpFileOp.fAnyOperationsAborted=FALSE;
305 level++;
306 nlpFileOp.wFunc = (level<<4) + FuncSwitch;
307 if (level == 1)
308 TRACE("flags (0x%04x) : %s%s%s%s%s%s%s%s%s%s%s%s \n", lpFileOp->fFlags,
309 lpFileOp->fFlags & FOF_MULTIDESTFILES ? "FOF_MULTIDESTFILES " : "",
310 lpFileOp->fFlags & FOF_CONFIRMMOUSE ? "FOF_CONFIRMMOUSE " : "",
311 lpFileOp->fFlags & FOF_SILENT ? "FOF_SILENT " : "",
312 lpFileOp->fFlags & FOF_RENAMEONCOLLISION ? "FOF_RENAMEONCOLLISION " : "",
313 lpFileOp->fFlags & FOF_NOCONFIRMATION ? "FOF_NOCONFIRMATION " : "",
314 lpFileOp->fFlags & FOF_WANTMAPPINGHANDLE ? "FOF_WANTMAPPINGHANDLE " : "",
315 lpFileOp->fFlags & FOF_ALLOWUNDO ? "FOF_ALLOWUNDO " : "",
316 lpFileOp->fFlags & FOF_FILESONLY ? "FOF_FILESONLY " : "",
317 lpFileOp->fFlags & FOF_SIMPLEPROGRESS ? "FOF_SIMPLEPROGRESS " : "",
318 lpFileOp->fFlags & FOF_NOCONFIRMMKDIR ? "FOF_NOCONFIRMMKDIR " : "",
319 lpFileOp->fFlags & FOF_NOERRORUI ? "FOF_NOERRORUI " : "",
320 lpFileOp->fFlags & 0xf800 ? "MORE-UNKNOWN-Flags" : "");
321 switch(FuncSwitch) {
322 case FO_MOVE:
323 retCode = 0xb7;
324 pToFuncTXT = "FO_MOVE";
325 case FO_COPY:
326 {
327 /* establish when pTo is interpreted as the name of the destination file
328 * or the directory where the Fromfile should be copied to.
329 * This depends on:
330 * (1) pTo points to the name of an existing directory;
331 * (2) the flag FOF_MULTIDESTFILES is present;
332 * (3) whether pFrom point to multiple filenames.
333 *
334 * Some experiments:
335 *
336 * destisdir 1 1 1 1 0 0 0 0
337 * FOF_MULTIDESTFILES 1 1 0 0 1 1 0 0
338 * multiple from filenames 1 0 1 0 1 0 1 0
339 * ---------------
340 * copy files to dir 1 0 1 1 0 0 1 0
341 * create dir 0 0 0 0 0 0 1 0
342 */
343 nlpFileOp.pFrom = pTempFrom = HeapAlloc(GetProcessHeap(), 0, 3 * MAX_PATH+6);
344 nlpFileOp.pTo = pTempTo = &pTempFrom[MAX_PATH+4];
345/*
346 * FOF_MULTIDESTFILES, FOF_NOCONFIRMATION, FOF_FILESONLY are implemented
347 * FOF_CONFIRMMOUSE, FOF_SILENT, FOF_NOCONFIRMMKDIR, FOF_SIMPLEPROGRESS are not implemented and ignored
348 * FOF_RENAMEONCOLLISION are implemented partially and breaks if file exist
349 * FOF_ALLOWUNDO, FOF_WANTMAPPINGHANDLE are not implemented and breaks
350 * if any other flag set, an error occurs
351 */
352 TRACE(__FUNCTION__" %s level=%d lpFileOp->fFlags=0x%x\n", pToFuncTXT, level, lpFileOp->fFlags);
353// OFl = (OFl & (-1 - (FOF_MULTIDESTFILES | FOF_FILESONLY)));
354// OFl = (OFl ^ (FOF_SILENT | FOF_NOCONFIRMATION | FOF_SIMPLEPROGRESS | FOF_NOCONFIRMMKDIR));
355 OFl = (OFl & ( ~ (FOF_MULTIDESTFILES | FOF_NOCONFIRMATION | FOF_FILESONLY))); // implemented
356 OFl = (OFl ^ (FOF_SILENT | FOF_NOCONFIRMMKDIR)); // ignored, if one
357 OFl = (OFl & ( ~ FOF_SIMPLEPROGRESS)); // ignored, only with FOF_SILENT
358 if (OFl) {
359 if (OFl & ( ~ (FOF_CONFIRMMOUSE | FOF_SILENT | FOF_RENAMEONCOLLISION | FOF_NOCONFIRMMKDIR))) {
360 TRACE(__FUNCTION__" %s level=%d lpFileOp->fFlags=0x%x not implemented, Aborted=TRUE, stub\n", pToFuncTXT, level, OFl);
361 nlpFileOp.fAnyOperationsAborted=TRUE;
362 } else {
363 TRACE(__FUNCTION__" %s level=%d lpFileOp->fFlags=0x%x not full implemented ,stub\n", pToFuncTXT, level, OFl);
364 } /* endif */
365 } /* endif */
366
367 ask_overwrite = (!(lpFileOp->fFlags & FOF_NOCONFIRMATION) && !(lpFileOp->fFlags & FOF_RENAMEONCOLLISION));
368 not_overwrite = (!(lpFileOp->fFlags & FOF_NOCONFIRMATION) || (lpFileOp->fFlags & FOF_RENAMEONCOLLISION));
369
370// need break at error before change sourcepointer
371 while(!nlpFileOp.fAnyOperationsAborted && (pFrom+=lenFrom+1)[0]) {
372
373 if (Multi) pTo += lenTo + 1;
374 if(!pTo[0]) {
375 nlpFileOp.fAnyOperationsAborted=TRUE;
376 where = 200;
377 break;
378 } /* endif */
379
380 FromTyp = SHFileTyp(pFrom, &lenFrom,
381 pTempFrom, &lenTempFrom,
382 &pFromFile,
383 &FromPathAttr, &FromAttr);
384
385 zTyp = ToTyp;
386 ToTyp = SHFileTyp(pTo, &lenTo,
387 pTempTo, &lenTempTo,
388 &pToFile,
389 &ToPathAttr, &ToAttr);
390
391 TRACE(__FUNCTION__" %s level=%d From='%s'(%d,%d,%d) To='%s'(%d,%d,%d)\n", pToFuncTXT,
392 level, pTempFrom, FromTyp, lenFrom, lenTempFrom, pTempTo, ToTyp, lenTo, lenTempTo);
393
394/* Check Source */
395 if (FromTyp != TypisDir
396 && FromTyp != TypisFile
397 && FromTyp != TypisMask) {
398 nlpFileOp.fAnyOperationsAborted=TRUE;
399 where = 201;
400 retCode=0x402;
401 break;
402 } /* endif */
403/* fix for more then one source for one target */
404 if (ToTyp == TypisUnkn
405 && zTyp < TypisFile
406 && zTyp > TypisUnkn
407 && lenTo == 0) {
408 pToFile[0] = '\0';
409 ToTyp = zTyp;
410 lenTo = strlen(pTempTo);
411 } /* endif */
412/* recursiv creating from directorys are not valid for FO_MOVE. */
413 if (FuncSwitch == FO_MOVE && ToTyp == TypisNewPath) ToTyp = TypisUnkn;
414/* Check Target */
415 if (ToTyp == TypisMask) {
416 nlpFileOp.fAnyOperationsAborted=TRUE;
417 where = 202;
418 if (FromTyp != TypisMask) {
419 retCode=0x10003;
420 } else {
421 if ((lpFileOp->wFunc & 0xf) == FO_MOVE) TempretCode = 0x2;
422 }
423 break;
424 } /* endif */
425 if (ToTyp == TypisBad) {
426 nlpFileOp.fAnyOperationsAborted=TRUE;
427 where = 203;
428 retCode=0x402;
429 break;
430 } /* endif */
431 TargetisDir = (ToTyp == TypisDir || ToTyp == TypisDirS || ToTyp == TypisRoot);
432 ToSingle = (pTo[lenTo+1]=='\0');
433 if (Multi && ToSingle) {
434 Multi = (!(pFrom[lenFrom+1]!='\0'));
435 }
436
437 switch(FromTyp) {
438 case TypisFile: { /* Source is File */
439 if (ToTyp == TypisName) {
440 if (ToSingle && pFrom[lenFrom+1] == '\0') break;
441 if (Multi) break;
442 }
443 if (ToTyp == TypisFile) {
444 if (0 == strcmp(pTempFrom,pTempTo)) { /* target is the same as source ? */
445 nlpFileOp.fAnyOperationsAborted=TRUE;
446 retCode = 0x71;
447 where = 221;
448 break;
449 } /* endif */
450 if (ask_overwrite && SHELL_ConfirmDialog (ASK_OVERWRITE_FILE, pTempTo)) break;
451 if (!not_overwrite) break;
452 if (FuncSwitch == FO_MOVE && (!Multi)) TempretCode = 0x75;
453 }
454 if (!Multi && TargetisDir) {
455 strcpy(pToFile,pFromFile);
456 pTempTo[strlen(pTempTo)+1] = '\0';
457 ToAttr = GetFileAttributesA(pTempTo);
458 if (ToAttr == -1) break;
459 if (!(ToAttr & FILE_ATTRIBUTE_DIRECTORY)) break;
460 }
461 if (FuncSwitch == FO_MOVE && (ToTyp == TypisUnkn || ToTyp == TypisName || ToTyp == TypisNameS)) TempretCode = 0x75;
462 nlpFileOp.fAnyOperationsAborted=TRUE;
463 where = 210 + TypisFile;
464 break;
465 }
466 case TypisMask: { /* Source is Mask */
467 if (TargetisDir) break;
468 nlpFileOp.fAnyOperationsAborted=TRUE;
469 where = 210 + TypisMask;
470 break;
471 }
472 case TypisRoot: /* Source is Root, only small test with this */
473 case TypisDir: { /* Source is Dir */
474 if (TargetisDir) {
475/* From Root to Root not full tested, also no Comparsion in W98 */
476 MakeDir = (!(lpFileOp->fFlags & FOF_MULTIDESTFILES) && ToTyp != TypisRoot);
477 break;
478 }
479 MakeDir = TRUE;
480 if ((ToTyp == TypisName || ToTyp == TypisNameS || ToTyp == TypisNewPath)) break;
481 nlpFileOp.fAnyOperationsAborted=TRUE;
482 where = 210 + TypisDir;
483 break;
484 }
485 default: {
486 nlpFileOp.fAnyOperationsAborted=TRUE;
487 where = 210 + FromTyp;
488/* retCode=0x750; */
489 break;
490 }
491 }
492 if (nlpFileOp.fAnyOperationsAborted) {
493 if (FuncSwitch == FO_MOVE && (ToTyp == TypisUnkn || ToTyp == TypisName || ToTyp == TypisNameS)) TempretCode = 0x75;
494 break;
495 }
496
497 recurseinto = FALSE;
498
499 if (FuncSwitch == FO_MOVE && ToTyp == TypisName && (FromTyp == TypisDir || FromTyp == TypisFile)) {
500 if (':' == pTempFrom[1] && ':' == pTempTo[1] && pTempFrom[0] == pTempTo[0]) {
501 if (MoveFileA(pTempFrom, pTempTo)) continue;
502 }
503 }
504 if (FromTyp == TypisDir && TargetisDir && MakeDir) {
505 PathRemoveFileSpecA(pTempFrom);
506 lenTempTo = strlen(pTempFrom);
507 strcpy(&pTempFrom[1],&pFrom[1]);
508 if (pTempFrom[lenTempTo-1] == '\\') lenTempTo--;
509 strcpy(pToFile,&pFrom[lenTempTo]);
510 lenTempTo = strlen(pTempTo);
511 pTempTo[lenTempTo+1] = '\0';
512 }
513
514 if (MakeDir) {
515 pToSlash = NULL;
516 if ((ToTyp == TypisNameS || ToTyp == TypisNewPath)) {
517 pToSlash = strchr(&pToFile[1],'\\');
518 if (NULL != pToSlash) pToSlash[0] = '\0';
519 }
520 TRACE(__FUNCTION__" %s level=%d Creating Directory '%s'\n", pToFuncTXT, level, pTempTo);
521 SHCreateDirectory(NULL,pTempTo);
522 ToPathAttr = GetFileAttributesA(pTempTo);
523 if (NULL != pToSlash) pToSlash[0] = '\\';
524 if (ToPathAttr == -1) {
525 nlpFileOp.fAnyOperationsAborted=TRUE;
526 retCode=0x10004;
527 where = 220;
528 break;
529 }
530 recurseinto = TRUE;
531 }
532 if (ToTyp != TypisNewPath) {
533 if (FromTyp == TypisDir || FromTyp == TypisRoot) {
534 strcpy(pFromFile,"\\*.*");
535 pTempFrom[strlen(pTempFrom)+1] = '\0';
536 recurseinto = TRUE;
537 } else {
538 if (FromTyp == TypisFile) {
539 if (TargetisDir) {
540 recurseinto = TRUE;
541 } else {
542 if (CopyFileA(pTempFrom, pTempTo, FALSE)) {
543 if (FuncSwitch == FO_COPY) continue;
544 if (DeleteFileA(pTempFrom)) continue;
545 }
546 nlpFileOp.fAnyOperationsAborted=TRUE;
547 where = 222;
548 break;
549 }
550 } /* endif */
551 }
552 }
553 if (recurseinto) {
554 TRACE(__FUNCTION__" %s level=%d Entering into Directory '%s'\n", pToFuncTXT, level, pTempTo);
555 TempretCode = SHFileOperationA (&nlpFileOp);
556 if (nlpFileOp.fAnyOperationsAborted) {where = 223;break;}
557 if (FuncSwitch == FO_MOVE && FromTyp == TypisDir) {
558 if (!(RemoveDirectoryA(pFrom))) {
559 nlpFileOp.fAnyOperationsAborted=TRUE;
560 where = 100;
561 break;
562 }
563 }
564 continue; /* next pTo/pFrom */
565 }
566 if (FromTyp == TypisMask) {
567 hFind = FindFirstFileA(pTempFrom, &wfd);
568 if (INVALID_HANDLE_VALUE == hFind) {
569 nlpFileOp.fAnyOperationsAborted=TRUE;
570 retCode=0x79;
571 where = 224;
572 break;
573 }
574
575 TRACE(__FUNCTION__" %s level=%d between Subdir %s -> %s'\n", pToFuncTXT, level, nlpFileOp.pFrom, nlpFileOp.pTo);
576 nlpFileOp.fFlags = (nlpFileOp.fFlags & (-1 - (FOF_MULTIDESTFILES)));
577
578 do {
579 TRACE(__FUNCTION__" %s level=%d find '%s'\n", pToFuncTXT, level, wfd.cFileName);
580 if (0==strcmp(wfd.cFileName,".")) continue;
581 if (0==strcmp(wfd.cFileName,"..")) continue;
582 if ((nlpFileOp.fFlags & FOF_FILESONLY) && (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)) {
583 continue; /* next name in pFrom(dir) */
584 } /* endif */
585
586 strcpy(&pFromFile[1],wfd.cFileName);
587 pTempFrom[strlen(pTempFrom)+1]='\0';
588
589 TempretCode = SHFileOperationA (&nlpFileOp);
590
591 if (nlpFileOp.fAnyOperationsAborted) {where = 230;break;}
592
593 } while(FindNextFileA(hFind, &wfd));
594 }
595 }
596 break;
597 }
598
599 case FO_DELETE:
600 TRACE(__FUNCTION__" FO_DELETE level=%d\n",level);
601/* need break at error before change sourcepointer */
602 while(!nlpFileOp.fAnyOperationsAborted && (pFrom+=lenFrom+1)[0]) {
603 lenFrom=strlen(pFrom);
604 FromAttr = GetFileAttributesA(pFrom);
605 if (!(FromAttr & FILE_ATTRIBUTE_DIRECTORY)) {
606 TRACE(__FUNCTION__" FO_DELETE level=%d File='%s'\n",level , pFrom);
607 if (DeleteFileA(pFrom)) continue;
608 nlpFileOp.fAnyOperationsAborted=TRUE;
609/* retCode = 0x71; */
610 where = 301;
611 break;
612 }
613 if (!(pTempFrom)) pTempFrom = HeapAlloc(GetProcessHeap(), 0, MAX_PATH+2);
614 strcpy(pTempFrom,pFrom);
615 PathRemoveBackslashA(pTempFrom);
616 FromAttr = GetFileAttributesA(pTempFrom);
617 if (!(FromAttr & FILE_ATTRIBUTE_DIRECTORY) ) {
618 nlpFileOp.fAnyOperationsAborted=TRUE;
619/* retCode = 0x71; */
620 where = 302;
621 break;
622 }
623/* is Source an existing directory\*.* ? */
624 if (FromAttr == -1) {
625 PathRemoveFileSpecA(pTempFrom);
626 FromAttr = GetFileAttributesA(pTempFrom);
627 }
628
629 PathAddBackslashA(pTempFrom);
630 lenTempFrom = strlen(pTempFrom);
631 pFromFile=&pTempFrom[lenTempFrom];
632
633 if (FromAttr == -1 ||
634 ((lenTempFrom==lenFrom) && !PathIsRootA(pFrom)) ||
635 !(FromAttr & FILE_ATTRIBUTE_DIRECTORY) ||
636 !(('\0'==pFrom[lenTempFrom]) || (0==strcmp(&pFrom[lenTempFrom],"*.*"))) ) {
637 retCode=0x402;
638 nlpFileOp.fAnyOperationsAborted=TRUE;
639 where = 303;
640 break;
641 }
642 strcpy(pFromFile, "*.*");
643 lenTempFrom = strlen(pTempFrom);
644 if (lenFrom < lenTempFrom) {
645/* Source is without \*.* */
646 pTempFrom[lenTempFrom+1]='\0';
647 nlpFileOp.pFrom = pTempFrom;
648
649 TRACE(__FUNCTION__" FO_DELETE level=%d Entering Directory '%s'\n",level , nlpFileOp.pFrom);
650 TempretCode = SHFileOperationA (&nlpFileOp);
651
652 if (nlpFileOp.fAnyOperationsAborted) {break;}
653/* Call SHELL_DeleteDirectoryA ? */
654 if (RemoveDirectoryA(pFrom)) continue;
655 nlpFileOp.fAnyOperationsAborted=TRUE;
656 where = 304;
657 break;
658 }
659 hFind = FindFirstFileA(pTempFrom, &wfd);
660 if (INVALID_HANDLE_VALUE == hFind) {
661 nlpFileOp.fAnyOperationsAborted=TRUE;
662 retCode=0x79;
663 where = 303;
664 break;
665 }
666
667 nlpFileOp.pFrom = pTempFrom;
668
669 nlpFileOp.fFlags = (nlpFileOp.fFlags & (-1 - (FOF_MULTIDESTFILES)));
670
671 TRACE(__FUNCTION__" FO_DELETE level=%d Delete in Subdir %s'\n",level , nlpFileOp.pFrom);
672
673 do {
674 TRACE(__FUNCTION__" FO_DELETE level=%d find '%s'\n",level , wfd.cFileName);
675 if (0==strcmp(wfd.cFileName,".")) continue;
676 if (0==strcmp(wfd.cFileName,"..")) continue;
677 if ((nlpFileOp.fFlags & FOF_FILESONLY) && (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)) {
678 continue;
679 } /* endif */
680
681 strcpy(pFromFile,wfd.cFileName);
682 pTempFrom[strlen(pTempFrom)+1]='\0';
683
684 TempretCode = SHFileOperationA (&nlpFileOp);
685
686 if (nlpFileOp.fAnyOperationsAborted) {where = 304;break;}
687
688 } while(FindNextFileA(hFind, &wfd));
689
690 FindClose(hFind);
691 if (nlpFileOp.fAnyOperationsAborted) {where = 305;break;}
692 continue;
693 }
694 break;
695 case 0:
696 break;
697 case FO_RENAME:
698 TRACE("File Rename:\n");
699 if (pFrom[strlen(pFrom) + 1] != '\0')
700 {
701 WARN("Attempt to rename more than one file\n");
702 lpFileOp->fAnyOperationsAborted=TRUE;
703 retCode=0;
704 break;
705 }
706 TRACE("From %s, To %s\n", pFrom, pTo);
707 pTempFrom = HeapAlloc(GetProcessHeap(), 0, 2 * MAX_PATH+6);
708 pTempTo = &pTempFrom[MAX_PATH+4];
709
710 FromTyp = SHFileTyp(pFrom, &lenFrom,
711 pTempFrom, &lenTempFrom,
712 &pFromFile,
713 &FromPathAttr, &FromAttr);
714
715 ToTyp = SHFileTyp(pTo, &lenTo,
716 pTempTo, &lenTempTo,
717 &pToFile,
718 &ToPathAttr, &ToAttr);
719
720 TRACE(__FUNCTION__" FO_RENAME level=%d From='%s'(%d,%d,%d) To='%s'(%d,%d,%d)\n",
721 level, pTempFrom, FromTyp, lenFrom, lenTempFrom, pTempTo, ToTyp, lenTo, lenTempTo);
722
723/* Check Target, Target = Dir is invalid, must be befor the next SourceCheck */
724 if (FromTyp == TypisDir && ToTyp == TypisDir) {
725 nlpFileOp.fAnyOperationsAborted=TRUE;
726 retCode=0x7b;
727 break;
728 } /* endif */
729/* Check Source */
730 if (FromTyp == TypisDirS || FromTyp == TypisName
731/* for (FromTyp == TypisNewPath) to many, all are invalid and not are retCode 0x402 */
732 || (FromTyp == TypisNewPath && (ToTyp != TypisDir && ToTyp != TypisName))) {
733 nlpFileOp.fAnyOperationsAborted=TRUE;
734 retCode=0x402;
735 break;
736 } /* endif */
737/* Check Target, Target = NewDir is invalid, must be after 0x402 ? */
738 if (ToTyp == TypisNewPath) {
739 nlpFileOp.fAnyOperationsAborted=TRUE;
740 retCode=0x75;
741 break;
742 } /* endif */
743 if (ToTyp == TypisDirS) {
744 nlpFileOp.fAnyOperationsAborted=TRUE;
745 retCode=0xb7;
746 break;
747 } /* endif */
748/* What is with existing Target, can we replace it ? */
749 if (((FromTyp == TypisFile || FromTyp == TypisDir) && ToTyp == TypisName)
750 || (FromTyp == TypisDir && ToTyp == TypisNameS)) {
751 if (ToTyp == TypisNameS) pTempTo[--lenTo] = '\0';
752 lenTempTo = lenTempFrom = 2;
753/* FileRename only in the same path !! ?? */
754 if (FromTyp == TypisFile) {
755 pFromFile = strrchr(pTempFrom,'\\');
756 pToFile = strrchr(pTempTo,'\\');
757 if (pFromFile == NULL || pToFile == NULL) {
758 if (ToTyp == TypisNameS) pTempTo[lenTo++] = '\\';
759 break;
760 }
761 pFromFile[0] = '\0';
762 pToFile[0] = '\0';
763 lenTempTo = strlen(pTempTo);
764 lenTempFrom = strlen(pTempFrom);
765 pFromFile[0] = '\\';
766 pToFile[0] = '\\';
767 }
768 if (lenTempTo == lenTempFrom && 0 == memcmp(pTempTo,pTempFrom,lenTempTo)) {
769 retCode = !MoveFileA(pTempFrom, pTempTo);
770 if (ToTyp == TypisNameS) pTempTo[lenTo++] = '\\';
771 if (retCode) nlpFileOp.fAnyOperationsAborted=TRUE;
772 break;
773 }
774 }
775
776 retCode=0x73;
777 nlpFileOp.fAnyOperationsAborted=TRUE;
778 break;
779
780 default:
781 TRACE(__FUNCTION__" Unhandled shell file operation %d at level=%d stub\n",(lpFileOp->wFunc & 15), level );
782 lpFileOp->fAnyOperationsAborted=TRUE;
783 return 1;
784 }
785 if (pTempFrom) HeapFree(GetProcessHeap(), 0, pTempFrom);
786
787 if (nlpFileOp.fAnyOperationsAborted) {
788 lpFileOp->fAnyOperationsAborted=TRUE;
789 if (TempretCode > 0 /* retCode */) {
790 retCode = TempretCode;
791 } /* endif */
792 }
793 if (lpFileOp->fAnyOperationsAborted==TRUE) {
794 if (FO_DELETE == (lpFileOp->wFunc & 15)) {
795 TRACE(__FUNCTION__" Setting AnyOpsAborted=TRUE level=%d ret=0x%x, at=%i with %s\n",level, retCode,where,pFrom);
796 } else {
797 TRACE(__FUNCTION__" Setting AnyOpsAborted=TRUE level=%d ret=0x%x, at=%i with %s -> %s\n",level, retCode,where,pFrom,pTo);
798 }
799 return retCode;
800 } /* endif */
801 TRACE(__FUNCTION__" Setting AnyOpsAborted=FALSE\n");
802 return 0;
803
804}
805
806/*************************************************************************
807 * SHFileOperationW [SHELL32.@]
808 *
809 * NOTES
810 * exported by name
811 */
812DWORD WINAPI SHFileOperationW (LPSHFILEOPSTRUCTW lpFileOp)
813{
814 FIXME("(%p):stub.\n", lpFileOp);
815 return 1;
816}
817
818/*************************************************************************
819 * SHFileOperation [SHELL32.@]
820 *
821 */
822DWORD WINAPI SHFileOperationAW(LPVOID lpFileOp)
823{
824 if (SHELL_OsIsUnicode())
825 return SHFileOperationW(lpFileOp);
826 return SHFileOperationA(lpFileOp);
827}
828
829/*************************************************************************
830 * SheGetDirW [SHELL32.281]
831 *
832 */
833HRESULT WINAPI SheGetDirW(LPWSTR u, LPWSTR v)
834{ FIXME("%p %p stub\n",u,v);
835 return 0;
836}
837
838/*************************************************************************
839 * SheChangeDirW [SHELL32.274]
840 *
841 */
842HRESULT WINAPI SheChangeDirW(LPWSTR u)
843{ FIXME("(%s),stub\n",debugstr_w(u));
844 return 0;
845}
846
847/*************************************************************************
848 * IsNetDrive [SHELL32.66]
849 */
850BOOL WINAPI IsNetDrive(DWORD drive)
851{
852 char root[4];
853 strcpy(root, "A:\\");
854 root[0] += drive;
855 return (GetDriveTypeA(root) == DRIVE_REMOTE);
856}
857
Note: See TracBrowser for help on using the repository browser.