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