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