/*++ Copyright (c) Microsoft Corporation. All rights reserved. Module Name: parseini.c Abstract: This modules contains routines to parse an inf file. This is based on the code from the osloader. All indices are zero based. Author: Santosh Jodh (santoshj) 08-Aug-1998 Environment: Kernel mode. Revision History: --*/ #include "cmp.h" #include "string.h" #include "ctype.h" #include "stdlib.h" #include "parseini.h" typedef struct _value VALUE, *PVALUE; typedef struct _line LINE, *PLINE; typedef struct _section SECTION, *PSECTION; typedef struct _inf INF, *PINF; typedef struct _cm_token CM_TOKEN,*PCM_TOKEN; typedef enum _tokentype TOKENTYPE, *PTOKENTTYPE; typedef enum _stringsSectionType STRINGSSECTIONTYPE;; struct _value { PVALUE pNext; PCHAR pName; BOOLEAN Allocated; }; struct _line { PLINE pNext; PCHAR pName; PVALUE pValue; BOOLEAN Allocated; }; struct _section { PSECTION pNext; PCHAR pName; PLINE pLine; BOOLEAN Allocated; }; struct _inf { PSECTION pSection; PSECTION pSectionRecord; PLINE pLineRecord; PVALUE pValueRecord; STRINGSSECTIONTYPE StringsSectionType; PSECTION StringsSection; }; // // [Strings] section types. // enum _stringsSectionType { StringsSectionNone, StringsSectionPlain, StringsSectionLoosePrimaryMatch, StringsSectionExactPrimaryMatch, StringsSectionExactMatch }; enum _tokentype { TOK_EOF, TOK_EOL, TOK_LBRACE, TOK_RBRACE, TOK_STRING, TOK_EQUAL, TOK_COMMA, TOK_ERRPARSE, TOK_ERRNOMEM }; struct _cm_token { TOKENTYPE Type; PCHAR pValue; BOOLEAN Allocated; }; VOID CmpFreeValueList( IN PVALUE pValue ); VOID CmpFreeLineList( IN PLINE pLine ); VOID CmpFreeSectionList( IN PSECTION pSection ); PCHAR CmpProcessForSimpleStringSub( IN PINF pInf, IN PCHAR String ); BOOLEAN CmpAppendSection( IN PINF pInf, IN PCHAR pSectionName, IN BOOLEAN Allocated ); BOOLEAN CmpAppendLine( IN PINF pInf, IN PCHAR pLineKey, IN BOOLEAN Allocated ); BOOLEAN CmpAppendValue( IN PINF pInf, IN PCHAR pValueString, IN BOOLEAN Allocated ); VOID CmpGetToken( IN OUT PCHAR *Stream, IN PCHAR MaxStream, IN OUT PCM_TOKEN Token ); PINF CmpParseInfBuffer( IN PCHAR Buffer, IN ULONG Size, IN OUT PULONG ErrorLine ); PVALUE CmpSearchValueInLine( IN PLINE pLine, IN ULONG ValueIndex ); PLINE CmpSearchLineInSectionByIndex( IN PSECTION pSection, IN ULONG LineIndex ); PSECTION CmpSearchSectionByName( IN PINF pInf, IN PCHAR SectionName ); #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,CmpFreeValueList) #pragma alloc_text(INIT,CmpFreeLineList) #pragma alloc_text(INIT,CmpFreeSectionList) #pragma alloc_text(INIT,CmpProcessForSimpleStringSub) #pragma alloc_text(INIT,CmpAppendSection) #pragma alloc_text(INIT,CmpAppendLine) #pragma alloc_text(INIT,CmpAppendValue) #pragma alloc_text(INIT,CmpGetToken) #pragma alloc_text(INIT,CmpParseInfBuffer) #pragma alloc_text(INIT,CmpSearchValueInLine) #pragma alloc_text(INIT,CmpSearchLineInSectionByIndex) #pragma alloc_text(INIT,CmpSearchSectionByName) #pragma alloc_text(INIT,CmpSearchInfLine) #pragma alloc_text(INIT,CmpOpenInfFile) #pragma alloc_text(INIT,CmpCloseInfFile) #pragma alloc_text(INIT,CmpGetKeyName) #pragma alloc_text(INIT,CmpSearchInfSection) #pragma alloc_text(INIT,CmpGetSectionLineIndex) #pragma alloc_text(INIT,CmpGetSectionLineIndexValueCount) #pragma alloc_text(INIT,CmpGetIntField) #pragma alloc_text(INIT,CmpGetBinaryField) #endif // // 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. // #ifdef ALLOC_DATA_PRAGMA #pragma const_seg("INITCONST") #endif const CHAR StringTerminators[] = "[]=,\t \"\n\f\v\r"; CHAR const* const QStringTerminators = StringTerminators + 6; const CHAR EmptyValue[] = ""; BOOLEAN CmpAppendSection( IN PINF pInf, IN PCHAR pSectionName, IN BOOLEAN Allocated ) /*++ Routine Description: This routine creates a new section or merges with an existing section in the inf. Input Parameters: pInf - Pointer to the inf to be processed. pSectionName - Name of the section. Allocated - TRUE if memory was allocated for the section name. Return Value: TRUE iff successful. --*/ { PSECTION pNewSection; PLINE pLineRecord; STRINGSSECTIONTYPE type; USHORT id; USHORT threadLang; PCHAR p; // // Check to see if INF initialised and the parameters passed in is valid // if ( pInf == (PINF)NULL || pSectionName == (PCHAR)NULL) { return (FALSE); } // // 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 && _stricmp(pNewSection->pName,pSectionName) == 0) { break; } } if(pNewSection) { // // Set pLineRecord to point to the last line currently in the section. // for( pLineRecord = pNewSection->pLine; pLineRecord && pLineRecord->pNext; pLineRecord = pLineRecord->pNext); pInf->pLineRecord = pLineRecord; } else { // // Allocate memory for the new section // pNewSection = (PSECTION)ExAllocatePoolWithTag(PagedPool, sizeof(SECTION), CM_PARSEINI_TAG); if (pNewSection == (PSECTION)NULL) { ASSERT(pNewSection); return (FALSE); } // // Initialize the new section. // pNewSection->pNext = NULL; pNewSection->pLine = NULL; pNewSection->pName = pSectionName; pNewSection->Allocated = Allocated; // // Link it in. // pNewSection->pNext = pInf->pSection; pInf->pSection = pNewSection; if(_strnicmp(pSectionName, "Strings", 7) == 0) { type = StringsSectionNone; if(pSectionName[7] == '.') { // // The langid part must be in the form of 4 hex digits. // id = (USHORT)strtoul(pSectionName + 8, &p, 16); if(p == (pSectionName + 8 + 5) && *p == '\0') { threadLang = LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale); if(threadLang == id) { type = StringsSectionExactMatch; } else { if(id == PRIMARYLANGID(threadLang)) { type = StringsSectionExactPrimaryMatch; } else { if(PRIMARYLANGID(id) == PRIMARYLANGID(threadLang)) { type = StringsSectionLoosePrimaryMatch; } } } } } else { if(!pSectionName[7]) { type = StringsSectionPlain; } } if(type > pInf->StringsSectionType) { pInf->StringsSection = pNewSection; } } // // Reset the current line record. // pInf->pLineRecord = NULL; } pInf->pSectionRecord = pNewSection; pInf->pValueRecord = NULL; return (TRUE); } BOOLEAN CmpAppendLine( IN PINF pInf, IN PCHAR pLineKey, IN BOOLEAN Allocated ) /*++ Routine Description: This routine creates a new line and appends it to the end of the line list. Input Parameters: pInf - Pointer to the inf to be processed. pLineKey - Name of the line. Allocated - TRUE if memory was allocated for the line name. Return Value: TRUE iff successful. --*/ { PLINE pNewLine; // // Check to see if current section initialized. // if (pInf->pSectionRecord == (PSECTION)NULL) { return (FALSE); } // // Allocate memory for the new Line. // pNewLine = (PLINE)ExAllocatePoolWithTag(PagedPool, sizeof(LINE), CM_PARSEINI_TAG); if (pNewLine == (PLINE)NULL) { ASSERT(pNewLine); return (FALSE); } // // Link it in. // pNewLine->pNext = (PLINE)NULL; pNewLine->pValue = (PVALUE)NULL; pNewLine->pName = pLineKey; pNewLine->Allocated = Allocated; if (pInf->pLineRecord == (PLINE)NULL) { pInf->pSectionRecord->pLine = pNewLine; } else { pInf->pLineRecord->pNext = pNewLine; } pInf->pLineRecord = pNewLine; // // Reset the current value record // pInf->pValueRecord = (PVALUE)NULL; return (TRUE); } BOOLEAN CmpAppendValue( IN PINF pInf, IN PCHAR pValueString, IN BOOLEAN Allocated ) /*++ Routine Description: This routine creates a new value and appends it to the end of the value list. Input Parameters: pInf - Pointer to the inf to be processed. pValueString - Name of the value. Allocated - TRUE if memory was allocated for the value name. Return Value: TRUE iff successful. --*/ { PVALUE pNewValue; // // Check to see if current line record has been initialised and // the parameter passed in is valid. // if ( pInf->pLineRecord == (PLINE)NULL || pValueString == (PCHAR)NULL) { return (FALSE); } // // Allocate memory for the new value record. // pNewValue = (PVALUE)ExAllocatePoolWithTag(PagedPool, sizeof(VALUE), CM_PARSEINI_TAG); if (pNewValue == (PVALUE)NULL) { ASSERT(pNewValue); return (FALSE); } // // Link it in. // pNewValue->pNext = (PVALUE)NULL; pNewValue->pName = pValueString; pNewValue->Allocated = Allocated; if (pInf->pValueRecord == (PVALUE)NULL) { pInf->pLineRecord->pValue = pNewValue; } else { pInf->pValueRecord->pNext = pNewValue; } pInf->pValueRecord = pNewValue; return (TRUE); } VOID CmpGetToken( IN OUT PCHAR *Stream, IN PCHAR MaxStream, IN OUT PCM_TOKEN Token ) /*++ 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: None. --*/ { PCHAR pch; PCHAR pchStart; PCHAR pchNew; ULONG length; BOOLEAN done; Token->Allocated = FALSE; Token->pValue = NULL; do { done = TRUE; // // Skip whitespace (except for EOL). // for ( pch = *Stream; pch < MaxStream && *pch != '\n' && isspace(*pch); pch++); // // Check for comments and remove them. // if ( pch < MaxStream && (*pch == '#' || *pch == ';')) { while (pch < MaxStream && *pch != '\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; } switch (*pch) { case '[': pch++; Token->Type = TOK_LBRACE; break; case ']': pch++; Token->Type = TOK_RBRACE; break; case '=': pch++; Token->Type = TOK_EQUAL; break; case ',': pch++; Token->Type = TOK_COMMA; break; case '\n': pch++; Token->Type = TOK_EOL; break; case '\"': pch++; // // Determine quoted string. // for ( pchStart = pch; pch < MaxStream && (strchr(QStringTerminators, *pch) == NULL); pch++); if (pch >= MaxStream || *pch != '\"') { Token->Type = TOK_ERRPARSE; } else { // // We require a quoted string to end with a double-quote. // (If the string ended with anything else, the if() above // would not have let us into the else clause.) The quote // character is irrelevent, however, and can be overwritten. // So we'll save some heap and use the string in-place. // No need to make a copy. // // Note that this alters the image of txtsetup.sif we pass // to setupdd.sys. Thus the inf parser in setupdd.sys must // be able to treat a nul character as if it were a terminating // double quote. // *pch++ = '\0'; Token->Type = TOK_STRING; Token->pValue = pchStart; } break; case '\\': for ( pchNew = ++pch; pchNew < MaxStream && *pchNew != '\n' && isspace(*pchNew); pchNew++); if (pch >= MaxStream) { Token->Type = TOK_ERRPARSE; break; } else if (*pchNew == '\n') { pch = pchNew + 1; done = FALSE; break; } default: // // Determine regular string. // for ( pchStart = pch; pch < MaxStream && (strchr(StringTerminators, *pch) == NULL); pch++); if (pch == pchStart) { pch++; Token->Type = TOK_ERRPARSE; } else { length = (ULONG)(pch - pchStart); pchNew = ExAllocatePoolWithTag(PagedPool, length + 1, CM_PARSEINI_TAG); if (pchNew == NULL) { ASSERT(pchNew); Token->Type = TOK_ERRNOMEM; } else { strncpy(pchNew, pchStart, length); pchNew[length] = 0; Token->Type = TOK_STRING; Token->pValue = pchNew; Token->Allocated = TRUE; } } break; } *Stream = pch; } while (!done); return; } PINF CmpParseInfBuffer( IN PCHAR Buffer, IN ULONG Size, IN OUT 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. 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. --*/ { PINF pInf; ULONG state; PCHAR stream; PCHAR maxStream; PCHAR pchSectionName; PCHAR pchValue; CM_TOKEN token; BOOLEAN done; BOOLEAN error; ULONG infLine; BOOLEAN allocated = FALSE; // // Allocate memory for the INF record. // pInf = (PINF)ExAllocatePoolWithTag(PagedPool, sizeof(INF), CM_PARSEINI_TAG); if (pInf == NULL) { return NULL; } pInf->pSection = NULL; pInf->pSectionRecord = NULL; pInf->pLineRecord = NULL; pInf->pValueRecord = NULL; pInf->StringsSectionType = StringsSectionNone; pInf->StringsSection = NULL; // // Set initial state. // state = 1; stream = Buffer; maxStream = Buffer + Size; pchSectionName = NULL; pchValue = NULL; done = FALSE; error = FALSE; infLine = 1; // // Enter token processing loop. // while (!done) { CmpGetToken(&stream, maxStream, &token); switch (state) { // // STATE1: Start of file, this state remains till first // section is found // Valid Tokens: TOK_EOL, TOK_EOF, TOK_LBRACE // case 1: switch (token.Type) { case TOK_EOL: break; case TOK_EOF: done = TRUE; break; case TOK_LBRACE: state = 2; break; case TOK_STRING: ASSERT(token.Type != TOK_STRING); error = done = TRUE; break; default: error = done = TRUE; break; } break; // // STATE 2: Section LBRACE has been received, expecting STRING // // Valid Tokens: TOK_STRING, TOK_RBRACE // case 2: switch (token.Type) { case TOK_STRING: state = 3; pchSectionName = token.pValue; allocated = token.Allocated; break; case TOK_RBRACE: token.pValue = (PCHAR)EmptyValue; token.Allocated = FALSE; allocated = FALSE; state = 4; break; default: error = done = TRUE; 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; break; } break; // // STATE 4: Section Definition Complete, expecting EOL // // Valid Tokens: TOK_EOL, TOK_EOF // case 4: switch (token.Type) { case TOK_EOL: if (!CmpAppendSection(pInf, pchSectionName, allocated)) { error = done = TRUE; } else { pchSectionName = NULL; state = 5; } break; case TOK_EOF: if (!CmpAppendSection(pInf, pchSectionName, allocated)) { error = done = TRUE; } else { pchSectionName = NULL; done = TRUE; } break; default: error = done = TRUE; 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; allocated = token.Allocated; state = 6; break; case TOK_LBRACE: state = 2; break; default: error = done = TRUE; 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 ( !CmpAppendLine(pInf, NULL, FALSE) || !CmpAppendValue(pInf, pchValue, allocated)) { error = done = TRUE; } else { pchValue = NULL; state = 5; } break; case TOK_EOF: if ( !CmpAppendLine(pInf, NULL, FALSE) || !CmpAppendValue(pInf, pchValue, allocated)) { error = done = TRUE; } else { pchValue = NULL; done = TRUE; } break; case TOK_COMMA: if ( !CmpAppendLine(pInf, NULL, FALSE) || !CmpAppendValue(pInf, pchValue, allocated)) { error = done = TRUE; } else { pchValue = NULL; state = 7; } break; case TOK_EQUAL: if (!CmpAppendLine(pInf, pchValue, allocated)) { error = done = TRUE; } else { pchValue = NULL; state = 8; } break; default: error = done = TRUE; break; } break; // // STATE 7: Comma received, Expecting another string // // Valid Tokens: TOK_STRING TOK_COMMA // A comma means we have an empty value. // case 7: switch (token.Type) { case TOK_COMMA: token.pValue = (PCHAR)EmptyValue; token.Allocated = FALSE; allocated = FALSE; if (!CmpAppendValue(pInf, token.pValue, FALSE)) { error = done = TRUE; } // // State stays at 7 because we are expecting a string // break; case TOK_STRING: if (!CmpAppendValue(pInf, token.pValue, token.Allocated)) { error = done = TRUE; } else { state = 9; } break; default: error = done = TRUE; 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 = (PCHAR)EmptyValue; token.Allocated = FALSE; allocated = FALSE; if(!CmpAppendValue(pInf, token.pValue, FALSE)) { error = TRUE; } done = TRUE; break; case TOK_EOL: token.pValue = (PCHAR)EmptyValue; token.Allocated = FALSE; allocated = FALSE; if(!CmpAppendValue(pInf, token.pValue, FALSE)) { error = TRUE; done = TRUE; } else { state = 5; } break; case TOK_STRING: if (!CmpAppendValue(pInf, token.pValue, FALSE)) { error = done = TRUE; } else { state = 9; } break; default: error = done = TRUE; 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; 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; break; } break; default: error = done = TRUE; break; } // END switch(state) if (error) { *ErrorLine = infLine; if (pchSectionName != (PCHAR)NULL && allocated) { ExFreePool(pchSectionName); } if (pchValue != (PCHAR)NULL && allocated) { ExFreePool(pchValue); } ExFreePool(pInf); pInf = (PINF)NULL; } else { // // Keep track of line numbers for error reporting. // if (token.Type == TOK_EOL) { infLine++; } } } // END while if (pInf) { pInf->pSectionRecord = NULL; } return(pInf); } PCHAR CmpProcessForSimpleStringSub( IN PINF pInf, IN PCHAR String ) /*++ Routine Description: This routine substitutes reference to string in the STRINGS section of the inf. Input Parameters: pInf - Pointer to the inf to be processed. String - String to be substituted. Return Value: None. --*/ { SIZE_T len; PCHAR returnString; PSECTION pSection; PLINE pLine; // // Assume no substitution necessary. // returnString = String; len = strlen(String); pSection = pInf->StringsSection; // // 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] == '%' && len > 2 && String[len - 1] == '%' && pSection) { for(pLine = pSection->pLine; pLine; pLine = pLine->pNext) { if( pLine->pName && _strnicmp(pLine->pName, String + 1, len - 2) == 0 && pLine->pName[len - 2] == '\0') { break; } } if(pLine && pLine->pValue && pLine->pValue->pName) { returnString = pLine->pValue->pName; } } return(returnString); } VOID CmpFreeValueList( IN PVALUE pValue ) /*++ Routine Description: This routine releases memory for the list of values. Input Parameters: pValue - Pointer to the value list to be freed. Return Value: None. --*/ { PVALUE pNext; while (pValue) { // // Save the next pointer so we dont access memory after it has // been freed. // pNext = pValue->pNext; // // Free any data inside this value. // if (pValue->Allocated && pValue->pName) { ExFreePool((PVOID)pValue->pName); } // // Free memory for this value. // ExFreePool(pValue); // // Go to the next value. // pValue = pNext; } } VOID CmpFreeLineList( IN PLINE pLine ) /*++ Routine Description: This routine releases memory for the list of lines and values under it. Input Parameters: pLine - Pointer to the line list to be freed. Return Value: None. --*/ { PLINE pNext; while (pLine) { // // Save the next pointer so we dont access memory after it has // been freed. // pNext = pLine->pNext; // // Free any data inside this Line. // if (pLine->Allocated && pLine->pName) { ExFreePool((PVOID)pLine->pName); } // // Free the list of values inside this Line. // CmpFreeValueList(pLine->pValue); // // Free memory for this line itself. // ExFreePool((PVOID)pLine); // // Go to the next line. // pLine = pNext; } } VOID CmpFreeSectionList( IN PSECTION pSection ) /*++ Routine Description: This routine releases memory for the list of sections and lines under it. Input Parameters: pSection - Pointer to the section list to be freed. Return Value: None. --*/ { PSECTION pNext; while (pSection) { // // Save the next pointer so we dont access memory after it has // been freed. // pNext = pSection->pNext; // // Free any data inside this Line. // if (pSection->Allocated && pSection->pName) { ExFreePool((PVOID)pSection->pName); } // // Free the list of values inside this Line. // CmpFreeLineList(pSection->pLine); // // Free memory for this line itself. // ExFreePool((PVOID)pSection); // // Go to the next line. // pSection = pNext; } } PVALUE CmpSearchValueInLine( IN PLINE pLine, IN ULONG ValueIndex ) /*++ Routine Description: This routine searches for the specified value in the inf. Input Parameters: pLine - Pointer to the line to be searched. ValueIndex - Index of the value to be searched. Return Value: Pointer to the value iff found. Else NULL. --*/ { ULONG i; PVALUE pValue = NULL; if (pLine) { for ( i = 0, pValue = pLine->pValue; i < ValueIndex && pValue; i++, pValue = pValue->pNext); } return (pValue); } PSECTION CmpSearchSectionByName( IN PINF pInf, IN PCHAR SectionName ) /*++ Routine Description: This routine searches for the specified section in the inf. Input Parameters: pInf - Pointer to the inf to be searched. SectionName - Name of the section to be searched. Return Value: Pointer to the section iff found. Else NULL. --*/ { PSECTION pSection = NULL; PSECTION pFirstSearchedSection; // // Validate the parameters passed in. // if (pInf && SectionName) { // // Traverse down the section list searching each section for the // section name mentioned. // for ( pSection = pFirstSearchedSection = pInf->pSectionRecord; pSection && _stricmp(pSection->pName, SectionName); pSection = pSection->pNext); // // If we did not find the section, search from the beginning. // if (pSection == NULL) { for ( pSection = pInf->pSection; pSection && pSection != pFirstSearchedSection; pSection = pSection->pNext) { if (pSection->pName && _stricmp(pSection->pName, SectionName) == 0) { break; } } if (pSection == pFirstSearchedSection) { pSection = NULL; } } if (pSection) { pInf->pSectionRecord = pSection; } } // // Return the section at which we stopped. // return (pSection); } PLINE CmpSearchLineInSectionByIndex( IN PSECTION pSection, IN ULONG LineIndex ) /*++ Routine Description: This routine searches for the specified line in the inf. Input Parameters: pSection - Pointer to the section to be searched. LineIndex - Index of the line to be searched. Return Value: Pointer to the line iff found. Else NULL. --*/ { PLINE pLine = NULL; ULONG i; // // Validate the parameters passed in. // if (pSection) { // // Traverse down the current line list to the LineIndex line. // for( i = 0, pLine = pSection->pLine; i < LineIndex && pLine; i++, pLine = pLine->pNext); } // // Return the Line found // return (pLine); } PVOID CmpOpenInfFile( IN PVOID InfImage, IN ULONG ImageSize ) /*++ Routine Description: This routine opens an handle to the inf. Input Parameters: InfImage - Pointer to the inf image read into memory. ImageSize - Image size. Return Value: Returns handle to the inf iff successful. Else NULL. --*/ { PINF infHandle; ULONG errorLine = 0; // // Parse the inf buffer. // infHandle = CmpParseInfBuffer(InfImage, ImageSize, &errorLine); if (infHandle == NULL) { #ifndef _CM_LDR_ DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Error on line %d in CmpOpenInfFile!\n", errorLine); #endif //_CM_LDR_ } return (infHandle); } VOID CmpCloseInfFile( PVOID InfHandle ) /*++ Routine Description: This routine closes the inf handle by releasing any memory allocated for it during parsing. Input Parameters: InfHandle - Handle to the inf to be closed. Return Value: None. --*/ { if (InfHandle) { CmpFreeSectionList(((PINF)InfHandle)->pSection); ExFreePool(InfHandle); } } LOGICAL CmpSearchInfSection( IN PINF pInf, IN PCHAR Section ) /*++ Routine Description: This routine searches for the specified section in the inf. Input Parameters: InfHandle - Handle to the inf to be read. Section - Name of the section to be read. Return Value: TRUE iff section is found in the inf. --*/ { return (CmpSearchSectionByName(pInf, Section) != NULL); } PCHAR CmpGetKeyName( IN PVOID InfHandle, IN PCHAR Section, IN ULONG LineIndex ) /*++ Routine Description: This routine returns the name of the specified line in the inf. Input Parameters: InfHandle - Handle to the inf to be read. Section - Name of the section to be read. LineIndex - Index of the line to be read. Return Value: Pointer to the name of line in the inf iff successful. Else NULL. --*/ { PSECTION pSection; PLINE pLine; // // First search the section. // pSection = CmpSearchSectionByName((PINF)InfHandle, Section); if(pSection) { // // Get the line in the section. // pLine = CmpSearchLineInSectionByIndex(pSection, LineIndex); if(pLine) { return(pLine->pName); } } return (NULL); } LOGICAL CmpSearchInfLine( IN PVOID InfHandle, IN PCHAR Section, IN ULONG LineIndex ) /*++ Routine Description: This routine searches for the specified line in the inf. Input Parameters: InfHandle - Handle to the inf to be read. Section - Name of the section to be read. LineIndex - Index of the line to be read. Return Value: TRUE iff line is found in the section in the inf. --*/ { PSECTION pSection; PLINE pLine = NULL; // // First search the section. // pSection = CmpSearchSectionByName((PINF)InfHandle, Section); if(pSection) { // // Search the line in the section. // pLine = CmpSearchLineInSectionByIndex(pSection, LineIndex); } return (pLine != NULL); } PCHAR CmpGetSectionLineIndex ( IN PVOID InfHandle, IN PCHAR Section, IN ULONG LineIndex, IN ULONG ValueIndex ) /*++ Routine Description: This routine returns the value at the specified location in the inf. Input Parameters: InfHandle - Handle to the inf to be read. Section - Name of the section to be read. LineIndex - Index of the line to be read. ValueIndex - Index of the value to be read. Return Value: Pointer to the value iff successful. Else NULL. --*/ { PSECTION pSection; PLINE pLine; PVALUE pValue; // // Search the section in the inf. // pSection = CmpSearchSectionByName((PINF)InfHandle, Section); if(pSection) { // // Search the line in the section. // pLine = CmpSearchLineInSectionByIndex(pSection, LineIndex); if(pLine) { // // Search the value in the line. // pValue = CmpSearchValueInLine(pLine, ValueIndex); if(pValue) { // // The value may need to be replaced by one of the strings // from the string section. // return(CmpProcessForSimpleStringSub(InfHandle, pValue->pName)); } } } return(NULL); } ULONG CmpGetSectionLineIndexValueCount( IN PVOID InfHandle, IN PCHAR Section, IN ULONG LineIndex ) /*++ Routine Description: This routine returns the number of values in the inf line. Input Parameters: InfHandle - Handle to the inf to be read. Section - Name of the section to be read. LineIndex - Index of the line to be read. Return Value: Number of values in the inf line. --*/ { PSECTION pSection; PLINE pLine; PVALUE pValue; ULONG count = 0; // // Search the section in the inf. // pSection = CmpSearchSectionByName((PINF)InfHandle, Section); if(pSection) { // // Search the line in the section. // pLine = CmpSearchLineInSectionByIndex(pSection, LineIndex); if (pLine) { // // Count the number of values in this line. // for( pValue = pLine->pValue; pValue; pValue = pValue->pNext, count++); } } return (count); } BOOLEAN CmpGetIntField( IN PVOID InfHandle, IN PCHAR Section, IN ULONG LineIndex, IN ULONG ValueIndex, IN OUT PULONG Data ) /*++ Routine Description: This routine reads integer data from the inf. Input Parameters: InfHandle - Handle to the inf to be read. Section - Name of the section to be read. LineIndex - Index of the line to be read. ValueIndex - Index of the value to be read. Data - Receives the integer data. Return Value: TRUE iff successful. --*/ { PCHAR valueStr; // // Get the specified value. // valueStr = CmpGetSectionLineIndex( InfHandle, Section, LineIndex, ValueIndex); // // If valid value is found, convert it to an integer. // if (valueStr && *valueStr) { *Data = strtoul(valueStr, NULL, 16); return (TRUE); } return (FALSE); } BOOLEAN CmpGetBinaryField( IN PVOID InfHandle, IN PCHAR Section, IN ULONG LineIndex, IN ULONG ValueIndex, IN OUT PVOID Buffer, IN ULONG BufferSize, IN OUT PULONG ActualSize ) /*++ Routine Description: This routine reads binary data from the inf. Input Parameters: InfHandle - Handle to the inf to be read. Section - Name of the section to be read. LineIndex - Index of the line to be read. ValueIndex - Index of the value to be read. Buffer - Receives the binary data read. BufferSize - Size of the buffer. ActualSize - Receives the size of the data buffer required. Return Value: TRUE iff successful. --*/ { BOOLEAN result = FALSE; ULONG requiredSize; PSECTION pSection; PLINE pLine; PVALUE pValue; ULONG count; PCHAR valueStr = NULL; PUCHAR charBuf; // // Compute the size of buffer required to read in the binary data. // requiredSize = (CmpGetSectionLineIndexValueCount( InfHandle, Section, LineIndex) - ValueIndex) * sizeof(UCHAR); // // Validate input parameters. // if (Buffer && BufferSize >= requiredSize) { // // Search the section in the inf. // pSection = CmpSearchSectionByName((PINF)InfHandle, Section); if(pSection) { // // Search the line in this section. // pLine = CmpSearchLineInSectionByIndex(pSection, LineIndex); if (pLine) { // // Go to the specified value. // for( pValue = pLine->pValue, count = 0; pValue && count < ValueIndex; pValue = pValue->pNext, count++); // // Read in and convert the binary data. // charBuf = (PUCHAR) Buffer; for ( ; pValue; pValue = pValue->pNext) { valueStr = CmpGetSectionLineIndex( InfHandle, Section, LineIndex, ValueIndex++); if (valueStr == NULL) { break; } *charBuf++ = (UCHAR)strtoul(valueStr, NULL, 16); } if (valueStr) { result = TRUE; } } } } // // The caller wants to know the buffer size required. // if (ActualSize) { *ActualSize = requiredSize; result = TRUE; } return (result); } #ifdef ALLOC_DATA_PRAGMA #pragma const_seg() #endif