- Remove the remaining __USE_W32API, deprecated for ages.
[reactos.git] / reactos / base / shell / cmd / move.c
1 /*
2 * MOVE.C - move internal command.
3 *
4 *
5 * History:
6 *
7 * 14-Dec-1998 (Eric Kohl)
8 * Started.
9 *
10 * 18-Jan-1999 (Eric Kohl)
11 * Unicode safe!
12 * Preliminary version!!!
13 *
14 * 20-Jan-1999 (Eric Kohl)
15 * Redirection safe!
16 *
17 * 27-Jan-1999 (Eric Kohl)
18 * Added help text ("/?").
19 * Added more error checks.
20 *
21 * 03-Feb-1999 (Eric Kohl)
22 * Added "/N" option.
23 *
24 * 30-Apr-2005 (Magnus Olsen) <magnus@greatlord.com>)
25 * Remove all hardcode string to En.rc
26 *
27 * 24-Jun-2005 (Brandon Turner) <turnerb7@msu.edu>)
28 * Fixed bug to allow MS style wildcards + code clean up
29 * added /y and /-y
30 */
31
32 #include <precomp.h>
33
34 #ifdef INCLUDE_CMD_MOVE
35
36 enum
37 {
38 MOVE_NOTHING = 0x001, /* /N */
39 MOVE_OVER_YES = 0x002, /* /Y */
40 MOVE_OVER_NO = 0x004, /* /-Y */
41 };
42
43 enum
44 { /* Move status flags */
45 MOVE_SOURCE_IS_DIR = 0x001,
46 MOVE_SOURCE_IS_FILE = 0x002,
47 MOVE_DEST_IS_DIR = 0x004,
48 MOVE_DEST_IS_FILE = 0x008,
49 MOVE_SOURCE_HAS_WILD = 0x010, /* source has wildcard */
50 MOVE_SRC_CURRENT_IS_DIR = 0x020, /* source is file but at the current round we found a directory */
51 MOVE_DEST_EXISTS = 0x040,
52 MOVE_PATHS_ON_DIF_VOL = 0x080 /* source and destination paths are on different volume */
53 };
54
55 static INT MoveOverwrite (LPTSTR fn)
56 {
57 /*ask the user if they want to override*/
58 INT res;
59 ConOutResPrintf(STRING_MOVE_HELP1, fn);
60 res = FilePromptYNA (0);
61 return res;
62 }
63
64 void GetDirectory (LPTSTR wholepath, LPTSTR directory, BOOL CheckExisting)
65 {
66 /* returns only directory part of path with backslash */
67 /* TODO: make code unc aware */
68 /* Is there a better alternative to this? */
69 LPTSTR last;
70 if (CheckExisting && IsExistingDirectory(wholepath))
71 {
72 _tcscpy(directory, wholepath);
73 }
74 else if ((last = _tcsrchr(wholepath,_T('\\'))) != NULL)
75 {
76 _tcsncpy(directory, wholepath, last - wholepath + 1);
77 directory[last - wholepath + 1] = 0;
78 }
79 else
80 {
81 GetRootPath(wholepath,directory, MAX_PATH);
82 }
83 }
84
85
86 INT cmd_move (LPTSTR param)
87 {
88 LPTSTR *arg;
89 INT argc, i, nFiles;
90 LPTSTR pszDest;
91 TCHAR szDestPath[MAX_PATH];
92 TCHAR szFullDestPath[MAX_PATH];
93 TCHAR szSrcDirPath[MAX_PATH];
94 TCHAR szSrcPath[MAX_PATH];
95 TCHAR szFullSrcPath[MAX_PATH];
96 DWORD dwFlags = 0;
97 INT nOverwrite = 0;
98 WIN32_FIND_DATA findBuffer;
99 HANDLE hFile;
100
101 /* used only when source and destination directories are on different volume*/
102 HANDLE hDestFile;
103 WIN32_FIND_DATA findDestBuffer;
104 TCHAR szMoveDest[MAX_PATH];
105 TCHAR szMoveSrc[MAX_PATH];
106 LPTSTR pszDestDirPointer;
107 LPTSTR pszSrcDirPointer;
108 INT nDirLevel = 0;
109
110 LPTSTR pszFile;
111 BOOL OnlyOneFile;
112 BOOL FoundFile;
113 BOOL MoveStatus;
114 DWORD dwMoveFlags = 0;
115 DWORD dwMoveStatusFlags = 0;
116
117
118 if (!_tcsncmp (param, _T("/?"), 2))
119 {
120 #if 0
121 ConOutPuts (_T("Moves files and renames files and directories.\n\n"
122 "To move one or more files:\n"
123 "MOVE [/N][/Y|/-Y][drive:][path]filename1[,...] destination\n"
124 "\n"
125 "To rename a directory:\n"
126 "MOVE [/N][/Y|/-Y][drive:][path]dirname1 dirname2\n"
127 "\n"
128 " [drive:][path]filename1 Specifies the location and name of the file\n"
129 " or files you want to move.\n"
130 " /N Nothing. Don everthing but move files or direcories.\n"
131 " /Y\n"
132 " /-Y\n"
133 "..."));
134 #else
135 ConOutResPaging(TRUE,STRING_MOVE_HELP2);
136 #endif
137 return 0;
138 }
139
140 nErrorLevel = 0;
141 arg = splitspace(param, &argc);
142
143 /* read options */
144 for (i = 0; i < argc; i++)
145 {
146 if (!_tcsicmp(arg[i], _T("/N")))
147 dwFlags |= MOVE_NOTHING;
148 else if (!_tcsicmp(arg[i], _T("/Y")))
149 dwFlags |= MOVE_OVER_YES;
150 else if (!_tcsicmp(arg[i], _T("/-Y")))
151 dwFlags |= MOVE_OVER_NO;
152 else
153 break;
154 }
155 nFiles = argc - i;
156
157 if (nFiles < 1)
158 {
159 /* there must be at least one pathspec */
160 error_req_param_missing();
161 freep(arg);
162 return 1;
163 }
164
165 if (nFiles > 2)
166 {
167 /* there are more than two pathspecs */
168 error_too_many_parameters(param);
169 freep(arg);
170 return 1;
171 }
172
173 /* If no destination is given, default to current directory */
174 pszDest = (nFiles == 1) ? _T(".") : arg[i + 1];
175
176 /* check for wildcards in source and destination */
177 if (_tcschr(pszDest, _T('*')) != NULL || _tcschr(pszDest, _T('?')) != NULL)
178 {
179 /* '*'/'?' in dest, this doesnt happen. give folder name instead*/
180 error_invalid_parameter_format(pszDest);
181 freep(arg);
182 return 1;
183 }
184 if (_tcschr(arg[i], _T('*')) != NULL || _tcschr(arg[i], _T('?')) != NULL)
185 {
186 dwMoveStatusFlags |= MOVE_SOURCE_HAS_WILD;
187 }
188
189
190 /* get destination */
191 GetFullPathName (pszDest, MAX_PATH, szDestPath, NULL);
192 TRACE ("Destination: %s\n", debugstr_aw(szDestPath));
193
194 /* get source folder */
195 GetFullPathName(arg[i], MAX_PATH, szSrcDirPath, &pszFile);
196 if (pszFile != NULL)
197 *pszFile = _T('\0');
198 TRACE ("Source Folder: %s\n", debugstr_aw(szSrcDirPath));
199
200 hFile = FindFirstFile (arg[i], &findBuffer);
201 if (hFile == INVALID_HANDLE_VALUE)
202 {
203 ErrorMessage (GetLastError (), arg[i]);
204 freep (arg);
205 return 1;
206
207 }
208
209 /* check for special cases "." and ".." and if found skip them */
210 FoundFile = TRUE;
211 while(FoundFile &&
212 (_tcscmp(findBuffer.cFileName,_T(".")) == 0 ||
213 _tcscmp(findBuffer.cFileName,_T("..")) == 0))
214 FoundFile = FindNextFile (hFile, &findBuffer);
215
216 if (!FoundFile)
217 {
218 /* what? we don't have anything to move? */
219 error_file_not_found();
220 FindClose(hFile);
221 freep(arg);
222 return 1;
223 }
224
225 OnlyOneFile = TRUE;
226 /* check if there can be found files as files have first priority */
227 if (findBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
228 dwMoveStatusFlags |= MOVE_SOURCE_IS_DIR;
229 else
230 dwMoveStatusFlags |= MOVE_SOURCE_IS_FILE;
231 while(OnlyOneFile && FindNextFile(hFile,&findBuffer))
232 {
233 if (!(findBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
234 {
235 ConOutPrintf(_T(""));
236 if (dwMoveStatusFlags & MOVE_SOURCE_IS_FILE) OnlyOneFile = FALSE;
237 else
238 { /* this has been done this way so that we don't disturb other settings if they have been set before this */
239 dwMoveStatusFlags |= MOVE_SOURCE_IS_FILE;
240 dwMoveStatusFlags &= ~MOVE_SOURCE_IS_DIR;
241 }
242 }
243 }
244 FindClose(hFile);
245
246 TRACE ("Do we have only one file: %s\n", OnlyOneFile ? "TRUE" : "FALSE");
247
248 /* we have to start again to be sure we don't miss any files or folders*/
249 hFile = FindFirstFile (arg[i], &findBuffer);
250 if (hFile == INVALID_HANDLE_VALUE)
251 {
252 ErrorMessage (GetLastError (), arg[i]);
253 freep (arg);
254 return 1;
255
256 }
257
258 /* check for special cases "." and ".." and if found skip them */
259 FoundFile = TRUE;
260 while(FoundFile &&
261 (_tcscmp(findBuffer.cFileName,_T(".")) == 0 ||
262 _tcscmp(findBuffer.cFileName,_T("..")) == 0))
263 FoundFile = FindNextFile (hFile, &findBuffer);
264
265 if (!FoundFile)
266 {
267 /* huh? somebody removed files and/or folders which were there */
268 error_file_not_found();
269 FindClose(hFile);
270 freep(arg);
271 return 1;
272 }
273
274 /* check if source and destination paths are on different volumes */
275 if (szSrcDirPath[0] != szDestPath[0])
276 dwMoveStatusFlags |= MOVE_PATHS_ON_DIF_VOL;
277
278 /* move it */
279 do
280 {
281 TRACE ("Found file/directory: %s\n", debugstr_aw(findBuffer.cFileName));
282 nOverwrite = 1;
283 dwMoveFlags = 0;
284 dwMoveStatusFlags &= ~MOVE_DEST_IS_FILE &
285 ~MOVE_DEST_IS_DIR &
286 ~MOVE_SRC_CURRENT_IS_DIR &
287 ~MOVE_DEST_EXISTS;
288 _tcscpy(szFullSrcPath,szSrcDirPath);
289 if(szFullSrcPath[_tcslen(szFullSrcPath) - 1] != _T('\\'))
290 _tcscat (szFullSrcPath, _T("\\"));
291 _tcscat(szFullSrcPath,findBuffer.cFileName);
292 _tcscpy(szSrcPath, szFullSrcPath);
293
294 if (IsExistingDirectory(szSrcPath))
295 {
296 /* source is directory */
297
298 if (dwMoveStatusFlags & MOVE_SOURCE_IS_FILE)
299 {
300 dwMoveStatusFlags |= MOVE_SRC_CURRENT_IS_DIR; /* source is file but at the current round we found a directory */
301 continue;
302 }
303 TRACE ("Source is dir: %s\n", debugstr_aw(szSrcPath));
304 dwMoveFlags = MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED;
305 }
306
307 /* if source is file we don't need to do anything special */
308
309 if (IsExistingDirectory(szDestPath))
310 {
311 /* destination is existing directory */
312 TRACE ("Destination is directory: %s\n", debugstr_aw(szDestPath));
313
314 dwMoveStatusFlags |= MOVE_DEST_IS_DIR;
315
316 /*build the dest string(accounts for *)*/
317 _tcscpy (szFullDestPath, szDestPath);
318 /*check to see if there is an ending slash, if not add one*/
319 if(szFullDestPath[_tcslen(szFullDestPath) - 1] != _T('\\'))
320 _tcscat (szFullDestPath, _T("\\"));
321 _tcscat (szFullDestPath, findBuffer.cFileName);
322
323 if (IsExistingFile(szFullDestPath) || IsExistingDirectory(szFullDestPath))
324 dwMoveStatusFlags |= MOVE_DEST_EXISTS;
325
326 dwMoveFlags |= MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED;
327
328 }
329 if (IsExistingFile(szDestPath))
330 {
331 /* destination is a file */
332 TRACE ("Destination is file: %s\n", debugstr_aw(szDestPath));
333
334 dwMoveStatusFlags |= MOVE_DEST_IS_FILE | MOVE_DEST_EXISTS;
335 _tcscpy (szFullDestPath, szDestPath);
336
337 dwMoveFlags |= MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED;
338
339 }
340
341 TRACE ("Move Status Flags: 0x%X\n",dwMoveStatusFlags);
342
343 if (dwMoveStatusFlags & MOVE_SOURCE_IS_DIR &&
344 dwMoveStatusFlags & MOVE_DEST_IS_DIR &&
345 dwMoveStatusFlags & MOVE_SOURCE_HAS_WILD)
346 {
347 /* We are not allowed to have existing source and destination dir when there is wildcard in source */
348 error_syntax(NULL);
349 FindClose(hFile);
350 freep(arg);
351 return 1;
352 }
353
354 if (!(dwMoveStatusFlags & (MOVE_DEST_IS_FILE | MOVE_DEST_IS_DIR)))
355 {
356 /* destination doesn't exist */
357 _tcscpy (szFullDestPath, szDestPath);
358 if (dwMoveStatusFlags & MOVE_SOURCE_IS_FILE) dwMoveStatusFlags |= MOVE_DEST_IS_FILE;
359 if (dwMoveStatusFlags & MOVE_SOURCE_IS_DIR) dwMoveStatusFlags |= MOVE_DEST_IS_DIR;
360
361 dwMoveFlags |= MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED;
362 }
363
364 if (dwMoveStatusFlags & MOVE_SOURCE_IS_FILE &&
365 dwMoveStatusFlags & MOVE_DEST_IS_FILE &&
366 !OnlyOneFile)
367 {
368 /*source has many files but there is only one destination file*/
369 error_invalid_parameter_format(pszDest);
370 FindClose(hFile);
371 freep (arg);
372 return 1;
373 }
374
375 /*checks to make sure user wanted/wants the override*/
376 if((dwFlags & MOVE_OVER_NO) &&
377 (dwMoveStatusFlags & MOVE_DEST_EXISTS))
378 continue;
379 if(!(dwFlags & MOVE_OVER_YES) &&
380 (dwMoveStatusFlags & MOVE_DEST_EXISTS))
381 nOverwrite = MoveOverwrite (szFullDestPath);
382 if (nOverwrite == PROMPT_NO || nOverwrite == PROMPT_BREAK)
383 continue;
384 if (nOverwrite == PROMPT_ALL)
385 dwFlags |= MOVE_OVER_YES;
386
387
388 ConOutPrintf (_T("%s => %s "), szSrcPath, szFullDestPath);
389
390 /* are we really supposed to do something */
391 if (dwFlags & MOVE_NOTHING)
392 continue;
393
394 /*move the file*/
395 if (!(dwMoveStatusFlags & MOVE_SOURCE_IS_DIR &&
396 dwMoveStatusFlags & MOVE_PATHS_ON_DIF_VOL))
397 /* we aren't moving source folder to different drive */
398 MoveStatus = MoveFileEx (szSrcPath, szFullDestPath, dwMoveFlags);
399 else
400 { /* we are moving source folder to different drive */
401 _tcscpy(szMoveDest, szFullDestPath);
402 _tcscpy(szMoveSrc, szSrcPath);
403 DeleteFile(szMoveDest);
404 MoveStatus = CreateDirectory(szMoveDest, NULL); /* we use default security settings */
405 if (MoveStatus)
406 {
407 _tcscat(szMoveDest,_T("\\"));
408 _tcscat(szMoveSrc,_T("\\"));
409 nDirLevel = 0;
410 pszDestDirPointer = szMoveDest + _tcslen(szMoveDest);
411 pszSrcDirPointer = szMoveSrc + _tcslen(szMoveSrc);
412 _tcscpy(pszSrcDirPointer,_T("*.*"));
413 hDestFile = FindFirstFile(szMoveSrc, &findDestBuffer);
414 if (hDestFile == INVALID_HANDLE_VALUE)
415 MoveStatus = FALSE;
416 else
417 {
418 BOOL FirstTime = TRUE;
419 FoundFile = TRUE;
420 MoveStatus = FALSE;
421 while(FoundFile)
422 {
423 if (FirstTime)
424 FirstTime = FALSE;
425 else
426 FoundFile = FindNextFile (hDestFile, &findDestBuffer);
427
428 if (!FoundFile)
429 { /* Nothing to do in this folder so we stop working on it */
430 FindClose(hDestFile);
431 (pszSrcDirPointer)--;
432 (pszDestDirPointer)--;
433 _tcscpy(pszSrcDirPointer,_T(""));
434 _tcscpy(pszDestDirPointer,_T(""));
435 if (nDirLevel > 0)
436 {
437 TCHAR szTempPath[MAX_PATH];
438 INT nDiff;
439
440 FoundFile = TRUE; /* we need to continue our seek for files */
441 nDirLevel--;
442 RemoveDirectory(szMoveSrc);
443 GetDirectory(szMoveSrc,szTempPath,0);
444 nDiff = _tcslen(szMoveSrc) - _tcslen(szTempPath);
445 pszSrcDirPointer = pszSrcDirPointer - nDiff;
446 _tcscpy(pszSrcDirPointer,_T(""));
447 GetDirectory(szMoveDest,szTempPath,0);
448 nDiff = _tcslen(szMoveDest) - _tcslen(szTempPath);
449 pszDestDirPointer = pszDestDirPointer - nDiff;
450 _tcscpy(pszDestDirPointer,_T(""));
451 if(szMoveSrc[_tcslen(szMoveSrc) - 1] != _T('\\'))
452 _tcscat (szMoveSrc, _T("\\"));
453 if(szMoveDest[_tcslen(szMoveDest) - 1] != _T('\\'))
454 _tcscat (szMoveDest, _T("\\"));
455 pszDestDirPointer = szMoveDest + _tcslen(szMoveDest);
456 pszSrcDirPointer = szMoveSrc + _tcslen(szMoveSrc);
457 _tcscpy(pszSrcDirPointer,_T("*.*"));
458 hDestFile = FindFirstFile(szMoveSrc, &findDestBuffer);
459 if (hDestFile == INVALID_HANDLE_VALUE)
460 continue;
461 FirstTime = TRUE;
462 }
463 else
464 {
465 MoveStatus = TRUE; /* we moved everything so lets tell user about it */
466 RemoveDirectory(szMoveSrc);
467 }
468 continue;
469 }
470
471 /* if we find "." or ".." we'll skip them */
472 if (_tcscmp(findDestBuffer.cFileName,_T(".")) == 0 ||
473 _tcscmp(findDestBuffer.cFileName,_T("..")) == 0)
474 continue;
475
476 _tcscpy(pszSrcDirPointer, findDestBuffer.cFileName);
477 _tcscpy(pszDestDirPointer, findDestBuffer.cFileName);
478 if (IsExistingFile(szMoveSrc))
479 {
480 FoundFile = CopyFile(szMoveSrc, szMoveDest, FALSE);
481 if (!FoundFile) continue;
482 DeleteFile(szMoveSrc);
483 }
484 else
485 {
486 FindClose(hDestFile);
487 CreateDirectory(szMoveDest, NULL);
488 _tcscat(szMoveDest,_T("\\"));
489 _tcscat(szMoveSrc,_T("\\"));
490 nDirLevel++;
491 pszDestDirPointer = szMoveDest + _tcslen(szMoveDest);
492 pszSrcDirPointer = szMoveSrc + _tcslen(szMoveSrc);
493 _tcscpy(pszSrcDirPointer,_T("*.*"));
494 hDestFile = FindFirstFile(szMoveSrc, &findDestBuffer);
495 if (hDestFile == INVALID_HANDLE_VALUE)
496 {
497 FoundFile = FALSE;
498 continue;
499 }
500 FirstTime = TRUE;
501 }
502 }
503 }
504 }
505 }
506 if (MoveStatus)
507 ConOutResPrintf(STRING_MOVE_ERROR1);
508 else
509 ConOutResPrintf(STRING_MOVE_ERROR2);
510 }
511 while ((!OnlyOneFile || dwMoveStatusFlags & MOVE_SRC_CURRENT_IS_DIR ) &&
512 !(dwMoveStatusFlags & MOVE_SOURCE_IS_DIR) &&
513 FindNextFile (hFile, &findBuffer));
514 FindClose (hFile);
515
516 freep (arg);
517 return 0;
518 }
519
520 #endif /* INCLUDE_CMD_MOVE */