windows-nt/Source/XPSP1/NT/admin/services/sched/test/jt/atsign.cxx
2020-09-26 16:20:57 +08:00

471 lines
13 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 1996.
//
// File: atsign.cxx
//
// Contents: Functions to read commands from a script file.
//
// History: 04-20-95 davidmun Created
//
//----------------------------------------------------------------------------
#include <headers.hxx>
#pragma hdrstop
#include "jt.hxx"
//
// Forward references
//
ULONG FindEol(CHAR *pstr, ULONG cchRemaining);
CHAR FindNextNonSpace(CHAR *pstr, ULONG cchRemaining);
//+---------------------------------------------------------------------------
//
// Function: DoAtSign
//
// Synopsis: Get filename from commandline and process it.
//
// Arguments: [ppwsz] - command line
//
// Returns: S_OK - file processed without error
// E_* - error logged
//
// Modifies: *[ppwsz]
//
// History: 04-20-95 davidmun Created
// 01-03-96 DavidMun Support multi-line commands
//
// Notes: This routine may be indirectly recursive.
//
//----------------------------------------------------------------------------
HRESULT DoAtSign(WCHAR **ppwsz)
{
HRESULT hr = S_OK;
ShHANDLE shFile;
ShHANDLE shFileMapping;
VOID *pvBase = NULL;
BOOL fOk;
ULONG cchFile;
TCHAR tszFilename[MAX_PATH + 1] = TEXT("None");
g_Log.Write(LOG_DEBUG, "DoAtSign");
do
{
hr = GetFilename(ppwsz, L"command file name");
BREAK_ON_FAILURE(hr);
#ifdef UNICODE
wcscpy(tszFilename, g_wszLastStringToken);
#else
wcstombs(tszFilename, g_wszLastStringToken, wcslen(g_wszLastStringToken)+1);
#endif
//
// Open the command file, then hand one line at a time to
// ProcessCommandLine (which is our caller, so we're recursing).
//
shFile = CreateFile(
tszFilename,
GENERIC_READ,
FILE_SHARE_READ,
NULL, // default security
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL); // no template
if (shFile == INVALID_HANDLE_VALUE)
{
hr = E_FAIL;
g_Log.Write(LOG_ERROR, "Can't open file %S", tszFilename);
break;
}
shFileMapping = CreateFileMapping(
shFile,
NULL, // default security
PAGE_READONLY,
0, 0, // max size is size of file
NULL); // unnamed object
if (shFileMapping == NULL)
{
hr = E_FAIL;
g_Log.Write(LOG_ERROR, "CreateFileMapping (%u)", GetLastError());
break;
}
pvBase = MapViewOfFile(
shFileMapping,
FILE_MAP_READ,
0, 0, // start mapping at beginning of file
0); // map entire file
if (!pvBase)
{
hr = E_FAIL;
g_Log.Write(LOG_ERROR, "MapViewOfFile (%u)", GetLastError());
break;
}
//
// pvBase points to start of mapped file. Get the file length.
//
BY_HANDLE_FILE_INFORMATION bhfi;
fOk = GetFileInformationByHandle(shFile, &bhfi);
if (!fOk)
{
hr = E_FAIL;
g_Log.Write(LOG_ERROR, "GetFileInformationByHandle (%u)", GetLastError());
break;
}
cchFile = bhfi.nFileSizeLow;
if (bhfi.nFileSizeHigh)
{
hr = E_FAIL;
g_Log.Write(LOG_ERROR, "File too large");
break;
}
}
while (0);
//
// Finally ready to process file.
//
ULONG cchProcessed = 0;
ULONG ulCurLine = 1;
CHAR *pstrFile = (CHAR *) pvBase;
while (SUCCEEDED(hr) && cchProcessed < cchFile)
{
CHAR szLine[MAX_TOKEN_LEN + 1] = "";
ULONG cchLine = 0;
WCHAR wszLine[MAX_TOKEN_LEN + 1];
WCHAR wszExpandedLine[MAX_TOKEN_LEN + 1];
BOOL fInQuote = FALSE;
BOOL fFoundNextCommand = FALSE;
//
// Copy all chars of the next command from pstrfile into wszline,
// condensing whitespace into single blanks and skipping comment
// lines.
//
for (;
cchProcessed < cchFile &&
!fFoundNextCommand &&
cchLine < MAX_TOKEN_LEN;
cchProcessed++)
{
//
// If we're in a quoted string, copy everything verbatim until
// closing quote
//
if (fInQuote)
{
if (pstrFile[cchProcessed] == '"')
{
fInQuote = FALSE;
}
//
// Check for error case of newline in string
//
if (pstrFile[cchProcessed] == '\n')
{
hr = E_FAIL;
g_Log.Write(LOG_FAIL, "Newline in string constant");
break;
}
szLine[cchLine++] = pstrFile[cchProcessed];
continue;
}
//
// Not already in a quoted string. See if we're entering one.
//
if (pstrFile[cchProcessed] == '"')
{
fInQuote = TRUE;
szLine[cchLine++] = pstrFile[cchProcessed];
continue;
}
//
// Not in or starting a quoted string, so we're free to condense
// whitespace (including newlines) and ignore comment lines
//
if (isspace(pstrFile[cchProcessed]))
{
//
// Only copy this space char if none has been copied yet.
// Bump the line count if the whitespace char we're skipping
// is a newline.
//
if (cchLine && szLine[cchLine - 1] != ' ')
{
szLine[cchLine++] = ' ';
}
if (pstrFile[cchProcessed] == '\n')
{
ulCurLine++;
}
continue;
}
if (pstrFile[cchProcessed] == ';')
{
//
// Skip to end of line
//
cchProcessed += FindEol(
&pstrFile[cchProcessed],
cchFile - cchProcessed);
// subtract 1 because for loop is going to add one
cchProcessed--;
continue;
}
//
// Next char is not quote, semicolon (start of comment), or
// whitespace. If we haven't copied anything yet, copy that char
// as the first of the line.
//
if (!cchLine)
{
szLine[cchLine++] = pstrFile[cchProcessed];
continue;
}
//
// Since we've already started copying stuff, we want to quit when
// we get to the start of the next command, i.e., when we see a
// switch char '/' or '-'.
//
// Unfortunately these two characters also delimit the parts of a
// date, and we don't want to stop copying the line because of a
// date.
//
// Therefore we'll only stop copying if the next non whitespace
// character is not a number. This imposes the constraints that
// no commands can be a number (i.e. /10 cannot be a valid
// command) and that dates must use only digits (i.e. 10-Feb is
// not valid because we'll think -Feb is a command and only copy
// the 10).
//
// Note it isn't safe to check that the *previous* character was a
// digit and assume we're in a date, since a command with a
// numeric argument could be mistaken for a date.
//
// /foo bar=10 /baz
//
if (pstrFile[cchProcessed] == '/' ||
pstrFile[cchProcessed] == '-')
{
CHAR ch;
ch = FindNextNonSpace(
&pstrFile[cchProcessed + 1],
cchFile - (cchProcessed + 1));
if (isdigit(ch))
{
szLine[cchLine++] = pstrFile[cchProcessed];
}
else
{
fFoundNextCommand = TRUE;
cchProcessed--; // because for loop will increment it
}
}
else
{
szLine[cchLine++] = pstrFile[cchProcessed];
}
}
BREAK_ON_FAILURE(hr);
//
// If we stopped copying not because we found the next command or hit
// the end of the file, then it's because we ran out of room in
// szLine, which is an error.
//
if (!fFoundNextCommand && cchProcessed < cchFile)
{
hr = E_FAIL;
g_Log.Write(
LOG_ERROR,
"Line %u is longer than %u chars",
ulCurLine,
MAX_TOKEN_LEN);
break;
}
#ifdef UNICODE
//
// Convert line to wchar and null terminate it.
//
mbstowcs(wszLine, szLine, cchLine);
wszLine[cchLine] = L'\0';
//
// Expand environment variables
//
ULONG cchRequired;
cchRequired = ExpandEnvironmentStrings(
wszLine,
wszExpandedLine,
MAX_TOKEN_LEN+1);
#else
CHAR szExpandedLine[MAX_TOKEN_LEN + 1];
ULONG cchRequired;
szLine[cchLine] = '\0';
cchRequired = ExpandEnvironmentStrings(
szLine,
szExpandedLine,
MAX_TOKEN_LEN+1);
mbstowcs(wszExpandedLine, szExpandedLine, MAX_TOKEN_LEN + 1);
#endif
if (!cchRequired || cchRequired > MAX_TOKEN_LEN + 1)
{
hr = E_FAIL;
g_Log.Write(LOG_FAIL, "ExpandEnvironmentStrings failed");
break;
}
//
// Perform the command in wszExpandedLine, then loop around and
// read the next command.
//
if (SUCCEEDED(hr))
{
g_Log.Write(LOG_DEBUG, "DoAtSign: processing '%S'", wszExpandedLine);
hr = ProcessCommandLine(wszExpandedLine);
}
}
if (FAILED(hr))
{
g_Log.Write(LOG_ERROR, "File: %S Line: %u", tszFilename, ulCurLine);
}
if (pvBase)
{
fOk = UnmapViewOfFile(pvBase);
if (!fOk)
{
hr = E_FAIL;
g_Log.Write(LOG_ERROR, "UnmapViewOfFile (%u)", GetLastError());
}
}
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: FindEol
//
// Synopsis: Return the number of characters between [pstr] and the end
// of the line.
//
// Arguments: [pstr] - non-terminated string
// [cchRemaining] - characters till eof
//
// Returns: Character count
//
// History: 04-20-95 davidmun Created
//
//----------------------------------------------------------------------------
ULONG FindEol(CHAR *pstr, ULONG cchRemaining)
{
CHAR *pstrCur;
ULONG cchCur;
for (pstrCur = pstr, cchCur = 0;
cchCur < cchRemaining;
cchCur++, pstrCur++)
{
if (*pstrCur == '\r' || *pstrCur == '\n')
{
return cchCur;
}
}
return cchCur;
}
//+---------------------------------------------------------------------------
//
// Function: FindNextNonSpace
//
// Synopsis: Return the next non-whitespace character in [pstr], or space
// if there are no non-whitespace characters in the next
// [cchRemaining] characters.
//
// Arguments: [pstr] - non-terminated string
// [cchRemaining] - number of characters in string
//
// Returns: Character as described.
//
// History: 01-10-96 DavidMun Created
//
//----------------------------------------------------------------------------
CHAR FindNextNonSpace(CHAR *pstr, ULONG cchRemaining)
{
CHAR *pstrCur;
ULONG cchCur;
for (pstrCur = pstr, cchCur = 0;
cchCur < cchRemaining;
cchCur++, pstrCur++)
{
if (!isspace(*pstrCur))
{
return *pstrCur;
}
}
return ' ';
}