mirror of
https://github.com/reactos/reactos.git
synced 2025-04-27 09:00:27 +00:00
[CMD] TYPE: Rewrite the command so as to fix some of its behaviour.
- Display the names of the files being TYPEd only if more than one file has been specified on the command-line, or if a file specification (with wildcards) is present (even just for one). These names are displayed on STDERR while the files are TYPEd on STDOUT, therefore allowing concatenating files by just redirecting STDOUT to a destination, without corrupting it with the displayed file names. Also, add a /N option to force not displaying these file names. - When file specifications (with wildcards) are being processed, silently ignore any directories matching them. If no corresponding files have been found, display a file-not-found error. - When explicitly directory names are specified, don't do any special treatment; the CreateFile() call will fail and return the appropriate error. - Fix the returned errorlevel values. See https://ss64.com/nt/type.html for more information. Fixes some cmd_winetests. - When reading from a file, retrieve its original size so that we can stop reading it once we are beyond its original ending. This allows avoiding an infinite read loop in case the output of the file is redirected back to it. Fixes CORE-17208 - Move the FileGetString() helper to the only file where it is actually used.
This commit is contained in:
parent
3d4af22328
commit
6a8754c83a
3 changed files with 293 additions and 108 deletions
|
@ -300,7 +300,6 @@ VOID StripQuotes(LPTSTR);
|
|||
BOOL IsValidPathName (LPCTSTR);
|
||||
BOOL IsExistingFile (LPCTSTR);
|
||||
BOOL IsExistingDirectory (LPCTSTR);
|
||||
BOOL FileGetString (HANDLE, LPTSTR, INT);
|
||||
VOID GetPathCase(TCHAR *, TCHAR *);
|
||||
|
||||
#define PROMPT_NO 0
|
||||
|
|
|
@ -510,45 +510,6 @@ BOOL IsExistingDirectory (LPCTSTR pszPath)
|
|||
}
|
||||
|
||||
|
||||
BOOL FileGetString (HANDLE hFile, LPTSTR lpBuffer, INT nBufferLength)
|
||||
{
|
||||
LPSTR lpString;
|
||||
DWORD dwRead;
|
||||
INT len = 0;
|
||||
#ifdef _UNICODE
|
||||
lpString = cmd_alloc(nBufferLength);
|
||||
#else
|
||||
lpString = lpBuffer;
|
||||
#endif
|
||||
|
||||
if (ReadFile(hFile, lpString, nBufferLength - 1, &dwRead, NULL))
|
||||
{
|
||||
/* break at new line*/
|
||||
CHAR *end = memchr(lpString, '\n', dwRead);
|
||||
len = dwRead;
|
||||
if (end)
|
||||
{
|
||||
len = (INT)(end - lpString) + 1;
|
||||
SetFilePointer(hFile, len - dwRead, NULL, FILE_CURRENT);
|
||||
}
|
||||
}
|
||||
|
||||
if (!len)
|
||||
{
|
||||
#ifdef _UNICODE
|
||||
cmd_free(lpString);
|
||||
#endif
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
lpString[len++] = '\0';
|
||||
#ifdef _UNICODE
|
||||
MultiByteToWideChar(OutputCodePage, 0, lpString, -1, lpBuffer, len);
|
||||
cmd_free(lpString);
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// See r874
|
||||
BOOL __stdcall PagePrompt(PCON_PAGER Pager, DWORD Done, DWORD Total)
|
||||
{
|
||||
|
|
|
@ -30,23 +30,211 @@
|
|||
|
||||
#ifdef INCLUDE_CMD_TYPE
|
||||
|
||||
static BOOL
|
||||
FileGetString(
|
||||
IN HANDLE hFile,
|
||||
OUT LPTSTR lpBuffer,
|
||||
IN LONG nBufferLength)
|
||||
{
|
||||
PCHAR pString;
|
||||
DWORD dwRead;
|
||||
LONG len = 0;
|
||||
|
||||
#ifdef _UNICODE
|
||||
pString = cmd_alloc(nBufferLength);
|
||||
#else
|
||||
pString = lpBuffer;
|
||||
#endif
|
||||
|
||||
if (ReadFile(hFile, pString, nBufferLength - 1, &dwRead, NULL))
|
||||
{
|
||||
/* Break at new line*/
|
||||
PCHAR end = memchr(pString, '\n', dwRead);
|
||||
len = dwRead;
|
||||
if (end)
|
||||
{
|
||||
len = (LONG)(end - pString) + 1;
|
||||
SetFilePointer(hFile, len - dwRead, NULL, FILE_CURRENT);
|
||||
}
|
||||
}
|
||||
|
||||
if (!len)
|
||||
{
|
||||
#ifdef _UNICODE
|
||||
cmd_free(pString);
|
||||
#endif
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pString[len++] = '\0';
|
||||
#ifdef _UNICODE
|
||||
MultiByteToWideChar(OutputCodePage, 0, pString, -1, lpBuffer, len);
|
||||
cmd_free(pString);
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL
|
||||
DoTypeFile(
|
||||
IN LPTSTR FileName,
|
||||
IN HANDLE hConsoleOut,
|
||||
IN BOOL bNoFileName,
|
||||
IN BOOL bPaging)
|
||||
{
|
||||
HANDLE hFile;
|
||||
BOOL bIsFile;
|
||||
DWORD dwFileSize;
|
||||
DWORD dwFilePos;
|
||||
DWORD dwRet;
|
||||
LPTSTR errmsg;
|
||||
TCHAR buff[256];
|
||||
|
||||
hFile = CreateFile(FileName,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
// FIXME: Use ErrorMessage() ?
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM,
|
||||
NULL,
|
||||
GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR)&errmsg,
|
||||
0,
|
||||
NULL);
|
||||
ConErrPrintf(_T("%s - %s"), FileName, errmsg);
|
||||
LocalFree(errmsg);
|
||||
nErrorLevel = 1;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* When reading from a file, retrieve its original size, so that
|
||||
* we can stop reading it once we are beyond its original ending.
|
||||
* This allows avoiding an infinite read loop in case the output
|
||||
* of the file is redirected back to it.
|
||||
* If we read from somewhere else (device, ...) don't do anything;
|
||||
* we will stop when ReadFile() fails (e.g. when Ctrl-Z is seen...).
|
||||
*/
|
||||
bIsFile = ((GetFileType(hFile) & ~FILE_TYPE_REMOTE) == FILE_TYPE_DISK);
|
||||
if (bIsFile)
|
||||
{
|
||||
dwFileSize = GetFileSize(hFile, NULL);
|
||||
if ((dwFileSize == INVALID_FILE_SIZE) &&
|
||||
(GetLastError() != ERROR_SUCCESS))
|
||||
{
|
||||
WARN("Error when retrieving file size, or size too large (%d)\n",
|
||||
dwFileSize);
|
||||
dwFileSize = 0;
|
||||
}
|
||||
dwFilePos = SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
|
||||
if ((dwFilePos == INVALID_SET_FILE_POINTER) &&
|
||||
(GetLastError() != ERROR_SUCCESS))
|
||||
{
|
||||
WARN("Error when setting file pointer\n");
|
||||
dwFilePos = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dwFileSize = dwFilePos = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Display the file name on StdErr if required, so that if StdOut
|
||||
* alone is redirected, we can obtain the file contents only.
|
||||
*/
|
||||
if (!bNoFileName)
|
||||
ConErrPrintf(_T("\n%s\n\n\n"), FileName);
|
||||
|
||||
if (bPaging)
|
||||
{
|
||||
while (FileGetString(hFile, buff, ARRAYSIZE(buff)))
|
||||
{
|
||||
if (!ConOutPrintfPaging(FALSE, _T("%s"), buff))
|
||||
{
|
||||
bCtrlBreak = FALSE;
|
||||
CloseHandle(hFile);
|
||||
nErrorLevel = 1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we read from a file, check where we are and stop
|
||||
* once we are beyond the original end of the file.
|
||||
*/
|
||||
if (bIsFile)
|
||||
{
|
||||
dwFilePos = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
|
||||
if ((dwFilePos == INVALID_SET_FILE_POINTER) &&
|
||||
(GetLastError() != ERROR_SUCCESS))
|
||||
{
|
||||
WARN("Error when getting file pointer\n");
|
||||
break;
|
||||
}
|
||||
if (dwFilePos >= dwFileSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (ReadFile(hFile, buff, sizeof(buff), &dwRet, NULL) && dwRet > 0)
|
||||
{
|
||||
WriteFile(hConsoleOut, buff, dwRet, &dwRet, NULL);
|
||||
if (bCtrlBreak)
|
||||
{
|
||||
bCtrlBreak = FALSE;
|
||||
CloseHandle(hFile);
|
||||
nErrorLevel = 1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we read from a file, check where we are and stop
|
||||
* once we are beyond the original end of the file.
|
||||
*/
|
||||
if (bIsFile)
|
||||
{
|
||||
dwFilePos = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
|
||||
if ((dwFilePos == INVALID_SET_FILE_POINTER) &&
|
||||
(GetLastError() != ERROR_SUCCESS))
|
||||
{
|
||||
WARN("Error when getting file pointer\n");
|
||||
break;
|
||||
}
|
||||
if (dwFilePos >= dwFileSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(hFile);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
INT cmd_type(LPTSTR param)
|
||||
{
|
||||
TCHAR buff[256];
|
||||
HANDLE hFile, hConsoleOut;
|
||||
DWORD dwRet;
|
||||
INT argc,i;
|
||||
LPTSTR *argv;
|
||||
INT argc, i;
|
||||
LPTSTR* argv;
|
||||
LPTSTR errmsg;
|
||||
HANDLE hConsoleOut;
|
||||
BOOL bNoFileName = FALSE;
|
||||
BOOL bPaging = FALSE;
|
||||
BOOL bFirstTime = TRUE;
|
||||
|
||||
hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
BOOL bFileFound;
|
||||
DWORD dwLastError;
|
||||
UINT nFileSpecs = 0;
|
||||
HANDLE hFind;
|
||||
WIN32_FIND_DATA FindData;
|
||||
|
||||
if (!_tcsncmp(param, _T("/?"), 2))
|
||||
{
|
||||
ConOutResPaging(TRUE,STRING_TYPE_HELP1);
|
||||
ConOutResPaging(TRUE, STRING_TYPE_HELP1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -56,87 +244,124 @@ INT cmd_type(LPTSTR param)
|
|||
return 1;
|
||||
}
|
||||
|
||||
argv = split(param, &argc, TRUE, FALSE);
|
||||
/* Parse the command line. We will manually expand any file specification present. */
|
||||
argv = split(param, &argc, FALSE, FALSE);
|
||||
|
||||
for (i = 0; i < argc; i++)
|
||||
/* Loop through the options, count also the specified number of file specifications */
|
||||
for (i = 0; i < argc; ++i)
|
||||
{
|
||||
if (argv[i][0] == _T('/') && _tcslen(argv[i]) == 2 && _totupper(argv[i][1]) == _T('P'))
|
||||
if (argv[i][0] == _T('/'))
|
||||
{
|
||||
bPaging = TRUE;
|
||||
if (_tcslen(argv[i]) == 2)
|
||||
{
|
||||
switch (_totupper(argv[i][1]))
|
||||
{
|
||||
case _T('N'):
|
||||
bNoFileName = TRUE;
|
||||
continue;
|
||||
|
||||
case _T('P'):
|
||||
bPaging = TRUE;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// error_invalid_switch(argv[i] + 1);
|
||||
ConErrResPrintf(STRING_TYPE_ERROR1, argv[i] + 1);
|
||||
nErrorLevel = 1;
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
/* This should be a file specification */
|
||||
++nFileSpecs;
|
||||
}
|
||||
|
||||
for (i = 0; i < argc; i++)
|
||||
nErrorLevel = 0;
|
||||
|
||||
hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
/* Reset paging state */
|
||||
if (bPaging)
|
||||
ConOutPrintfPaging(TRUE, _T(""));
|
||||
|
||||
/* Now loop through the files */
|
||||
for (i = 0; i < argc; ++i)
|
||||
{
|
||||
if (argv[i][0] == _T('/') && _totupper(argv[i][1]) != _T('P'))
|
||||
{
|
||||
ConErrResPrintf(STRING_TYPE_ERROR1, argv[i] + 1);
|
||||
/* Skip the options */
|
||||
if (argv[i][0] == _T('/'))
|
||||
continue;
|
||||
}
|
||||
if (argv[i][0] == _T('/') && _tcslen(argv[i]) == 2 && _totupper(argv[i][1]) == _T('P'))
|
||||
|
||||
/* If wildcards are present in this file specification, perform a file enumeration */
|
||||
if (_tcschr(argv[i], _T('*')) || _tcschr(argv[i], _T('?')))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
dwLastError = ERROR_SUCCESS;
|
||||
bFileFound = FALSE;
|
||||
|
||||
nErrorLevel = 0;
|
||||
hFind = FindFirstFile(argv[i], &FindData);
|
||||
|
||||
hFile = CreateFile(argv[i],
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM,
|
||||
NULL,
|
||||
GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR) &errmsg,
|
||||
0,
|
||||
NULL);
|
||||
ConErrPrintf (_T("%s - %s"), argv[i], errmsg);
|
||||
LocalFree (errmsg);
|
||||
nErrorLevel = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bPaging)
|
||||
{
|
||||
while (FileGetString(hFile, buff, ARRAYSIZE(buff)))
|
||||
if (hFind != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (!ConOutPrintfPaging(bFirstTime, _T("%s"), buff))
|
||||
/* Loop through all the files */
|
||||
do
|
||||
{
|
||||
bCtrlBreak = FALSE;
|
||||
CloseHandle(hFile);
|
||||
freep(argv);
|
||||
return 0;
|
||||
}
|
||||
bFirstTime = FALSE;
|
||||
/* Ignore any directory silently */
|
||||
if (!_tcscmp(FindData.cFileName, _T(".")) ||
|
||||
!_tcscmp(FindData.cFileName, _T("..")) ||
|
||||
(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bFileFound = TRUE;
|
||||
if (!DoTypeFile(FindData.cFileName, hConsoleOut, bNoFileName, bPaging))
|
||||
{
|
||||
FindClose(hFind);
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
} while (FindNextFile(hFind, &FindData));
|
||||
|
||||
FindClose(hFind);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return an error if the file specification could not be resolved,
|
||||
* or no actual files were encountered (but only directories).
|
||||
*/
|
||||
if (hFind == INVALID_HANDLE_VALUE)
|
||||
dwLastError = GetLastError();
|
||||
else if (!bFileFound)
|
||||
dwLastError = ERROR_FILE_NOT_FOUND;
|
||||
|
||||
if (dwLastError != ERROR_SUCCESS)
|
||||
{
|
||||
// FIXME: Use ErrorMessage() ?
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM,
|
||||
NULL,
|
||||
dwLastError,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR)&errmsg,
|
||||
0,
|
||||
NULL);
|
||||
ConErrPrintf(_T("%s - %s"), argv[i], errmsg);
|
||||
LocalFree(errmsg);
|
||||
nErrorLevel = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (ReadFile(hFile, buff, sizeof(buff), &dwRet, NULL) && dwRet > 0)
|
||||
{
|
||||
WriteFile(hConsoleOut, buff, dwRet, &dwRet, NULL);
|
||||
if (bCtrlBreak)
|
||||
{
|
||||
bCtrlBreak = FALSE;
|
||||
CloseHandle(hFile);
|
||||
freep(argv);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (!DoTypeFile(argv[i], hConsoleOut, (bNoFileName || (nFileSpecs <= 1)), bPaging))
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
CloseHandle(hFile);
|
||||
/* Continue with the next file specification */
|
||||
}
|
||||
|
||||
Quit:
|
||||
freep(argv);
|
||||
return 0;
|
||||
return nErrorLevel;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue