reactos/base/shell/cmd/move.c
Hermès Bélusca-Maïto 3b960a1c21
[CMD] MOVE: Set the errorlevel on failure.
CORE-14261
2020-08-19 20:36:07 +02:00

523 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 */