Made cmd unicode compatible.
[reactos.git] / reactos / subsys / system / 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 Overwrite (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 arg = split (param, &argc, FALSE);
144 nFiles = argc;
145
146 /* read options */
147 for (i = 0; i < argc; i++)
148 {
149 if (*arg[i] == _T('/'))
150 {
151 if (_tcslen(arg[i]) >= 2)
152 {
153 switch (_totupper(arg[i][1]))
154 {
155 case _T('N'):
156 dwFlags |= MOVE_NOTHING;
157 break;
158
159 case _T('Y'):
160 dwFlags |= MOVE_OVER_YES;
161 break;
162
163 case _T('-'):
164 dwFlags |= MOVE_OVER_NO;
165 break;
166 }
167 }
168 nFiles--;
169 }
170 }
171
172 if (nFiles < 2)
173 {
174 /* there must be at least two pathspecs */
175 error_req_param_missing ();
176 return 1;
177 }
178
179 /* check for wildcards in source and destination */
180 if (_tcschr (arg[argc - 1], _T('*')) != NULL || _tcschr (arg[argc - 1], _T('?')) != NULL)
181 {
182 /* '*'/'?' in dest, this doesnt happen. give folder name instead*/
183 error_invalid_parameter_format(arg[argc - 1]);
184 return 1;
185 }
186 if (_tcschr (arg[argc - 2], _T('*')) != NULL || _tcschr (arg[argc - 2], _T('?')) != NULL)
187 {
188 dwMoveStatusFlags |= MOVE_SOURCE_HAS_WILD;
189 }
190
191
192 /* get destination */
193 GetFullPathName (arg[argc - 1], MAX_PATH, szDestPath, NULL);
194 #ifdef _DEBUG
195 DebugPrintf (_T("Destination: %s\n"), szDestPath);
196 #endif
197
198 /* get source folder */
199 GetDirectory(arg[argc - 2], szSrcDirPath, 1);
200 GetFullPathName(szSrcDirPath, MAX_PATH, szSrcPath, &pszFile);
201 _tcscpy(szSrcDirPath,szSrcPath);
202 /* we need following check to see if source happens to be directly given directory
203 and if it is then rip off last directory part so that there won't be any clashes with codes after this point */
204 GetFullPathName(arg[argc - 2], MAX_PATH, szSrcPath, &pszFile);
205 if (_tcscmp(szSrcDirPath,szSrcPath) == 0)
206 szSrcDirPath[pszFile - szSrcPath] = _T('\0');
207 #ifdef _DEBUG
208 DebugPrintf (_T("Source Folder: %s\n"), szSrcDirPath);
209 #endif
210
211 hFile = FindFirstFile (arg[argc - 2], &findBuffer);
212 if (hFile == INVALID_HANDLE_VALUE)
213 {
214 ErrorMessage (GetLastError (), arg[argc - 2]);
215 freep (arg);
216 return 1;
217
218 }
219
220 /* check for special cases "." and ".." and if found skip them */
221 FoundFile = TRUE;
222 while(FoundFile &&
223 (_tcscmp(findBuffer.cFileName,_T(".")) == 0 ||
224 _tcscmp(findBuffer.cFileName,_T("..")) == 0))
225 FoundFile = FindNextFile (hFile, &findBuffer);
226
227 if (!FoundFile)
228 {
229 /* what? we don't have anything to move? */
230 error_file_not_found();
231 FindClose(hFile);
232 freep(arg);
233 return 1;
234 }
235
236 OnlyOneFile = TRUE;
237 _tcscpy(szSrcPath,szSrcDirPath);
238 /*check to see if there is an ending slash, if not add one*/
239 if(szSrcPath[_tcslen(szSrcPath) - 1] != _T('\\'))
240 _tcscat (szSrcPath, _T("\\"));
241 _tcscat(szSrcPath,findBuffer.cFileName);
242 #ifdef _DEBUG
243 DebugPrintf (_T("Source Path: %s\n"), szSrcPath);
244 #endif
245 /* check if there can be found files as files have first priority */
246 if (IsExistingFile(szSrcPath)) dwMoveStatusFlags |= MOVE_SOURCE_IS_FILE;
247 else dwMoveStatusFlags |= MOVE_SOURCE_IS_DIR;
248 while(OnlyOneFile && FindNextFile(hFile,&findBuffer))
249 {
250 _tcscpy(szSrcPath,szSrcDirPath);
251 if(szSrcPath[_tcslen(szSrcPath) - 1] != _T('\\'))
252 _tcscat (szSrcPath, _T("\\"));
253 _tcscat(szSrcPath,findBuffer.cFileName);
254 if (IsExistingFile(szSrcPath))
255 {
256 ConOutPrintf(_T(""));
257 if (dwMoveStatusFlags & MOVE_SOURCE_IS_FILE) OnlyOneFile = FALSE;
258 else
259 { /* this has been done this way so that we don't disturb other settings if they have been set before this */
260 dwMoveStatusFlags |= MOVE_SOURCE_IS_FILE;
261 dwMoveStatusFlags &= ~MOVE_SOURCE_IS_DIR;
262 }
263 }
264 }
265 FindClose(hFile);
266
267 #ifdef _DEBUG
268 DebugPrintf(_T("Do we have only one file: %s\n"), OnlyOneFile ? _T("TRUE") : _T("FALSE"));
269 #endif
270
271 /* we have to start again to be sure we don't miss any files or folders*/
272 hFile = FindFirstFile (arg[argc - 2], &findBuffer);
273 if (hFile == INVALID_HANDLE_VALUE)
274 {
275 ErrorMessage (GetLastError (), arg[argc - 2]);
276 freep (arg);
277 return 1;
278
279 }
280
281 /* check for special cases "." and ".." and if found skip them */
282 FoundFile = TRUE;
283 while(FoundFile &&
284 (_tcscmp(findBuffer.cFileName,_T(".")) == 0 ||
285 _tcscmp(findBuffer.cFileName,_T("..")) == 0))
286 FoundFile = FindNextFile (hFile, &findBuffer);
287
288 if (!FoundFile)
289 {
290 /* huh? somebody removed files and/or folders which were there */
291 error_file_not_found();
292 FindClose(hFile);
293 freep(arg);
294 return 1;
295 }
296
297 /* check if source and destination paths are on different volumes */
298 if (szSrcPath[0] != szDestPath[0])
299 dwMoveStatusFlags |= MOVE_PATHS_ON_DIF_VOL;
300
301 /* move it */
302 do
303 {
304 #ifdef _DEBUG
305 DebugPrintf (_T("Found file/directory: %s\n"), findBuffer.cFileName);
306 #endif
307 nOverwrite = 1;
308 dwMoveFlags = 0;
309 dwMoveStatusFlags &= ~MOVE_DEST_IS_FILE &
310 ~MOVE_DEST_IS_DIR &
311 ~MOVE_SRC_CURRENT_IS_DIR &
312 ~MOVE_DEST_EXISTS;
313 _tcscpy(szFullSrcPath,szSrcDirPath);
314 if(szFullSrcPath[_tcslen(szFullSrcPath) - 1] != _T('\\'))
315 _tcscat (szFullSrcPath, _T("\\"));
316 _tcscat(szFullSrcPath,findBuffer.cFileName);
317 _tcscpy(szSrcPath, szFullSrcPath);
318
319 if (IsExistingDirectory(szSrcPath))
320 {
321 /* source is directory */
322
323 if (dwMoveStatusFlags & MOVE_SOURCE_IS_FILE)
324 {
325 dwMoveStatusFlags |= MOVE_SRC_CURRENT_IS_DIR; /* source is file but at the current round we found a directory */
326 continue;
327 }
328 #ifdef _DEBUG
329 DebugPrintf (_T("Source is dir: %s\n"), szSrcPath);
330 #endif
331 dwMoveFlags = MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED;
332 }
333
334 /* if source is file we don't need to do anything special */
335
336 if (IsExistingDirectory(szDestPath))
337 {
338 /* destination is existing directory */
339 #ifdef _DEBUG
340 DebugPrintf (_T("Destination is directory: %s\n"), szDestPath);
341 #endif
342
343 dwMoveStatusFlags |= MOVE_DEST_IS_DIR;
344
345 /*build the dest string(accounts for *)*/
346 _tcscpy (szFullDestPath, szDestPath);
347 /*check to see if there is an ending slash, if not add one*/
348 if(szFullDestPath[_tcslen(szFullDestPath) - 1] != _T('\\'))
349 _tcscat (szFullDestPath, _T("\\"));
350 _tcscat (szFullDestPath, findBuffer.cFileName);
351
352 if (IsExistingFile(szFullDestPath) || IsExistingDirectory(szFullDestPath))
353 dwMoveStatusFlags |= MOVE_DEST_EXISTS;
354
355 dwMoveFlags |= MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED;
356
357 }
358 if (IsExistingFile(szDestPath))
359 {
360 /* destination is a file */
361 #ifdef _DEBUG
362 DebugPrintf (_T("Destination is file: %s\n"), szDestPath);
363 #endif
364
365 dwMoveStatusFlags |= MOVE_DEST_IS_FILE | MOVE_DEST_EXISTS;
366 _tcscpy (szFullDestPath, szDestPath);
367
368 dwMoveFlags |= MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED;
369
370 }
371
372 #ifdef _DEBUG
373 DebugPrintf(_T("Move Status Flags: 0x%X\n"),dwMoveStatusFlags);
374 #endif
375
376 if (dwMoveStatusFlags & MOVE_SOURCE_IS_DIR &&
377 dwMoveStatusFlags & MOVE_DEST_IS_DIR &&
378 dwMoveStatusFlags & MOVE_SOURCE_HAS_WILD)
379 {
380 /* We are not allowed to have existing source and destination dir when there is wildcard in source */
381 error_syntax(NULL);
382 FindClose(hFile);
383 freep(arg);
384 return 1;
385 }
386
387 if (!(dwMoveStatusFlags & (MOVE_DEST_IS_FILE | MOVE_DEST_IS_DIR)))
388 {
389 /* destination doesn't exist */
390 _tcscpy (szFullDestPath, szDestPath);
391 if (dwMoveStatusFlags & MOVE_SOURCE_IS_FILE) dwMoveStatusFlags |= MOVE_DEST_IS_FILE;
392 if (dwMoveStatusFlags & MOVE_SOURCE_IS_DIR) dwMoveStatusFlags |= MOVE_DEST_IS_DIR;
393
394 dwMoveFlags |= MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED;
395 }
396
397 if (dwMoveStatusFlags & MOVE_SOURCE_IS_FILE &&
398 dwMoveStatusFlags & MOVE_DEST_IS_FILE &&
399 !OnlyOneFile)
400 {
401 /*source has many files but there is only one destination file*/
402 error_invalid_parameter_format(arg[argc - 1]);
403 FindClose(hFile);
404 freep (arg);
405 return 1;
406 }
407
408 /*checks to make sure user wanted/wants the override*/
409 if((dwFlags & MOVE_OVER_NO) &&
410 (dwMoveStatusFlags & MOVE_DEST_EXISTS))
411 continue;
412 if(!(dwFlags & MOVE_OVER_YES) &&
413 (dwMoveStatusFlags & MOVE_DEST_EXISTS))
414 nOverwrite = Overwrite (szFullDestPath);
415 if (nOverwrite == PROMPT_NO || nOverwrite == PROMPT_BREAK)
416 continue;
417 if (nOverwrite == PROMPT_ALL)
418 dwFlags |= MOVE_OVER_YES;
419
420
421 ConOutPrintf (_T("%s => %s "), szSrcPath, szFullDestPath);
422
423 /* are we really supposed to do something */
424 if (dwFlags & MOVE_NOTHING)
425 continue;
426
427 /*move the file*/
428 if (!(dwMoveStatusFlags & MOVE_SOURCE_IS_DIR &&
429 dwMoveStatusFlags & MOVE_PATHS_ON_DIF_VOL))
430 /* we aren't moving source folder to different drive */
431 MoveStatus = MoveFileEx (szSrcPath, szFullDestPath, dwMoveFlags);
432 else
433 { /* we are moving source folder to different drive */
434 _tcscpy(szMoveDest, szFullDestPath);
435 _tcscpy(szMoveSrc, szSrcPath);
436 DeleteFile(szMoveDest);
437 MoveStatus = CreateDirectory(szMoveDest, NULL); /* we use default security settings */
438 if (MoveStatus)
439 {
440 _tcscat(szMoveDest,_T("\\"));
441 _tcscat(szMoveSrc,_T("\\"));
442 nDirLevel = 0;
443 pszDestDirPointer = szMoveDest + _tcslen(szMoveDest);
444 pszSrcDirPointer = szMoveSrc + _tcslen(szMoveSrc);
445 _tcscpy(pszSrcDirPointer,_T("*.*"));
446 hDestFile = FindFirstFile(szMoveSrc, &findDestBuffer);
447 if (hDestFile == INVALID_HANDLE_VALUE)
448 MoveStatus = FALSE;
449 else
450 {
451 BOOL FirstTime = TRUE;
452 FoundFile = TRUE;
453 MoveStatus = FALSE;
454 while(FoundFile)
455 {
456 if (FirstTime)
457 FirstTime = FALSE;
458 else
459 FoundFile = FindNextFile (hDestFile, &findDestBuffer);
460
461 if (!FoundFile)
462 { /* Nothing to do in this folder so we stop working on it */
463 FindClose(hDestFile);
464 (pszSrcDirPointer)--;
465 (pszDestDirPointer)--;
466 _tcscpy(pszSrcDirPointer,_T(""));
467 _tcscpy(pszDestDirPointer,_T(""));
468 if (nDirLevel > 0)
469 {
470 TCHAR szTempPath[MAX_PATH];
471 INT nDiff;
472
473 FoundFile = TRUE; /* we need to continue our seek for files */
474 nDirLevel--;
475 RemoveDirectory(szMoveSrc);
476 GetDirectory(szMoveSrc,szTempPath,0);
477 nDiff = _tcslen(szMoveSrc) - _tcslen(szTempPath);
478 pszSrcDirPointer = pszSrcDirPointer - nDiff;
479 _tcscpy(pszSrcDirPointer,_T(""));
480 GetDirectory(szMoveDest,szTempPath,0);
481 nDiff = _tcslen(szMoveDest) - _tcslen(szTempPath);
482 pszDestDirPointer = pszDestDirPointer - nDiff;
483 _tcscpy(pszDestDirPointer,_T(""));
484 if(szMoveSrc[_tcslen(szMoveSrc) - 1] != _T('\\'))
485 _tcscat (szMoveSrc, _T("\\"));
486 if(szMoveDest[_tcslen(szMoveDest) - 1] != _T('\\'))
487 _tcscat (szMoveDest, _T("\\"));
488 pszDestDirPointer = szMoveDest + _tcslen(szMoveDest);
489 pszSrcDirPointer = szMoveSrc + _tcslen(szMoveSrc);
490 _tcscpy(pszSrcDirPointer,_T("*.*"));
491 hDestFile = FindFirstFile(szMoveSrc, &findDestBuffer);
492 if (hDestFile == INVALID_HANDLE_VALUE)
493 continue;
494 FirstTime = TRUE;
495 }
496 else
497 {
498 MoveStatus = TRUE; /* we moved everything so lets tell user about it */
499 RemoveDirectory(szMoveSrc);
500 }
501 continue;
502 }
503
504 /* if we find "." or ".." we'll skip them */
505 if (_tcscmp(findDestBuffer.cFileName,_T(".")) == 0 ||
506 _tcscmp(findDestBuffer.cFileName,_T("..")) == 0)
507 continue;
508
509 _tcscpy(pszSrcDirPointer, findDestBuffer.cFileName);
510 _tcscpy(pszDestDirPointer, findDestBuffer.cFileName);
511 if (IsExistingFile(szMoveSrc))
512 {
513 FoundFile = CopyFile(szMoveSrc, szMoveDest, FALSE);
514 if (!FoundFile) continue;
515 DeleteFile(szMoveSrc);
516 }
517 else
518 {
519 FindClose(hDestFile);
520 CreateDirectory(szMoveDest, NULL);
521 _tcscat(szMoveDest,_T("\\"));
522 _tcscat(szMoveSrc,_T("\\"));
523 nDirLevel++;
524 pszDestDirPointer = szMoveDest + _tcslen(szMoveDest);
525 pszSrcDirPointer = szMoveSrc + _tcslen(szMoveSrc);
526 _tcscpy(pszSrcDirPointer,_T("*.*"));
527 hDestFile = FindFirstFile(szMoveSrc, &findDestBuffer);
528 if (hDestFile == INVALID_HANDLE_VALUE)
529 {
530 FoundFile = FALSE;
531 continue;
532 }
533 FirstTime = TRUE;
534 }
535 }
536 }
537 }
538 }
539 if (MoveStatus)
540 LoadString(CMD_ModuleHandle, STRING_MOVE_ERROR1, szMsg, RC_STRING_MAX_SIZE);
541 else
542 LoadString(CMD_ModuleHandle, STRING_MOVE_ERROR2, szMsg, RC_STRING_MAX_SIZE);
543
544 ConOutPrintf(szMsg);
545 }
546 while ((!OnlyOneFile || dwMoveStatusFlags & MOVE_SRC_CURRENT_IS_DIR ) &&
547 !(dwMoveStatusFlags & MOVE_SOURCE_IS_DIR) &&
548 FindNextFile (hFile, &findBuffer));
549 FindClose (hFile);
550
551 freep (arg);
552 return 0;
553 }
554
555 #endif /* INCLUDE_CMD_MOVE */