Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2241 lines
46 KiB

/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
sptxtfil.c
Abstract:
Routines to load and extract information from
setup text files.
Author:
Ted Miller (tedm) 4-Aug-1993
Revision History:
--*/
#include "spprecmp.h"
#pragma hdrstop
PVOID
ParseInfBuffer(
PWCHAR Buffer,
ULONG Size,
PULONG ErrorLine
);
NTSTATUS
SppWriteTextToFile(
IN PVOID Handle,
IN PWSTR String
);
NTSTATUS
SpLoadSetupTextFile(
IN PWCHAR Filename, OPTIONAL
IN PVOID Image, OPTIONAL
IN ULONG ImageSize, OPTIONAL
OUT PVOID *Handle,
OUT PULONG ErrorLine
)
/*++
Routine Description:
Load a setup text file into memory.
Arguments:
Filename - If specified, supplies full filename (in NT namespace)
of the file to be loaded. Oneof Image or Filename must be specified.
Image - If specified, supplies a pointer to an image of the file
already in memory. One of Image or Filename must be specified.
ImageSize - if Image is specified, then this parameter supplies the
size of the buffer pointed to by Image. Ignored otherwise.
Handle - receives handle to loaded file, which can be
used in subsequent calls to other text file services.
ErrorLine - receives line number of syntax error, if parsing fails.
Return Value:
STATUS_SUCCESS - file was read and parsed successfully.
In this case, Handle is filled in.
STATUS_UNSUCCESSFUL - syntax error in file. In this case, ErrorLine
is filled in.
STATUS_NO_MEMORY - unable to allocate memory while parsing.
STATUS_IN_PAGE_ERROR - i/o error while reading the file.
--*/
{
HANDLE hFile;
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING FilenameU;
OBJECT_ATTRIBUTES oa;
PWCHAR pText;
ULONG cbText;
HANDLE hSection;
PVOID UnmapAddress;
PWCHAR UniText = NULL;
BOOLEAN LoadFromFile;
//
// Argument validation -- one of Filename or Image must be specified,
// but not both.
//
ASSERT(!(Filename && Image));
ASSERT(Filename || Image);
LoadFromFile = (BOOLEAN)(Filename != NULL);
CLEAR_CLIENT_SCREEN();
if(LoadFromFile) {
SpDisplayStatusText(
SP_STAT_LOADING_SIF,
DEFAULT_STATUS_ATTRIBUTE,
wcsrchr(Filename,L'\\')+1
);
//
// Open the file.
//
RtlInitUnicodeString(&FilenameU,Filename);
InitializeObjectAttributes(&oa,&FilenameU,OBJ_CASE_INSENSITIVE,NULL,NULL);
Status = ZwCreateFile(
&hFile,
FILE_GENERIC_READ,
&oa,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
0,
NULL,
0
);
if(!NT_SUCCESS(Status)) {
KdPrint(("SETUP: SpLoadSetupTextFile: unable to open file %ws (%lx)\n",Filename,Status));
goto ltf0;
}
//
// Get the file size.
//
Status = SpGetFileSize(hFile,&cbText);
if(!NT_SUCCESS(Status)) {
goto ltf1;
}
//
// Map the file.
//
Status = SpMapEntireFile(hFile,&hSection,&pText,FALSE);
if(!NT_SUCCESS(Status)) {
goto ltf1;
}
UnmapAddress = pText;
} else {
SpDisplayStatusText(SP_STAT_PROCESSING_SIF,DEFAULT_STATUS_ATTRIBUTE);
pText = Image;
cbText = ImageSize;
}
//
// See if we think the file is Unicode. We think it's Unicode
// if it's even length and starts with the Unicode text marker.
//
try {
if((*pText == 0xfeff) && !(cbText & 1)) {
//
// Assume it's already unicode.
//
pText++;
cbText -= sizeof(WCHAR);
} else {
//
// It's not Unicode. Convert it from OEM to Unicode.
//
// Allocate a buffer large enough to hold the maximum
// unicode text. This max size occurs when
// every character is single-byte, and this size is
// equal to exactly double the size of the single-byte text.
//
if(UniText = SpMemAlloc(cbText*sizeof(WCHAR))) {
Status = RtlOemToUnicodeN(
UniText, // output: newly allocatd buffer
cbText * sizeof(WCHAR), // max size of output
&cbText, // receives # bytes in unicode text
(PUCHAR)pText, // input: oem text (mapped file)
cbText // size of input
);
if(NT_SUCCESS(Status)) {
pText = UniText; // Use newly converted Unicode text
}
} else {
Status = STATUS_NO_MEMORY;
}
}
} except(IN_PAGE_ERROR) {
Status = STATUS_IN_PAGE_ERROR;
}
//
// Process the file.
//
if(NT_SUCCESS(Status)) {
try {
if((*Handle = ParseInfBuffer(pText,cbText,ErrorLine)) == (PVOID)NULL) {
Status = STATUS_UNSUCCESSFUL;
} else {
Status = STATUS_SUCCESS;
}
} except(IN_PAGE_ERROR) {
Status = STATUS_IN_PAGE_ERROR;
// BUGBUG need to clean up!
}
}
//
// Free the unicode text buffer if we allocated it.
//
if(UniText) {
SpMemFree(UniText);
}
//
// Unmap the file.
//
//ltf2:
if(LoadFromFile) {
SpUnmapFile(hSection,UnmapAddress);
}
ltf1:
//
// Close the file.
//
if(LoadFromFile) {
ZwClose(hFile);
}
ltf0:
return(Status);
}
typedef struct _TEXTFILE_VALUE {
struct _TEXTFILE_VALUE *pNext;
PWCHAR pName;
} TEXTFILE_VALUE, *PTEXTFILE_VALUE;
typedef struct _TEXTFILE_LINE {
struct _TEXTFILE_LINE *pNext;
PWCHAR pName;
PTEXTFILE_VALUE pValue;
} TEXTFILE_LINE, *PTEXTFILE_LINE;
typedef struct _TEXTFILE_SECTION {
struct _TEXTFILE_SECTION *pNext;
PWCHAR pName;
PTEXTFILE_LINE pLine;
PTEXTFILE_LINE PreviouslyFoundLine;
} TEXTFILE_SECTION, *PTEXTFILE_SECTION;
typedef struct _TEXTFILE {
PTEXTFILE_SECTION pSection;
PTEXTFILE_SECTION PreviouslyFoundSection;
} TEXTFILE, *PTEXTFILE;
//
// DEFINES USED FOR THE PARSER INTERNALLY
//
//
// typedefs used
//
typedef enum _tokentype {
TOK_EOF,
TOK_EOL,
TOK_LBRACE,
TOK_RBRACE,
TOK_STRING,
TOK_EQUAL,
TOK_COMMA,
TOK_ERRPARSE,
TOK_ERRNOMEM
} TOKENTYPE, *PTOKENTTYPE;
typedef struct _token {
TOKENTYPE Type;
PWCHAR pValue;
} TOKEN, *PTOKEN;
//
// Routine defines
//
NTSTATUS
SpAppendSection(
IN PWCHAR pSectionName
);
NTSTATUS
SpAppendLine(
IN PWCHAR pLineKey
);
NTSTATUS
SpAppendValue(
IN PWCHAR pValueString
);
TOKEN
SpGetToken(
IN OUT PWCHAR *Stream,
IN PWCHAR MaxStream
);
// what follows was alinf.c
//
// Internal Routine Declarations for freeing inf structure members
//
VOID
FreeSectionList (
IN PTEXTFILE_SECTION pSection
);
VOID
FreeLineList (
IN PTEXTFILE_LINE pLine
);
VOID
FreeValueList (
IN PTEXTFILE_VALUE pValue
);
//
// Internal Routine declarations for searching in the INF structures
//
PTEXTFILE_VALUE
SearchValueInLine(
IN PTEXTFILE_LINE pLine,
IN ULONG ValueIndex
);
PTEXTFILE_LINE
SearchLineInSectionByKey(
IN PTEXTFILE_SECTION pSection,
IN PWCHAR Key
);
PTEXTFILE_LINE
SearchLineInSectionByIndex(
IN PTEXTFILE_SECTION pSection,
IN ULONG LineIndex
);
PTEXTFILE_SECTION
SearchSectionByName(
IN PTEXTFILE pINF,
IN PWCHAR SectionName
);
PWCHAR
SpProcessForStringSubs(
IN PTEXTFILE pInf,
IN PWCHAR String
);
PVOID
SpNewSetupTextFile(
VOID
)
{
PTEXTFILE pFile;
pFile = SpMemAlloc(sizeof(TEXTFILE));
RtlZeroMemory(pFile,sizeof(TEXTFILE));
return(pFile);
}
VOID
SpAddLineToSection(
IN PVOID Handle,
IN PWSTR SectionName,
IN PWSTR KeyName, OPTIONAL
IN PWSTR Values[],
IN ULONG ValueCount
)
{
PTEXTFILE_SECTION pSection;
PTEXTFILE_LINE pLine;
PTEXTFILE_VALUE pValue,PrevVal;
PTEXTFILE pFile;
ULONG v;
pFile = (PTEXTFILE)Handle;
//
// If the section doesn't exist, create it.
//
pSection = SearchSectionByName(pFile,SectionName);
if(!pSection) {
pSection = SpMemAlloc(sizeof(TEXTFILE_SECTION));
RtlZeroMemory(pSection,sizeof(TEXTFILE_SECTION));
pSection->pNext = pFile->pSection;
pFile->pSection = pSection;
pSection->pName = SpDupStringW(SectionName);
}
//
// Create a structure for the line in the section.
//
pLine = SpMemAlloc(sizeof(TEXTFILE_LINE));
RtlZeroMemory(pLine,sizeof(TEXTFILE_LINE));
pLine->pNext = pSection->pLine;
pSection->pLine = pLine;
if(KeyName) {
pLine->pName = SpDupStringW(KeyName);
}
//
// Create value entries for each specified value.
// These must be kept in the order they were specified.
//
for(v=0; v<ValueCount; v++) {
pValue = SpMemAlloc(sizeof(TEXTFILE_VALUE));
RtlZeroMemory(pValue,sizeof(TEXTFILE_VALUE));
pValue->pName = SpDupStringW(Values[v]);
if(v == 0) {
pLine->pValue = pValue;
} else {
PrevVal->pNext = pValue;
}
PrevVal = pValue;
}
}
NTSTATUS
SpWriteSetupTextFile(
IN PVOID Handle,
IN PWSTR FilenamePart1,
IN PWSTR FilenamePart2, OPTIONAL
IN PWSTR FilenamePart3 OPTIONAL
)
{
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES Obja;
NTSTATUS Status;
HANDLE hFile;
PWSTR p;
PTEXTFILE pFile;
PTEXTFILE_SECTION pSection;
PTEXTFILE_LINE pLine;
PTEXTFILE_VALUE pValue;
//
// Do this because it takes care of read-only attributes, etc.
// Do it before starting to use TemporaryBuffer.
//
SpDeleteFile(FilenamePart1,FilenamePart2,FilenamePart3);
p = (PWSTR)TemporaryBuffer;
wcscpy(p,FilenamePart1);
if(FilenamePart2) {
SpConcatenatePaths(p,FilenamePart2);
}
if(FilenamePart3) {
SpConcatenatePaths(p,FilenamePart3);
}
INIT_OBJA(&Obja,&UnicodeString, p);
Status = ZwCreateFile(
&hFile,
FILE_ALL_ACCESS,
&Obja,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
0, // no sharing
FILE_OVERWRITE_IF,
FILE_SYNCHRONOUS_IO_ALERT | FILE_NON_DIRECTORY_FILE,
NULL,
0
);
if(!NT_SUCCESS(Status)) {
KdPrint(("SETUP: Unable to open .sif file %ws (%lx)\n",p, Status ));
return(Status);
}
//
// Write out the file contents.
//
pFile = (PTEXTFILE)Handle;
for(pSection=pFile->pSection; pSection; pSection=pSection->pNext) {
swprintf(p,L"[%s]\r\n",pSection->pName);
Status = SppWriteTextToFile( hFile, p );
if(!NT_SUCCESS(Status)) {
KdPrint(("SETUP: SppWriteTextToFile() failed. Status = %lx \n", Status));
goto wtf1;
}
for(pLine=pSection->pLine; pLine; pLine=pLine->pNext) {
wcscpy( p, L"" );
//
// Write the keyname if there is one.
//
if(pLine->pName) {
BOOLEAN AddDoubleQuotes;
AddDoubleQuotes = (wcschr(pLine->pName, (WCHAR)' ') == NULL)? FALSE : TRUE;
if( AddDoubleQuotes ) {
wcscat(p,L"\"");
}
wcscat(p,pLine->pName);
if( AddDoubleQuotes ) {
wcscat(p,L"\"");
}
wcscat(p,L" = ");
}
for(pValue=pLine->pValue; pValue; pValue=pValue->pNext) {
if(pValue != pLine->pValue) {
wcscat(p,L",");
}
wcscat(p,L"\"");
wcscat(p,pValue->pName);
wcscat(p,L"\"");
}
if(!pLine->pValue) {
wcscat(p,L"\"\"");
}
wcscat(p,L"\r\n");
Status = SppWriteTextToFile( hFile, p );
if(!NT_SUCCESS(Status)) {
KdPrint(("SETUP: SppWriteTextToFile() failed. Status = %lx \n", Status));
goto wtf1;
}
}
}
wtf1:
ZwClose(hFile);
return(Status);
}
BOOLEAN
SpFreeTextFile(
IN PVOID Handle
)
/*++
Routine Description:
Frees a text file.
Arguments:
Return Value:
TRUE.
--*/
{
PTEXTFILE pINF;
ASSERT(Handle);
//
// cast the buffer into an INF structure
//
pINF = (PTEXTFILE)Handle;
FreeSectionList(pINF->pSection);
//
// free the inf structure too
//
SpMemFree(pINF);
return(TRUE);
}
VOID
FreeSectionList (
IN PTEXTFILE_SECTION pSection
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PTEXTFILE_SECTION Next;
while(pSection) {
Next = pSection->pNext;
FreeLineList(pSection->pLine);
if(pSection->pName) {
SpMemFree(pSection->pName);
}
SpMemFree(pSection);
pSection = Next;
}
}
VOID
FreeLineList (
IN PTEXTFILE_LINE pLine
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PTEXTFILE_LINE Next;
while(pLine) {
Next = pLine->pNext;
FreeValueList(pLine->pValue);
if(pLine->pName) {
SpMemFree(pLine->pName);
}
SpMemFree(pLine);
pLine = Next;
}
}
VOID
FreeValueList (
IN PTEXTFILE_VALUE pValue
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PTEXTFILE_VALUE Next;
while(pValue) {
Next = pValue->pNext;
if(pValue->pName) {
SpMemFree(pValue->pName);
}
SpMemFree(pValue);
pValue = Next;
}
}
BOOLEAN
SpSearchTextFileSection (
IN PVOID Handle,
IN PWCHAR SectionName
)
/*++
Routine Description:
Searches for the existance of a particular section.
Arguments:
Return Value:
--*/
{
return((BOOLEAN)(SearchSectionByName((PTEXTFILE)Handle,SectionName) != NULL));
}
PWCHAR
SpGetSectionLineIndex(
IN PVOID Handle,
IN PWCHAR SectionName,
IN ULONG LineIndex,
IN ULONG ValueIndex
)
/*++
Routine Description:
Given section name, line number and index return the value.
Arguments:
Return Value:
--*/
{
PTEXTFILE_SECTION pSection;
PTEXTFILE_LINE pLine;
PTEXTFILE_VALUE pValue;
if((pSection = SearchSectionByName((PTEXTFILE)Handle,SectionName)) == NULL) {
return(NULL);
}
if((pLine = SearchLineInSectionByIndex(pSection,LineIndex)) == NULL) {
return(NULL);
}
if((pValue = SearchValueInLine(pLine,ValueIndex)) == NULL) {
return(NULL);
}
return(SpProcessForStringSubs(Handle,pValue->pName));
}
BOOLEAN
SpGetSectionKeyExists (
IN PVOID Handle,
IN PWCHAR SectionName,
IN PWCHAR Key
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PTEXTFILE_SECTION pSection;
if((pSection = SearchSectionByName((PTEXTFILE)Handle,SectionName)) == NULL) {
return(FALSE);
}
if(SearchLineInSectionByKey(pSection,Key) == NULL) {
return(FALSE);
}
return(TRUE);
}
PWCHAR
SpGetKeyName(
IN PVOID Handle,
IN PWCHAR SectionName,
IN ULONG LineIndex
)
{
PTEXTFILE_SECTION pSection;
PTEXTFILE_LINE pLine;
pSection = SearchSectionByName((PTEXTFILE)Handle,SectionName);
if(pSection == NULL) {
return(NULL);
}
pLine = SearchLineInSectionByIndex(pSection,LineIndex);
if(pLine == NULL) {
return(NULL);
}
return(pLine->pName);
}
PWCHAR
SpGetSectionKeyIndex (
IN PVOID Handle,
IN PWCHAR SectionName,
IN PWCHAR Key,
IN ULONG ValueIndex
)
/*++
Routine Description:
Given section name, key and index return the value
Arguments:
Return Value:
--*/
{
PTEXTFILE_SECTION pSection;
PTEXTFILE_LINE pLine;
PTEXTFILE_VALUE pValue;
if((pSection = SearchSectionByName((PTEXTFILE)Handle,SectionName)) == NULL) {
return(NULL);
}
if((pLine = SearchLineInSectionByKey(pSection,Key)) == NULL) {
return(NULL);
}
if((pValue = SearchValueInLine(pLine,ValueIndex)) == NULL) {
return(NULL);
}
return(SpProcessForStringSubs(Handle,pValue->pName));
}
ULONG
SpCountLinesInSection(
IN PVOID Handle,
IN PWCHAR SectionName
)
{
PTEXTFILE_SECTION pSection;
PTEXTFILE_LINE pLine;
ULONG Count;
if((pSection = SearchSectionByName((PTEXTFILE)Handle,SectionName)) == NULL) {
return(0);
}
for(pLine = pSection->pLine, Count = 0;
pLine;
pLine = pLine->pNext, Count++
);
return(Count);
}
PTEXTFILE_VALUE
SearchValueInLine(
IN PTEXTFILE_LINE pLine,
IN ULONG ValueIndex
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PTEXTFILE_VALUE pValue;
ULONG i;
if(pLine == NULL) {
return(NULL);
}
pValue = pLine->pValue;
for(i=0; (i<ValueIndex) && (pValue=pValue->pNext); i++) {
;
}
return pValue;
}
PTEXTFILE_LINE
SearchLineInSectionByKey(
IN PTEXTFILE_SECTION pSection,
IN PWCHAR Key
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PTEXTFILE_LINE pLine,pFirstSearchedLine;
//
// Start at the line where we left off in the last search.
//
pLine = pFirstSearchedLine = pSection->PreviouslyFoundLine;
while(pLine && ((pLine->pName == NULL) || _wcsicmp(pLine->pName,Key))) {
pLine = pLine->pNext;
}
//
// If we haven't found it yet, wrap around to the beginning of the section.
//
if(!pLine) {
pLine = pSection->pLine;
while(pLine && (pLine != pFirstSearchedLine)) {
if(pLine->pName && !_wcsicmp(pLine->pName,Key)) {
break;
}
pLine = pLine->pNext;
}
//
// If we wrapped around to the first line we searched,
// then we didn't find the line we're looking for.
//
if(pLine == pFirstSearchedLine) {
pLine = NULL;
}
}
//
// If we found the line, save it away so we can resume the
// search from that point the next time we are called.
//
if(pLine) {
pSection->PreviouslyFoundLine = pLine;
}
return pLine;
}
PTEXTFILE_LINE
SearchLineInSectionByIndex(
IN PTEXTFILE_SECTION pSection,
IN ULONG LineIndex
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PTEXTFILE_LINE pLine;
ULONG i;
//
// Validate the parameters passed in
//
if(pSection == NULL) {
return(NULL);
}
//
// find the start of the line list in the section passed in
//
pLine = pSection->pLine;
//
// traverse down the current line list to the LineIndex th line
//
for(i=0; (i<LineIndex) && (pLine = pLine->pNext); i++) {
;
}
//
// return the Line found
//
return pLine;
}
PTEXTFILE_SECTION
SearchSectionByName(
IN PTEXTFILE pINF,
IN PWCHAR SectionName
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PTEXTFILE_SECTION pSection,pFirstSearchedSection;
//
// find the section list
//
pSection = pFirstSearchedSection = pINF->PreviouslyFoundSection;
//
// traverse down the section list searching each section for the section
// name mentioned
//
while(pSection && _wcsicmp(pSection->pName,SectionName)) {
pSection = pSection->pNext;
}
//
// If we didn't find it so far, search the beginning of the file.
//
if(!pSection) {
pSection = pINF->pSection;
while(pSection && (pSection != pFirstSearchedSection)) {
if(pSection->pName && !_wcsicmp(pSection->pName,SectionName)) {
break;
}
pSection = pSection->pNext;
}
//
// If we wrapped around to the first section we searched,
// then we didn't find the section we're looking for.
//
if(pSection == pFirstSearchedSection) {
pSection = NULL;
}
}
if(pSection) {
pINF->PreviouslyFoundSection = pSection;
}
//
// return the section at which we stopped (either NULL or the section
// which was found).
//
return pSection;
}
PWCHAR
SpProcessForStringSubs(
IN PTEXTFILE pInf,
IN PWCHAR String
)
{
UINT Len;
PWCHAR ReturnString;
PTEXTFILE_SECTION pSection;
PTEXTFILE_LINE pLine;
//
// Assume no substitution necessary.
//
ReturnString = String;
//
// If it starts and end with % then look it up in the
// strings section. Note the initial check before doing a
// wcslen, to preserve performance in the 99% case where
// there is no substitution.
//
if((String[0] == L'%') && ((Len = wcslen(String)) > 2) && (String[Len-1] == L'%')) {
for(pSection = pInf->pSection; pSection; pSection=pSection->pNext) {
if(pSection->pName && !_wcsicmp(pSection->pName,L"Strings")) {
break;
}
}
if(pSection) {
for(pLine = pSection->pLine; pLine; pLine=pLine->pNext) {
if(pLine->pName
&& !_wcsnicmp(pLine->pName,String+1,Len-2)
&& (pLine->pName[Len-2] == 0)) {
break;
}
}
if(pLine && pLine->pValue && pLine->pValue->pName) {
ReturnString = pLine->pValue->pName;
}
}
}
return(ReturnString);
}
//
// Globals used to make building the lists easier
//
PTEXTFILE pINF;
PTEXTFILE_SECTION pSectionRecord;
PTEXTFILE_LINE pLineRecord;
PTEXTFILE_VALUE pValueRecord;
//
// Globals used by the token parser
//
// string terminators are the whitespace characters (isspace: space, tab,
// linefeed, formfeed, vertical tab, carriage return) or the chars given below
WCHAR StringTerminators[] = L"[]=,\t \"\n\f\v\r";
PWCHAR QStringTerminators = StringTerminators+6;
//
// Main parser routine
//
PVOID
ParseInfBuffer(
PWCHAR Buffer,
ULONG Size,
PULONG ErrorLine
)
/*++
Routine Description:
Given a character buffer containing the INF file, this routine parses
the INF into an internal form with Section records, Line records and
Value records.
Arguments:
Buffer - contains to ptr to a buffer containing the INF file
Size - contains the size of the buffer in bytes.
ErrorLine - if a parse error occurs, this variable receives the line
number of the line containing the error.
Return Value:
PVOID - INF handle ptr to be used in subsequent INF calls.
--*/
{
PWCHAR Stream, MaxStream, pchSectionName = NULL, pchValue = NULL;
ULONG State, InfLine;
TOKEN Token;
BOOLEAN Done;
BOOLEAN Error;
NTSTATUS ErrorCode;
//
// Initialise the globals
//
pINF = NULL;
pSectionRecord = NULL;
pLineRecord = NULL;
pValueRecord = NULL;
//
// Get INF record
//
if((pINF = SpMemAlloc(sizeof(TEXTFILE))) == NULL) {
return NULL;
}
pINF->pSection = NULL;
//
// Set initial state
//
State = 1;
InfLine = 1;
Stream = Buffer;
MaxStream = Buffer + (Size/sizeof(WCHAR));
Done = FALSE;
Error = FALSE;
//
// Enter token processing loop
//
while (!Done) {
Token = SpGetToken(&Stream, MaxStream);
switch (State) {
//
// STATE1: Start of file, this state remains till first
// section is found
// Valid Tokens: TOK_EOL, TOK_EOF, TOK_LBRACE, TOK_STRING
// TOK_STRING occurs when reading dblspace.ini
//
case 1:
switch (Token.Type) {
case TOK_EOL:
break;
case TOK_EOF:
Done = TRUE;
break;
case TOK_LBRACE:
State = 2;
break;
case TOK_STRING:
pchSectionName = SpMemAlloc( ( wcslen( DBLSPACE_SECTION ) + 1 )*sizeof( WCHAR ) );
if( pchSectionName == NULL ) {
Error = Done = TRUE;
ErrorCode = STATUS_NO_MEMORY;
}
wcscpy( pchSectionName, DBLSPACE_SECTION );
pchValue = Token.pValue;
if ((ErrorCode = SpAppendSection(pchSectionName)) != STATUS_SUCCESS) {
Error = Done = TRUE;
ErrorCode = STATUS_UNSUCCESSFUL;
} else {
pchSectionName = NULL;
State = 6;
}
break;
default:
Error = Done = TRUE;
ErrorCode = STATUS_UNSUCCESSFUL;
break;
}
break;
//
// STATE 2: Section LBRACE has been received, expecting STRING or RBRACE
//
// Valid Tokens: TOK_STRING, TOK_RBRACE
//
case 2:
switch (Token.Type) {
case TOK_STRING:
State = 3;
pchSectionName = Token.pValue;
break;
case TOK_RBRACE:
State = 4;
pchSectionName = SpDupStringW(L"");;
break;
default:
Error = Done = TRUE;
ErrorCode = STATUS_UNSUCCESSFUL;
break;
}
break;
//
// STATE 3: Section Name received, expecting RBRACE
//
// Valid Tokens: TOK_RBRACE
//
case 3:
switch (Token.Type) {
case TOK_RBRACE:
State = 4;
break;
default:
Error = Done = TRUE;
ErrorCode = STATUS_UNSUCCESSFUL;
break;
}
break;
//
// STATE 4: Section Definition Complete, expecting EOL
//
// Valid Tokens: TOK_EOL, TOK_EOF
//
case 4:
switch (Token.Type) {
case TOK_EOL:
if ((ErrorCode = SpAppendSection(pchSectionName)) != STATUS_SUCCESS)
Error = Done = TRUE;
else {
pchSectionName = NULL;
State = 5;
}
break;
case TOK_EOF:
if ((ErrorCode = SpAppendSection(pchSectionName)) != STATUS_SUCCESS)
Error = Done = TRUE;
else {
pchSectionName = NULL;
Done = TRUE;
}
break;
default:
Error = Done = TRUE;
ErrorCode = STATUS_UNSUCCESSFUL;
break;
}
break;
//
// STATE 5: Expecting Section Lines
//
// Valid Tokens: TOK_EOL, TOK_EOF, TOK_STRING, TOK_LBRACE
//
case 5:
switch (Token.Type) {
case TOK_EOL:
break;
case TOK_EOF:
Done = TRUE;
break;
case TOK_STRING:
pchValue = Token.pValue;
State = 6;
break;
case TOK_LBRACE:
State = 2;
break;
default:
Error = Done = TRUE;
ErrorCode = STATUS_UNSUCCESSFUL;
break;
}
break;
//
// STATE 6: String returned, not sure whether it is key or value
//
// Valid Tokens: TOK_EOL, TOK_EOF, TOK_COMMA, TOK_EQUAL
//
case 6:
switch (Token.Type) {
case TOK_EOL:
if ( (ErrorCode = SpAppendLine(NULL)) != STATUS_SUCCESS ||
(ErrorCode = SpAppendValue(pchValue)) !=STATUS_SUCCESS )
Error = Done = TRUE;
else {
pchValue = NULL;
State = 5;
}
break;
case TOK_EOF:
if ( (ErrorCode = SpAppendLine(NULL)) != STATUS_SUCCESS ||
(ErrorCode = SpAppendValue(pchValue)) !=STATUS_SUCCESS )
Error = Done = TRUE;
else {
pchValue = NULL;
Done = TRUE;
}
break;
case TOK_COMMA:
if ( (ErrorCode = SpAppendLine(NULL)) != STATUS_SUCCESS ||
(ErrorCode = SpAppendValue(pchValue)) !=STATUS_SUCCESS )
Error = Done = TRUE;
else {
pchValue = NULL;
State = 7;
}
break;
case TOK_EQUAL:
if ( (ErrorCode = SpAppendLine(pchValue)) != STATUS_SUCCESS)
Error = Done = TRUE;
else {
pchValue = NULL;
State = 8;
}
break;
default:
Error = Done = TRUE;
ErrorCode = STATUS_UNSUCCESSFUL;
break;
}
break;
//
// STATE 7: Comma received, Expecting another string
// Also allow a comma to indicate an empty value ie x = 1,,2
//
// Valid Tokens: TOK_STRING TOK_COMMA
//
case 7:
switch (Token.Type) {
case TOK_COMMA:
Token.pValue = SpDupStringW(L"");
if ((ErrorCode = SpAppendValue(Token.pValue)) != STATUS_SUCCESS)
Error = Done = TRUE;
//
// State stays at 7 because we are expecting a string
//
break;
case TOK_STRING:
if ((ErrorCode = SpAppendValue(Token.pValue)) != STATUS_SUCCESS)
Error = Done = TRUE;
else
State = 9;
break;
default:
Error = Done = TRUE;
ErrorCode = STATUS_UNSUCCESSFUL;
break;
}
break;
//
// STATE 8: Equal received, Expecting another string
// If none, assume there is a single empty string on the RHS
//
// Valid Tokens: TOK_STRING, TOK_EOL, TOK_EOF
//
case 8:
switch (Token.Type) {
case TOK_EOF:
Token.pValue = SpDupStringW(L"");
if((ErrorCode = SpAppendValue(Token.pValue)) != STATUS_SUCCESS) {
Error = TRUE;
}
Done = TRUE;
break;
case TOK_EOL:
Token.pValue = SpDupStringW(L"");
if((ErrorCode = SpAppendValue(Token.pValue)) != STATUS_SUCCESS) {
Error = TRUE;
Done = TRUE;
} else {
State = 5;
}
break;
case TOK_STRING:
if ((ErrorCode = SpAppendValue(Token.pValue)) != STATUS_SUCCESS)
Error = Done = TRUE;
else
State = 9;
break;
default:
Error = Done = TRUE;
ErrorCode = STATUS_UNSUCCESSFUL;
break;
}
break;
//
// STATE 9: String received after equal, value string
//
// Valid Tokens: TOK_EOL, TOK_EOF, TOK_COMMA
//
case 9:
switch (Token.Type) {
case TOK_EOL:
State = 5;
break;
case TOK_EOF:
Done = TRUE;
break;
case TOK_COMMA:
State = 7;
break;
default:
Error = Done = TRUE;
ErrorCode = STATUS_UNSUCCESSFUL;
break;
}
break;
//
// STATE 10: Value string definitely received
//
// Valid Tokens: TOK_EOL, TOK_EOF, TOK_COMMA
//
case 10:
switch (Token.Type) {
case TOK_EOL:
State =5;
break;
case TOK_EOF:
Done = TRUE;
break;
case TOK_COMMA:
State = 7;
break;
default:
Error = Done = TRUE;
ErrorCode = STATUS_UNSUCCESSFUL;
break;
}
break;
default:
Error = Done = TRUE;
ErrorCode = STATUS_UNSUCCESSFUL;
break;
} // end switch(State)
if (Error) {
switch (ErrorCode) {
case STATUS_UNSUCCESSFUL:
*ErrorLine = InfLine;
break;
case STATUS_NO_MEMORY:
//SpxOutOfMemory();
break;
default:
break;
}
SpFreeTextFile(pINF);
if(pchSectionName) {
SpMemFree(pchSectionName);
}
if(pchValue) {
SpMemFree(pchValue);
}
pINF = NULL;
}
else {
//
// Keep track of line numbers so that we can display Errors
//
if(Token.Type == TOK_EOL) {
InfLine++;
}
}
} // End while
if(pINF) {
PTEXTFILE_SECTION p;
pINF->PreviouslyFoundSection = pINF->pSection;
for(p=pINF->pSection; p; p=p->pNext) {
p->PreviouslyFoundLine = p->pLine;
}
}
return(pINF);
}
NTSTATUS
SpAppendSection(
IN PWCHAR pSectionName
)
/*++
Routine Description:
This appends a new section to the section list in the current INF.
All further lines and values pertain to this new section, so it resets
the line list and value lists too.
Arguments:
pSectionName - Name of the new section. ( [SectionName] )
Return Value:
STATUS_SUCCESS if successful.
STATUS_NO_MEMORY if memory allocation failed.
STATUS_UNSUCCESSFUL if invalid parameters passed in or the INF buffer not
initialised
--*/
{
PTEXTFILE_SECTION pNewSection;
//
// Check to see if INF initialised and the parameter passed in is valid
//
ASSERT(pINF);
ASSERT(pSectionName);
if((pINF == NULL) || (pSectionName == NULL)) {
return STATUS_UNSUCCESSFUL;
}
//
// See if we already have a section by this name. If so we want
// to merge sections.
//
for(pNewSection=pINF->pSection; pNewSection; pNewSection=pNewSection->pNext) {
if(pNewSection->pName && !_wcsicmp(pNewSection->pName,pSectionName)) {
break;
}
}
if(pNewSection) {
//
// Set pLineRecord to point to the list line currently in the section.
//
for(pLineRecord = pNewSection->pLine;
pLineRecord && pLineRecord->pNext;
pLineRecord = pLineRecord->pNext)
;
} else {
//
// Allocate memory for the new section
//
if((pNewSection = SpMemAlloc(sizeof(TEXTFILE_SECTION))) == NULL) {
return STATUS_NO_MEMORY;
}
//
// initialise the new section
//
RtlZeroMemory(pNewSection,sizeof(TEXTFILE_SECTION));
pNewSection->pName = pSectionName;
//
// link it in
//
pNewSection->pNext = pINF->pSection;
pINF->pSection = pNewSection;
//
// reset the current line record
//
pLineRecord = NULL;
}
pSectionRecord = pNewSection;
pValueRecord = NULL;
return STATUS_SUCCESS;
}
NTSTATUS
SpAppendLine(
IN PWCHAR pLineKey
)
/*++
Routine Description:
This appends a new line to the line list in the current section.
All further values pertain to this new line, so it resets
the value list too.
Arguments:
pLineKey - Key to be used for the current line, this could be NULL.
Return Value:
STATUS_SUCCESS if successful.
STATUS_NO_MEMORY if memory allocation failed.
STATUS_UNSUCCESSFUL if invalid parameters passed in or current section not
initialised
--*/
{
PTEXTFILE_LINE pNewLine;
//
// Check to see if current section initialised
//
ASSERT(pSectionRecord);
if(pSectionRecord == NULL) {
return STATUS_UNSUCCESSFUL;
}
//
// Allocate memory for the new Line
//
if((pNewLine = SpMemAlloc(sizeof(TEXTFILE_LINE))) == NULL) {
return STATUS_NO_MEMORY;
}
//
// Link it in
//
pNewLine->pNext = NULL;
pNewLine->pValue = NULL;
pNewLine->pName = pLineKey;
if (pLineRecord == NULL) {
pSectionRecord->pLine = pNewLine;
} else {
pLineRecord->pNext = pNewLine;
}
pLineRecord = pNewLine;
//
// Reset the current value record
//
pValueRecord = NULL;
return STATUS_SUCCESS;
}
NTSTATUS
SpAppendValue(
IN PWCHAR pValueString
)
/*++
Routine Description:
This appends a new value to the value list in the current line.
Arguments:
pValueString - The value string to be added.
Return Value:
STATUS_SUCCESS if successful.
STATUS_NO_MEMORY if memory allocation failed.
STATUS_UNSUCCESSFUL if invalid parameters passed in or current line not
initialised.
--*/
{
PTEXTFILE_VALUE pNewValue;
//
// Check to see if current line record has been initialised and
// the parameter passed in is valid
//
ASSERT(pLineRecord);
ASSERT(pValueString);
if((pLineRecord == NULL) || (pValueString == NULL)) {
return STATUS_UNSUCCESSFUL;
}
//
// Allocate memory for the new value record
//
if((pNewValue = SpMemAlloc(sizeof(TEXTFILE_VALUE))) == NULL) {
return STATUS_NO_MEMORY;
}
//
// Link it in.
//
pNewValue->pNext = NULL;
pNewValue->pName = pValueString;
if (pValueRecord == NULL) {
pLineRecord->pValue = pNewValue;
} else {
pValueRecord->pNext = pNewValue;
}
pValueRecord = pNewValue;
return STATUS_SUCCESS;
}
TOKEN
SpGetToken(
IN OUT PWCHAR *Stream,
IN PWCHAR MaxStream
)
/*++
Routine Description:
This function returns the Next token from the configuration stream.
Arguments:
Stream - Supplies the address of the configuration stream. Returns
the address of where to start looking for tokens within the
stream.
MaxStream - Supplies the address of the last character in the stream.
Return Value:
TOKEN - Returns the next token
--*/
{
PWCHAR pch, pchStart, pchNew;
ULONG Length;
TOKEN Token;
//
// Skip whitespace (except for eol)
//
pch = *Stream;
while((pch < MaxStream) && (*pch != '\n') && SpIsSpace(*pch)) {
pch++;
}
//
// Check for comments and remove them
//
if((pch < MaxStream) && ((*pch == L';') || (*pch == L'#'))) {
while((pch < MaxStream) && (*pch != L'\n')) {
pch++;
}
}
//
// Check to see if EOF has been reached, set the token to the right
// value
//
if((pch >= MaxStream) || (*pch == 26)) {
*Stream = pch;
Token.Type = TOK_EOF;
Token.pValue = NULL;
return Token;
}
switch (*pch) {
case L'[' :
pch++;
Token.Type = TOK_LBRACE;
Token.pValue = NULL;
break;
case L']' :
pch++;
Token.Type = TOK_RBRACE;
Token.pValue = NULL;
break;
case L'=' :
pch++;
Token.Type = TOK_EQUAL;
Token.pValue = NULL;
break;
case L',' :
pch++;
Token.Type = TOK_COMMA;
Token.pValue = NULL;
break;
case L'\n' :
pch++;
Token.Type = TOK_EOL;
Token.pValue = NULL;
break;
case L'\"':
pch++;
//
// determine quoted string
//
pchStart = pch;
while((pch < MaxStream) && (wcschr(QStringTerminators,*pch) == NULL)) {
pch++;
}
if((pch >= MaxStream) || (*pch != L'\"')) {
Token.Type = TOK_ERRPARSE;
Token.pValue = NULL;
}
else {
Length = ((PUCHAR)pch - (PUCHAR)pchStart)/sizeof(WCHAR);
if ((pchNew = SpMemAlloc((Length + 1) * sizeof(WCHAR))) == NULL) {
Token.Type = TOK_ERRNOMEM;
Token.pValue = NULL;
}
else {
if (Length != 0) { // Null quoted strings are allowed
wcsncpy(pchNew, pchStart, Length);
}
pchNew[Length] = 0;
Token.Type = TOK_STRING;
Token.pValue = pchNew;
}
pch++; // advance past the quote
}
break;
default:
//
// determine regular string
//
pchStart = pch;
while((pch < MaxStream) && (wcschr(StringTerminators,*pch) == NULL)) {
pch++;
}
if (pch == pchStart) {
pch++;
Token.Type = TOK_ERRPARSE;
Token.pValue = NULL;
}
else {
Length = ((PUCHAR)pch - (PUCHAR)pchStart)/sizeof(WCHAR);
if((pchNew = SpMemAlloc((Length + 1) * sizeof(WCHAR))) == NULL) {
Token.Type = TOK_ERRNOMEM;
Token.pValue = NULL;
}
else {
wcsncpy(pchNew, pchStart, Length);
pchNew[Length] = 0;
Token.Type = TOK_STRING;
Token.pValue = pchNew;
}
}
break;
}
*Stream = pch;
return (Token);
}
#if DBG
VOID
pSpDumpTextFileInternals(
IN PVOID Handle
)
{
PTEXTFILE pInf = Handle;
PTEXTFILE_SECTION pSection;
PTEXTFILE_LINE pLine;
PTEXTFILE_VALUE pValue;
for(pSection = pInf->pSection; pSection; pSection = pSection->pNext) {
KdPrint(("Section: [%ws]\r\n",pSection->pName));
for(pLine = pSection->pLine; pLine; pLine = pLine->pNext) {
KdPrint((" [%ws] = ",pLine->pName ? pLine->pName : L"(none)"));
for(pValue = pLine->pValue; pValue; pValue = pValue->pNext) {
KdPrint(("[%ws] ",pValue->pName));
}
KdPrint(("\n"));
}
}
}
#endif
PWSTR
SpGetKeyNameByValue(
IN PVOID Inf,
IN PWSTR SectionName,
IN PWSTR Value
)
/*++
Routine Description:
Determines the key name of a given value in a given section.
Arguments:
Inf - Handle to an inf file (txtsetup.sif or winnt.sif).
SectionName - Supplies the name of the section
Value - Supplies the string to be matched (eg. "Digital DECpc AXP 150")
Return Value:
NULL - No match was found.
PWSTR - Pointer to the canonical shortname of the component.
--*/
{
ULONG i;
PWSTR SearchName;
//
// If this is not an OEM component, then enumerate the entries in the
// section in txtsetup.sif
//
for (i=0;;i++) {
SearchName = SpGetSectionLineIndex(Inf,
SectionName,
i,
0);
if (SearchName==NULL) {
//
// we have enumerated the entire section without finding a
// match, return failure.
//
return(NULL);
}
if (_wcsicmp(Value, SearchName) == 0) {
//
// we have a match
//
break;
}
}
//
// i is the index into the section of the short machine name
//
return(SpGetKeyName(Inf,
SectionName,
i));
}
ULONG
SpCountSectionsInFile(
IN PVOID Handle
)
{
PTEXTFILE_SECTION pSection;
PTEXTFILE pFile;
ULONG Count;
pFile = (PTEXTFILE)Handle;
for(pSection=pFile->pSection, Count = 0;
pSection;
pSection = pSection->pNext, Count++
);
return(Count);
}
PWSTR
SpGetSectionName(
IN PVOID Handle,
IN ULONG Index
)
{
PTEXTFILE_SECTION pSection;
PTEXTFILE pFile;
ULONG Count;
PWSTR SectionName;
pFile = (PTEXTFILE)Handle;
for(pSection=pFile->pSection, Count = 0;
pSection && (Count < Index);
pSection = pSection->pNext, Count++
);
return( (pSection != NULL)? pSection->pName : NULL );
}
NTSTATUS
SppWriteTextToFile(
IN PVOID Handle,
IN PWSTR String
)
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
PCHAR OemText;
OemText = SpToOem( String );
Status = ZwWriteFile( Handle,
NULL,
NULL,
NULL,
&IoStatusBlock,
OemText,
strlen( OemText ),
NULL,
NULL );
SpMemFree( OemText );
return( Status );
}