mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 10:04:49 +00:00
3b960a1c21
CORE-14261
522 lines
18 KiB
C
522 lines
18 KiB
C
/*
|
|
* MOVE.C - move internal command.
|
|
*
|
|
*
|
|
* History:
|
|
*
|
|
* 14-Dec-1998 (Eric Kohl)
|
|
* Started.
|
|
*
|
|
* 18-Jan-1999 (Eric Kohl)
|
|
* Unicode safe!
|
|
* Preliminary version!!!
|
|
*
|
|
* 20-Jan-1999 (Eric Kohl)
|
|
* Redirection safe!
|
|
*
|
|
* 27-Jan-1999 (Eric Kohl)
|
|
* Added help text ("/?").
|
|
* Added more error checks.
|
|
*
|
|
* 03-Feb-1999 (Eric Kohl)
|
|
* Added "/N" option.
|
|
*
|
|
* 30-Apr-2005 (Magnus Olsen <magnus@greatlord.com>)
|
|
* Remove all hardcoded strings in En.rc
|
|
*
|
|
* 24-Jun-2005 (Brandon Turner <turnerb7@msu.edu>)
|
|
* Fixed bug to allow MS style wildcards + code clean up
|
|
* added /y and /-y
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
#ifdef INCLUDE_CMD_MOVE
|
|
|
|
enum
|
|
{
|
|
MOVE_NOTHING = 0x001, /* /N */
|
|
MOVE_OVER_YES = 0x002, /* /Y */
|
|
MOVE_OVER_NO = 0x004, /* /-Y */
|
|
};
|
|
|
|
enum
|
|
{
|
|
/* Move status flags */
|
|
MOVE_SOURCE_IS_DIR = 0x001,
|
|
MOVE_SOURCE_IS_FILE = 0x002,
|
|
MOVE_DEST_IS_DIR = 0x004,
|
|
MOVE_DEST_IS_FILE = 0x008,
|
|
MOVE_SOURCE_HAS_WILD = 0x010, /* source has wildcard */
|
|
MOVE_SRC_CURRENT_IS_DIR = 0x020, /* source is file but at the current round we found a directory */
|
|
MOVE_DEST_EXISTS = 0x040,
|
|
MOVE_PATHS_ON_DIF_VOL = 0x080 /* source and destination paths are on different volume */
|
|
};
|
|
|
|
static INT MoveOverwrite (LPTSTR fn)
|
|
{
|
|
/* ask the user if they want to override */
|
|
INT res;
|
|
ConOutResPrintf(STRING_MOVE_HELP1, fn);
|
|
res = FilePromptYNA (0);
|
|
return res;
|
|
}
|
|
|
|
void GetDirectory (LPTSTR wholepath, LPTSTR directory, BOOL CheckExisting)
|
|
{
|
|
/* returns only directory part of path with backslash */
|
|
/* TODO: make code unc aware */
|
|
/* Is there a better alternative to this? */
|
|
LPTSTR last;
|
|
if (CheckExisting && IsExistingDirectory(wholepath))
|
|
{
|
|
_tcscpy(directory, wholepath);
|
|
}
|
|
else if ((last = _tcsrchr(wholepath,_T('\\'))) != NULL)
|
|
{
|
|
_tcsncpy(directory, wholepath, last - wholepath + 1);
|
|
directory[last - wholepath + 1] = 0;
|
|
}
|
|
else
|
|
{
|
|
GetRootPath(wholepath,directory, MAX_PATH);
|
|
}
|
|
}
|
|
|
|
|
|
INT cmd_move (LPTSTR param)
|
|
{
|
|
LPTSTR *arg;
|
|
INT argc, i, nFiles;
|
|
LPTSTR pszDest;
|
|
TCHAR szDestPath[MAX_PATH];
|
|
TCHAR szFullDestPath[MAX_PATH];
|
|
TCHAR szSrcDirPath[MAX_PATH];
|
|
TCHAR szSrcPath[MAX_PATH];
|
|
TCHAR szFullSrcPath[MAX_PATH];
|
|
DWORD dwFlags = 0;
|
|
INT nOverwrite = 0;
|
|
WIN32_FIND_DATA findBuffer;
|
|
HANDLE hFile;
|
|
|
|
/* used only when source and destination directories are on different volume */
|
|
HANDLE hDestFile = NULL;
|
|
WIN32_FIND_DATA findDestBuffer;
|
|
TCHAR szMoveDest[MAX_PATH];
|
|
TCHAR szMoveSrc[MAX_PATH];
|
|
LPTSTR pszDestDirPointer;
|
|
LPTSTR pszSrcDirPointer;
|
|
INT nDirLevel = 0;
|
|
|
|
LPTSTR pszFile;
|
|
BOOL OnlyOneFile;
|
|
BOOL FoundFile;
|
|
BOOL MoveStatus;
|
|
DWORD dwMoveFlags = 0;
|
|
DWORD dwMoveStatusFlags = 0;
|
|
|
|
if (!_tcsncmp (param, _T("/?"), 2))
|
|
{
|
|
#if 0
|
|
ConOutPuts (_T("Moves files and renames files and directories.\n\n"
|
|
"To move one or more files:\n"
|
|
"MOVE [/N][/Y|/-Y][drive:][path]filename1[,...] destination\n"
|
|
"\n"
|
|
"To rename a directory:\n"
|
|
"MOVE [/N][/Y|/-Y][drive:][path]dirname1 dirname2\n"
|
|
"\n"
|
|
" [drive:][path]filename1 Specifies the location and name of the file\n"
|
|
" or files you want to move.\n"
|
|
" /N Nothing. Don everthing but move files or directories.\n"
|
|
" /Y\n"
|
|
" /-Y\n"
|
|
"..."));
|
|
#else
|
|
ConOutResPaging(TRUE,STRING_MOVE_HELP2);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
nErrorLevel = 0;
|
|
arg = splitspace(param, &argc);
|
|
|
|
/* read options */
|
|
for (i = 0; i < argc; i++)
|
|
{
|
|
if (!_tcsicmp(arg[i], _T("/N")))
|
|
dwFlags |= MOVE_NOTHING;
|
|
else if (!_tcsicmp(arg[i], _T("/Y")))
|
|
dwFlags |= MOVE_OVER_YES;
|
|
else if (!_tcsicmp(arg[i], _T("/-Y")))
|
|
dwFlags |= MOVE_OVER_NO;
|
|
else
|
|
break;
|
|
}
|
|
nFiles = argc - i;
|
|
|
|
if (nFiles < 1)
|
|
{
|
|
/* there must be at least one pathspec */
|
|
error_req_param_missing();
|
|
nErrorLevel = 1;
|
|
goto Quit;
|
|
}
|
|
|
|
if (nFiles > 2)
|
|
{
|
|
/* there are more than two pathspecs */
|
|
error_too_many_parameters(param);
|
|
nErrorLevel = 1;
|
|
goto Quit;
|
|
}
|
|
|
|
/* If no destination is given, default to current directory */
|
|
pszDest = (nFiles == 1) ? _T(".") : arg[i + 1];
|
|
|
|
/* check for wildcards in source and destination */
|
|
if (_tcschr(pszDest, _T('*')) != NULL || _tcschr(pszDest, _T('?')) != NULL)
|
|
{
|
|
/* '*'/'?' in dest, this doesnt happen. give folder name instead*/
|
|
error_invalid_parameter_format(pszDest);
|
|
nErrorLevel = 1;
|
|
goto Quit;
|
|
}
|
|
if (_tcschr(arg[i], _T('*')) != NULL || _tcschr(arg[i], _T('?')) != NULL)
|
|
{
|
|
dwMoveStatusFlags |= MOVE_SOURCE_HAS_WILD;
|
|
}
|
|
|
|
/* get destination */
|
|
GetFullPathName (pszDest, MAX_PATH, szDestPath, NULL);
|
|
TRACE ("Destination: %s\n", debugstr_aw(szDestPath));
|
|
|
|
/* get source folder */
|
|
GetFullPathName(arg[i], MAX_PATH, szSrcDirPath, &pszFile);
|
|
if (pszFile != NULL)
|
|
*pszFile = _T('\0');
|
|
TRACE ("Source Folder: %s\n", debugstr_aw(szSrcDirPath));
|
|
|
|
hFile = FindFirstFile (arg[i], &findBuffer);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
ErrorMessage(GetLastError(), arg[i]);
|
|
nErrorLevel = 1;
|
|
goto Quit;
|
|
}
|
|
|
|
/* check for special cases "." and ".." and if found skip them */
|
|
FoundFile = TRUE;
|
|
while(FoundFile &&
|
|
(_tcscmp(findBuffer.cFileName,_T(".")) == 0 ||
|
|
_tcscmp(findBuffer.cFileName,_T("..")) == 0))
|
|
FoundFile = FindNextFile (hFile, &findBuffer);
|
|
|
|
if (!FoundFile)
|
|
{
|
|
/* what? we don't have anything to move? */
|
|
error_file_not_found();
|
|
FindClose(hFile);
|
|
nErrorLevel = 1;
|
|
goto Quit;
|
|
}
|
|
|
|
OnlyOneFile = TRUE;
|
|
/* check if there can be found files as files have first priority */
|
|
if (findBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
dwMoveStatusFlags |= MOVE_SOURCE_IS_DIR;
|
|
else
|
|
dwMoveStatusFlags |= MOVE_SOURCE_IS_FILE;
|
|
while(OnlyOneFile && FindNextFile(hFile,&findBuffer))
|
|
{
|
|
if (!(findBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
ConOutPrintf(_T(""));
|
|
if (dwMoveStatusFlags & MOVE_SOURCE_IS_FILE) OnlyOneFile = FALSE;
|
|
else
|
|
{ /* this has been done this way so that we don't disturb other settings if they have been set before this */
|
|
dwMoveStatusFlags |= MOVE_SOURCE_IS_FILE;
|
|
dwMoveStatusFlags &= ~MOVE_SOURCE_IS_DIR;
|
|
}
|
|
}
|
|
}
|
|
FindClose(hFile);
|
|
|
|
TRACE ("Do we have only one file: %s\n", OnlyOneFile ? "TRUE" : "FALSE");
|
|
|
|
/* we have to start again to be sure we don't miss any files or folders*/
|
|
hFile = FindFirstFile (arg[i], &findBuffer);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
ErrorMessage(GetLastError(), arg[i]);
|
|
nErrorLevel = 1;
|
|
goto Quit;
|
|
}
|
|
|
|
/* check for special cases "." and ".." and if found skip them */
|
|
FoundFile = TRUE;
|
|
while(FoundFile &&
|
|
(_tcscmp(findBuffer.cFileName,_T(".")) == 0 ||
|
|
_tcscmp(findBuffer.cFileName,_T("..")) == 0))
|
|
FoundFile = FindNextFile (hFile, &findBuffer);
|
|
|
|
if (!FoundFile)
|
|
{
|
|
/* huh? somebody removed files and/or folders which were there */
|
|
error_file_not_found();
|
|
FindClose(hFile);
|
|
nErrorLevel = 1;
|
|
goto Quit;
|
|
}
|
|
|
|
/* check if source and destination paths are on different volumes */
|
|
if (szSrcDirPath[0] != szDestPath[0])
|
|
dwMoveStatusFlags |= MOVE_PATHS_ON_DIF_VOL;
|
|
|
|
/* move it */
|
|
do
|
|
{
|
|
TRACE ("Found file/directory: %s\n", debugstr_aw(findBuffer.cFileName));
|
|
nOverwrite = 1;
|
|
dwMoveFlags = 0;
|
|
dwMoveStatusFlags &= ~MOVE_DEST_IS_FILE &
|
|
~MOVE_DEST_IS_DIR &
|
|
~MOVE_SRC_CURRENT_IS_DIR &
|
|
~MOVE_DEST_EXISTS;
|
|
_tcscpy(szFullSrcPath,szSrcDirPath);
|
|
if (szFullSrcPath[_tcslen(szFullSrcPath) - 1] != _T('\\'))
|
|
_tcscat (szFullSrcPath, _T("\\"));
|
|
_tcscat(szFullSrcPath,findBuffer.cFileName);
|
|
_tcscpy(szSrcPath, szFullSrcPath);
|
|
|
|
if (IsExistingDirectory(szSrcPath))
|
|
{
|
|
/* source is directory */
|
|
if (dwMoveStatusFlags & MOVE_SOURCE_IS_FILE)
|
|
{
|
|
dwMoveStatusFlags |= MOVE_SRC_CURRENT_IS_DIR; /* source is file but at the current round we found a directory */
|
|
continue;
|
|
}
|
|
TRACE ("Source is dir: %s\n", debugstr_aw(szSrcPath));
|
|
dwMoveFlags = MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED;
|
|
}
|
|
|
|
/* if source is file we don't need to do anything special */
|
|
if (IsExistingDirectory(szDestPath))
|
|
{
|
|
/* destination is existing directory */
|
|
TRACE ("Destination is directory: %s\n", debugstr_aw(szDestPath));
|
|
|
|
dwMoveStatusFlags |= MOVE_DEST_IS_DIR;
|
|
|
|
/*build the dest string(accounts for *)*/
|
|
_tcscpy (szFullDestPath, szDestPath);
|
|
/*check to see if there is an ending slash, if not add one*/
|
|
if (szFullDestPath[_tcslen(szFullDestPath) - 1] != _T('\\'))
|
|
_tcscat (szFullDestPath, _T("\\"));
|
|
_tcscat (szFullDestPath, findBuffer.cFileName);
|
|
|
|
if (IsExistingFile(szFullDestPath) || IsExistingDirectory(szFullDestPath))
|
|
dwMoveStatusFlags |= MOVE_DEST_EXISTS;
|
|
|
|
dwMoveFlags |= MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED;
|
|
}
|
|
if (IsExistingFile(szDestPath))
|
|
{
|
|
/* destination is a file */
|
|
TRACE ("Destination is file: %s\n", debugstr_aw(szDestPath));
|
|
|
|
dwMoveStatusFlags |= MOVE_DEST_IS_FILE | MOVE_DEST_EXISTS;
|
|
_tcscpy (szFullDestPath, szDestPath);
|
|
|
|
dwMoveFlags |= MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED;
|
|
}
|
|
|
|
TRACE ("Move Status Flags: 0x%X\n",dwMoveStatusFlags);
|
|
|
|
if (dwMoveStatusFlags & MOVE_SOURCE_IS_DIR &&
|
|
dwMoveStatusFlags & MOVE_DEST_IS_DIR &&
|
|
dwMoveStatusFlags & MOVE_SOURCE_HAS_WILD)
|
|
{
|
|
/* We are not allowed to have existing source and destination dir when there is wildcard in source */
|
|
error_syntax(NULL);
|
|
FindClose(hFile);
|
|
nErrorLevel = 1;
|
|
goto Quit;
|
|
}
|
|
if (!(dwMoveStatusFlags & (MOVE_DEST_IS_FILE | MOVE_DEST_IS_DIR)))
|
|
{
|
|
/* destination doesn't exist */
|
|
_tcscpy (szFullDestPath, szDestPath);
|
|
if (dwMoveStatusFlags & MOVE_SOURCE_IS_FILE) dwMoveStatusFlags |= MOVE_DEST_IS_FILE;
|
|
if (dwMoveStatusFlags & MOVE_SOURCE_IS_DIR) dwMoveStatusFlags |= MOVE_DEST_IS_DIR;
|
|
|
|
dwMoveFlags |= MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED;
|
|
}
|
|
|
|
if (dwMoveStatusFlags & MOVE_SOURCE_IS_FILE &&
|
|
dwMoveStatusFlags & MOVE_DEST_IS_FILE &&
|
|
!OnlyOneFile)
|
|
{
|
|
/*source has many files but there is only one destination file*/
|
|
error_invalid_parameter_format(pszDest);
|
|
FindClose(hFile);
|
|
nErrorLevel = 1;
|
|
goto Quit;
|
|
}
|
|
|
|
/*checks to make sure user wanted/wants the override*/
|
|
if ((dwFlags & MOVE_OVER_NO) &&
|
|
(dwMoveStatusFlags & MOVE_DEST_EXISTS))
|
|
continue;
|
|
if (!(dwFlags & MOVE_OVER_YES) &&
|
|
(dwMoveStatusFlags & MOVE_DEST_EXISTS))
|
|
nOverwrite = MoveOverwrite (szFullDestPath);
|
|
if (nOverwrite == PROMPT_NO || nOverwrite == PROMPT_BREAK)
|
|
continue;
|
|
if (nOverwrite == PROMPT_ALL)
|
|
dwFlags |= MOVE_OVER_YES;
|
|
|
|
ConOutPrintf (_T("%s => %s "), szSrcPath, szFullDestPath);
|
|
|
|
/* are we really supposed to do something */
|
|
if (dwFlags & MOVE_NOTHING)
|
|
continue;
|
|
|
|
/*move the file*/
|
|
if (!(dwMoveStatusFlags & MOVE_SOURCE_IS_DIR &&
|
|
dwMoveStatusFlags & MOVE_PATHS_ON_DIF_VOL))
|
|
/* we aren't moving source folder to different drive */
|
|
MoveStatus = MoveFileEx (szSrcPath, szFullDestPath, dwMoveFlags);
|
|
else
|
|
{ /* we are moving source folder to different drive */
|
|
_tcscpy(szMoveDest, szFullDestPath);
|
|
_tcscpy(szMoveSrc, szSrcPath);
|
|
DeleteFile(szMoveDest);
|
|
MoveStatus = CreateDirectory(szMoveDest, NULL); /* we use default security settings */
|
|
if (MoveStatus)
|
|
{
|
|
_tcscat(szMoveDest,_T("\\"));
|
|
_tcscat(szMoveSrc,_T("\\"));
|
|
nDirLevel = 0;
|
|
pszDestDirPointer = szMoveDest + _tcslen(szMoveDest);
|
|
pszSrcDirPointer = szMoveSrc + _tcslen(szMoveSrc);
|
|
_tcscpy(pszSrcDirPointer,_T("*.*"));
|
|
hDestFile = FindFirstFile(szMoveSrc, &findDestBuffer);
|
|
if (hDestFile == INVALID_HANDLE_VALUE)
|
|
MoveStatus = FALSE;
|
|
else
|
|
{
|
|
BOOL FirstTime = TRUE;
|
|
FoundFile = TRUE;
|
|
MoveStatus = FALSE;
|
|
while(FoundFile)
|
|
{
|
|
if (FirstTime)
|
|
FirstTime = FALSE;
|
|
else
|
|
FoundFile = FindNextFile (hDestFile, &findDestBuffer);
|
|
|
|
if (!FoundFile)
|
|
{
|
|
/* Nothing to do in this folder so we stop working on it */
|
|
FindClose(hDestFile);
|
|
(pszSrcDirPointer)--;
|
|
(pszDestDirPointer)--;
|
|
_tcscpy(pszSrcDirPointer,_T(""));
|
|
_tcscpy(pszDestDirPointer,_T(""));
|
|
if (nDirLevel > 0)
|
|
{
|
|
TCHAR szTempPath[MAX_PATH];
|
|
INT_PTR nDiff;
|
|
|
|
FoundFile = TRUE; /* we need to continue our seek for files */
|
|
nDirLevel--;
|
|
RemoveDirectory(szMoveSrc);
|
|
GetDirectory(szMoveSrc,szTempPath,0);
|
|
nDiff = _tcslen(szMoveSrc) - _tcslen(szTempPath);
|
|
pszSrcDirPointer = pszSrcDirPointer - nDiff;
|
|
_tcscpy(pszSrcDirPointer,_T(""));
|
|
GetDirectory(szMoveDest,szTempPath,0);
|
|
nDiff = _tcslen(szMoveDest) - _tcslen(szTempPath);
|
|
pszDestDirPointer = pszDestDirPointer - nDiff;
|
|
_tcscpy(pszDestDirPointer,_T(""));
|
|
if (szMoveSrc[_tcslen(szMoveSrc) - 1] != _T('\\'))
|
|
_tcscat (szMoveSrc, _T("\\"));
|
|
if (szMoveDest[_tcslen(szMoveDest) - 1] != _T('\\'))
|
|
_tcscat (szMoveDest, _T("\\"));
|
|
pszDestDirPointer = szMoveDest + _tcslen(szMoveDest);
|
|
pszSrcDirPointer = szMoveSrc + _tcslen(szMoveSrc);
|
|
_tcscpy(pszSrcDirPointer,_T("*.*"));
|
|
hDestFile = FindFirstFile(szMoveSrc, &findDestBuffer);
|
|
if (hDestFile == INVALID_HANDLE_VALUE)
|
|
continue;
|
|
FirstTime = TRUE;
|
|
}
|
|
else
|
|
{
|
|
MoveStatus = TRUE; /* we moved everything so lets tell user about it */
|
|
RemoveDirectory(szMoveSrc);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* if we find "." or ".." we'll skip them */
|
|
if (_tcscmp(findDestBuffer.cFileName,_T(".")) == 0 ||
|
|
_tcscmp(findDestBuffer.cFileName,_T("..")) == 0)
|
|
continue;
|
|
|
|
_tcscpy(pszSrcDirPointer, findDestBuffer.cFileName);
|
|
_tcscpy(pszDestDirPointer, findDestBuffer.cFileName);
|
|
if (IsExistingFile(szMoveSrc))
|
|
{
|
|
FoundFile = CopyFile(szMoveSrc, szMoveDest, FALSE);
|
|
if (!FoundFile) continue;
|
|
DeleteFile(szMoveSrc);
|
|
}
|
|
else
|
|
{
|
|
FindClose(hDestFile);
|
|
CreateDirectory(szMoveDest, NULL);
|
|
_tcscat(szMoveDest,_T("\\"));
|
|
_tcscat(szMoveSrc,_T("\\"));
|
|
nDirLevel++;
|
|
pszDestDirPointer = szMoveDest + _tcslen(szMoveDest);
|
|
pszSrcDirPointer = szMoveSrc + _tcslen(szMoveSrc);
|
|
_tcscpy(pszSrcDirPointer,_T("*.*"));
|
|
hDestFile = FindFirstFile(szMoveSrc, &findDestBuffer);
|
|
if (hDestFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
FoundFile = FALSE;
|
|
continue;
|
|
}
|
|
FirstTime = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (MoveStatus)
|
|
{
|
|
ConOutResPrintf(STRING_MOVE_ERROR1);
|
|
}
|
|
else
|
|
{
|
|
ConOutResPrintf(STRING_MOVE_ERROR2);
|
|
nErrorLevel = 1;
|
|
}
|
|
}
|
|
while ((!OnlyOneFile || dwMoveStatusFlags & MOVE_SRC_CURRENT_IS_DIR ) &&
|
|
!(dwMoveStatusFlags & MOVE_SOURCE_IS_DIR) &&
|
|
FindNextFile (hFile, &findBuffer));
|
|
FindClose (hFile);
|
|
|
|
if(hDestFile && hDestFile != INVALID_HANDLE_VALUE)
|
|
FindClose(hDestFile);
|
|
|
|
Quit:
|
|
freep(arg);
|
|
return nErrorLevel;
|
|
}
|
|
|
|
#endif /* INCLUDE_CMD_MOVE */
|