mirror of https://github.com/lianthony/NT4.0
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
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 );
|
|
}
|