windows-nt/Source/XPSP1/NT/base/ntsetup/oemtools/oempatch/ansparse.cpp
2020-09-26 16:20:57 +08:00

695 lines
18 KiB
C++

#include <stdio.h>
#include <windows.h>
#include "patchapi.h"
#include "const.h"
#include "ansparse.h"
///////////////////////////////////////////////////////////////////////////////
//
// class PATCH_LANGUAGE
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// PATCH_LANGUAGE, constructor for the language struct, just zero everything
//
///////////////////////////////////////////////////////////////////////////////
PATCH_LANGUAGE::PATCH_LANGUAGE() :
s_hScriptFile(INVALID_HANDLE_VALUE),
s_blnBase(FALSE),
s_iComplete(0),
s_iDirectoryCount(0),
s_iPatchDirectoryCount(0),
s_iSubPatchDirectoryCount(0),
s_iSubExceptDirectoryCount(0),
s_pNext(NULL)
{
}
///////////////////////////////////////////////////////////////////////////////
//
// ~PATCH_LANGUAGE, destructor, erases the structures recursively
//
///////////////////////////////////////////////////////////////////////////////
PATCH_LANGUAGE::~PATCH_LANGUAGE()
{
if(s_pNext)
{
delete s_pNext;
s_pNext = NULL;
}
if(s_hScriptFile != INVALID_HANDLE_VALUE)
{
CloseHandle(s_hScriptFile);
}
}
///////////////////////////////////////////////////////////////////////////////
//
// class AnswerParser
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// AnswerParser, constructor for the parser, initializes member variables
//
///////////////////////////////////////////////////////////////////////////////
AnswerParser::AnswerParser() :
m_hAnsFile(INVALID_HANDLE_VALUE),
m_pHead(NULL),
m_iBaseDirectoryCount(0)
{
ZeroMemory(m_wszBaseDirectory, sizeof(m_wszBaseDirectory));
ZeroMemory(m_structHash, sizeof(m_structHash));
ZeroMemory(m_structHashUsed, sizeof(m_structHashUsed));
}
///////////////////////////////////////////////////////////////////////////////
//
// AnswerParser, destructor for the parser, removes the language structures
//
///////////////////////////////////////////////////////////////////////////////
AnswerParser::~AnswerParser()
{
if(m_hAnsFile != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hAnsFile);
m_hAnsFile = INVALID_HANDLE_VALUE;
}
if(m_pHead != NULL)
{
delete m_pHead;
m_pHead = NULL;
}
}
///////////////////////////////////////////////////////////////////////////////
//
// GetHashValues, computes a hashvalue based on double hashing
//
// Parameters:
//
// pwszFileName, the filename which the hash values are calculated from
// iHash1, the first hash value
// iHash2, the second hash value
//
// Return:
//
// none
//
///////////////////////////////////////////////////////////////////////////////
VOID AnswerParser::GetHashValues(IN CONST WCHAR* pwszFileName,
OUT ULONG& iHash1,
OUT ULONG& iHash2)
{
ULONG iLength = wcslen(pwszFileName);
if(iLength <= 2)
{
// minimum 1 letter filename
iHash1 = ((ULONG)(pwszFileName[0])) % EXCEP_FILE_LIMIT;
iHash2 = (((ULONG)(pwszFileName[0])) + 17) % EXCEP_FILE_LIMIT;
}
else if(iLength <= 5)
{
// minimum 3 letter
iHash1 = ((ULONG)(pwszFileName[1])) % EXCEP_FILE_LIMIT;
iHash2 = ((ULONG)(pwszFileName[2])) % EXCEP_FILE_LIMIT;
}
else if(iLength <= 10)
{
// minimum 6 letter
iHash1 = (((ULONG)(pwszFileName[0])) + 1) * (((ULONG)(pwszFileName[1])) + 2) % EXCEP_FILE_LIMIT;
iHash2 = (((ULONG)(pwszFileName[4])) + 5) * (((ULONG)(pwszFileName[5])) + 6) % EXCEP_FILE_LIMIT;
}
else if(iLength <= 15)
{
// minimum 11 letter
iHash1 = (((ULONG)(pwszFileName[2])) + 3) * (((ULONG)(pwszFileName[4])) + 5) % EXCEP_FILE_LIMIT;
iHash2 = (((ULONG)(pwszFileName[9])) + 10) * (((ULONG)(pwszFileName[10])) + 11) % EXCEP_FILE_LIMIT;
}
else if(iLength <= 20)
{
// minimum 16 letter
iHash1 = (((ULONG)(pwszFileName[4])) + 5) * (((ULONG)(pwszFileName[6])) + 7) % EXCEP_FILE_LIMIT;
iHash2 = (((ULONG)(pwszFileName[7])) + 8) * (((ULONG)(pwszFileName[15])) + 16) % EXCEP_FILE_LIMIT;
}
else
{
// minimum 21 letter
iHash1 = (((ULONG)(pwszFileName[11])) + 12) * (((ULONG)(pwszFileName[14])) + 15) % EXCEP_FILE_LIMIT;
iHash2 = (((ULONG)(pwszFileName[17])) + 18) * (((ULONG)(pwszFileName[20])) + 21) % EXCEP_FILE_LIMIT;
}
}
///////////////////////////////////////////////////////////////////////////////
//
// SaveFileExceptHash, saves the names of the exempt files into the hash table
//
// Parameters:
//
// pwszFileName, the filename that is to be saved
//
// Return:
//
// TRUE for saved, FALSE for redundant file or out of space, not saved
//
///////////////////////////////////////////////////////////////////////////////
BOOL AnswerParser::SaveFileExceptHash(IN WCHAR* pwszFileName)
{
ULONG iFirstHash = 0;
ULONG iSecondHash = 0;
ULONG iIndex = 0;
ULONG iHashValue = 0;
if(pwszFileName)
{
GetHashValues(pwszFileName, iFirstHash, iSecondHash);
do
{
iHashValue = (iFirstHash + iSecondHash * iIndex) % EXCEP_FILE_LIMIT;
if(m_structHashUsed[iHashValue] == 0)
{
wcsncpy(m_structHash[iHashValue], pwszFileName, SHORT_STRING_LENGTH);
m_structHashUsed[iHashValue] = 1;
return(TRUE);
}
else
{
if(wcscmp(m_structHash[iHashValue], pwszFileName) == 0)
{
// same file name, so treat as error for now
return(FALSE);
}
iIndex++;
}
}
while(iIndex < EXCEP_FILE_LIMIT);
}
// ran out of space
return(FALSE);
}
///////////////////////////////////////////////////////////////////////////////
//
// IsFileExceptHash, is the file an exempt file?
//
// Parameters:
//
// pwszFileName, the filename that is to be determined
//
// Return:
//
// TRUE for yes, FALSE otherwise
//
///////////////////////////////////////////////////////////////////////////////
BOOL AnswerParser::IsFileExceptHash(IN CONST WCHAR* pwszFileName)
{
ULONG iFirstHash = 0;
ULONG iSecondHash = 0;
ULONG iIndex = 0;
ULONG iHashValue = 0;
static WCHAR strBuffer[STRING_LENGTH];
if(pwszFileName)
{
wcsncpy(strBuffer, pwszFileName, STRING_LENGTH);
_wcslwr(strBuffer);
GetHashValues(strBuffer, iFirstHash, iSecondHash);
do
{
iHashValue = (iFirstHash + iSecondHash * iIndex) % EXCEP_FILE_LIMIT;
if(m_structHashUsed[iHashValue] == 1)
{
if(wcscmp(m_structHash[iHashValue], strBuffer) == 0)
{
// same file name
return(TRUE);
}
else
{
// space taken, goto the next location
iIndex++;
}
}
else
{
// space is not taken, hash slot empty
return(FALSE);
}
}
while(iIndex < EXCEP_FILE_LIMIT);
}
// ran out of space
return(FALSE);
}
///////////////////////////////////////////////////////////////////////////////
//
// Parse, the function used to parse the answerfile and fill in the language
// structures
//
// Parameters:
//
// pwszAnswerFile, the answer file's filename
//
// Return:
//
// TRUE for successful parsing, FALSE otherwise
//
///////////////////////////////////////////////////////////////////////////////
BOOL AnswerParser::Parse(IN CONST WCHAR* pwszAnswerFile)
{
WCHAR strLine[STRING_LENGTH];
WCHAR* strString = NULL;
WCHAR* strStringBefore = NULL;
WCHAR* strStringAfter = NULL;
PPATCH_LANGUAGE thisNode = NULL;
LONG iState = 0;
LONG iBaseCount = 0;
BOOL blnComplete = FALSE;
m_hAnsFile = CreateFileW(pwszAnswerFile,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(m_hAnsFile != INVALID_HANDLE_VALUE)
{
if(IsUnicodeFile(m_hAnsFile))
{
// state 1 is to exit
// state 0 is to start
// state 2 is in except files
// state 3 is in creating a new language struct
while(iState != 1)
{
switch(iState)
{
case 0:
iState = (ReadLine(m_hAnsFile, strLine) ? 0 : 1);
if(iState == 0 && strLine[0] != L';')
{
strString = wcstok(strLine, L";");
if(strString)
{
if(wcsstr(strString, L"[except]") != NULL)
{
// get except files
iState = 2;
}
else if((strStringBefore = wcschr(strString, L'[')) != NULL &&
(strStringAfter = wcschr(strString, L']')) != NULL)
{
// it's a language
iState = 3;
thisNode = new PATCH_LANGUAGE;
if(thisNode != NULL)
{
CreateNewLanguage(thisNode, strStringBefore, strStringAfter);
}
else
{
iState = 1;
}
}
}
}
break;
case 1:
break;
case 2:
iState = (ReadLine(m_hAnsFile, strLine) ? 2 : 1);
if(iState == 2 && strLine[0] != L';')
{
strString = wcstok(strLine, L";");
if(strString)
{
if(wcscspn(strString, L"[]") == wcslen(strString))
{
SaveFileExceptHash(strString);
}
else if(wcsstr(strString, L"[except]") != NULL)
{
// exception files
}
else if((strStringBefore = wcschr(strString, L'[')) != NULL &&
(strStringAfter = wcschr(strString, L']')) != NULL)
{
// it's a language
iState = 3;
thisNode = new PATCH_LANGUAGE;
if(thisNode != NULL)
{
CreateNewLanguage(thisNode, strStringBefore, strStringAfter);
}
else
{
iState = 1;
}
}
}
}
break;
case 3:
iState = (ReadLine(m_hAnsFile, strLine) ? 3 : 1);
if(iState == 3 && strLine[0] != L';')
{
strString = wcstok(strLine, L";");
if(strString)
{
if(wcscspn(strString, L"[]") == wcslen(strString))
{
// one of the language fields
strStringBefore = wcstok(strString, L"=");
strStringAfter = wcstok(NULL, L"=");
if(strStringBefore && strStringAfter && thisNode)
{
if(_wcsicmp(L"directory", strStringBefore) == 0 &&
(thisNode->s_iDirectoryCount = wcslen(strStringAfter)) > 0)
{
wcscpy(thisNode->s_wszDirectory, strStringAfter);
thisNode->s_iComplete += 1;
}
else if(_wcsicmp(L"base_directory", strStringBefore) == 0 &&
wcslen(strStringAfter) > 0)
{
thisNode->s_blnBase = TRUE;
wcscpy(m_wszBaseDirectory, strStringAfter);
}
else if(_wcsicmp(L"patch_directory", strStringBefore) == 0 &&
(thisNode->s_iPatchDirectoryCount = wcslen(strStringAfter)) > 0)
{
wcscpy(thisNode->s_wszPatchDirectory, strStringAfter);
wcscpy(thisNode->s_wszSubPatchDirectory, strStringAfter);
wcscat(thisNode->s_wszSubPatchDirectory, PATCH_SUB_PATCH);
thisNode->s_iSubPatchDirectoryCount = wcslen(thisNode->s_wszSubPatchDirectory);
wcscpy(thisNode->s_wszSubExceptDirectory, strStringAfter);
wcscat(thisNode->s_wszSubExceptDirectory, PATCH_SUB_EXCEPT);
thisNode->s_iSubExceptDirectoryCount = wcslen(thisNode->s_wszSubExceptDirectory);
wcscpy(thisNode->s_wszScriptFile, strStringAfter);
if(thisNode->s_wszScriptFile[thisNode->s_iPatchDirectoryCount - 1] != L'\\')
{
thisNode->s_wszScriptFile[thisNode->s_iPatchDirectoryCount] = L'\\';
thisNode->s_wszScriptFile[thisNode->s_iPatchDirectoryCount + 1] = 0;
}
wcscat(thisNode->s_wszScriptFile, APPLY_PATCH_SCRIPT);
thisNode->s_iComplete += 1;
}
}
}
else if(wcsstr(strString, L"[except]") != NULL)
{
// exception files
iState = 2;
}
else if((strStringBefore = wcschr(strString, L'[')) != NULL &&
(strStringAfter = wcschr(strString, L']')) != NULL)
{
// it's a language
thisNode = new PATCH_LANGUAGE;
if(thisNode != NULL)
{
CreateNewLanguage(thisNode, strStringBefore, strStringAfter);
}
else
{
iState = 1;
}
}
}
}
break;
default:
iState = 1;
break;
}
}
// end of file now check for validity
if(m_pHead == NULL ||
wcslen(m_wszBaseDirectory) < 1)
{
printf("The file OEMPatch.ans has no base language content. Use /? for help.\n");
return(FALSE);
}
thisNode = m_pHead;
while(thisNode)
{
blnComplete |= (thisNode->s_iComplete == LANGUAGE_COMPLETE);
if(thisNode->s_blnBase) iBaseCount += 1;
thisNode = thisNode->s_pNext;
}
if(!blnComplete)
{
printf("The file OEMPatch.ans has no language specified. Use /? for help.\n");
return(FALSE);
}
if(iBaseCount > 1)
{
printf("The file OEMPatch.ans has more than one base language. Use /? for help.\n");
return(FALSE);
}
}
else
{
printf("The file OEMPatch.ans is not UNICODE as required. Use /? for help.\n");
return(FALSE);
}
CloseHandle(m_hAnsFile);
m_hAnsFile = INVALID_HANDLE_VALUE;
}
else
{
printf("The file OEMPatch.ans cannot be found. Use /? for help.\n");
return(FALSE);
}
m_iBaseDirectoryCount = wcslen(m_wszBaseDirectory);
return(TRUE);
}
BOOL AnswerParser::IsUnicodeFile(IN HANDLE hFile)
{
WCHAR cFirstChar = 0;
ULONG iRead = 0;
if(hFile != INVALID_HANDLE_VALUE &&
ReadFile(hFile, &cFirstChar, sizeof(WCHAR), &iRead, NULL) &&
iRead != 0 &&
cFirstChar == UNICODE_HEAD)
{
return(TRUE);
}
return(FALSE);
}
///////////////////////////////////////////////////////////////////////////////
//
// ReadLine, this function reads a line from a unicoded file and return the
// contents in strLine, this function should only be called after
// the first two unicoded chars are already read
//
// Parameters:
//
// hFile, the file handle points to the file to read from
// strLine, the buffer that contains the line just read
//
// Return:
//
// TRUE for a line read, FALSE for invalid file, end of file and error in
// read
//
///////////////////////////////////////////////////////////////////////////////
BOOL AnswerParser::ReadLine(IN HANDLE hFile, IN WCHAR* strLine)
{
// read raw bytes into this buffer, notice the buffer length is 1 over the
// number of total read bytes, it is there to ensure that the last char is
// 0, so that when iLength = 10, which is the same as endofline, the
// wcstok function will return something bizzar
static WCHAR strBuffer[STRING_LENGTH + 1];
static LONG iLength = 0;
static LONG iReadChar = 0;
static ULONG iRead = 0;
static LONG iOffset = 0;
static LONG iThisLineLength = 0;
static WCHAR* strThisLine = NULL;
if(hFile != INVALID_HANDLE_VALUE && strLine)
{
if(iLength > 0)
{
// char 0xA is set to 0
strThisLine = wcstok(strBuffer + iReadChar - iLength, CRETURN);
iThisLineLength = wcslen(strThisLine);
if(iThisLineLength + 1 <= iLength)
{
iLength = iLength - iThisLineLength - 1;
// char 0xD is set to 0
strThisLine[iThisLineLength - 1] = 0;
wcscpy(strLine, strThisLine);
}
else
{
wcsncpy(strLine, strThisLine, iLength);
// set the last char + 1 to 0 for cat
strLine[iLength] = 0;
if(strLine[iLength - 1] != ENDOFLINE[0])
{
ReadFile(hFile, strBuffer, STRING_LENGTH * sizeof(WCHAR), &iRead, NULL);
iReadChar = iRead / sizeof(WCHAR);
// char 0xA is set to 0
strThisLine = wcstok(strBuffer, CRETURN);
iThisLineLength = wcslen(strThisLine);
iLength = iReadChar - iThisLineLength - 1;
// char 0xD is set to 0
strThisLine[iThisLineLength - 1] = 0;
wcscat(strLine, strThisLine);
if(strBuffer[0] == CRETURN[0])
{
iLength -= 1;
}
}
else
{
strLine[iLength - 1] = 0;
iLength = 0;
}
}
}
else
{
if(ReadFile(hFile, strBuffer, STRING_LENGTH * sizeof(WCHAR), &iRead, NULL) && iRead != 0)
{
iReadChar = iRead / sizeof(WCHAR);
// char 0xA is set to 0
strThisLine = wcstok(strBuffer, CRETURN);
iThisLineLength = wcslen(strThisLine);
// iLength is the number of wchars left in strBuffer, subtract it to exclude 0xA
iLength = iReadChar - iThisLineLength - 1;
// char 0xD is set to 0
strThisLine[iThisLineLength - 1] = 0;
wcscpy(strLine, strThisLine);
if(strBuffer[0] == CRETURN[0])
{
iLength -= 1;
}
}
else
{
return(FALSE);
}
}
}
else
{
return(FALSE);
}
return(TRUE);
}
///////////////////////////////////////////////////////////////////////////////
//
// GetBaseLanguge, get the base language struct to create a base tree, this
// function should only be called after a successful parsing
// of the answer file so that the base language is guanranteed
// to be there
//
// Parameters:
//
// none
//
// Return:
//
// a pointer to the base language structure
//
///////////////////////////////////////////////////////////////////////////////
PPATCH_LANGUAGE AnswerParser::GetBaseLanguage(VOID)
{
PPATCH_LANGUAGE pPointer = m_pHead;
while(pPointer)
{
if(pPointer->s_blnBase) break;
else pPointer = pPointer->s_pNext;
}
return(pPointer);
}
///////////////////////////////////////////////////////////////////////////////
//
// GetNextLanguage, get the next language structure that is ready to be
// matched and patched
//
// Parameters:
//
// none
//
// Return:
//
// a pointer to the next language structure
//
///////////////////////////////////////////////////////////////////////////////
PPATCH_LANGUAGE AnswerParser::GetNextLanguage(VOID)
{
static PPATCH_LANGUAGE pPointer = m_pHead;
PPATCH_LANGUAGE pReturn = NULL;
while(pPointer)
{
if(!pPointer->s_blnBase && pPointer->s_iComplete == LANGUAGE_COMPLETE)
{
pReturn = pPointer;
pPointer = pPointer->s_pNext;
break;
}
pPointer = pPointer->s_pNext;
}
return(pReturn);
}
///////////////////////////////////////////////////////////////////////////////
//
// CreateNewLanguage, create a new language struct to store information about
// a new language, and link it to the language struct list
//
// Parameters:
//
// pNode, this language struct, empty
// strBegin, the string "[something]"
// strEnd, the string starting at "]"
//
// Return:
//
// none
//
///////////////////////////////////////////////////////////////////////////////
VOID AnswerParser::CreateNewLanguage(IN PPATCH_LANGUAGE pNode,
IN WCHAR* strBegin,
IN WCHAR* strEnd)
{
wcsncpy(pNode->s_wszLanguage, strBegin + 1,
strEnd - strBegin - 1);
pNode->s_wszLanguage[strEnd - strBegin - 1] = 0;
pNode->s_iComplete += 1;
pNode->s_pNext = m_pHead;
m_pHead = pNode;
}