blob: 0a44131a082a95436aedd8fab85c23de4da2dc66 [file] [log] [blame]
/** @file
This library parses the INI configuration file.
The INI file format is:
================
[SectionName]
EntryName=EntryValue
================
Where:
1) SectionName is an ASCII string. The valid format is [A-Za-z0-9_]+
2) EntryName is an ASCII string. The valid format is [A-Za-z0-9_]+
3) EntryValue can be:
3.1) an ASCII String. The valid format is [A-Za-z0-9_]+
3.2) a GUID. The valid format is xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, where x is [A-Fa-f0-9]
3.3) a decimal value. The valid format is [0-9]+
3.4) a hexadecimal value. The valid format is 0x[A-Fa-f0-9]+
4) '#' or ';' can be used as comment at anywhere.
5) TAB(0x20) or SPACE(0x9) can be used as separator.
6) LF(\n, 0xA) or CR(\r, 0xD) can be used as line break.
Caution: This module requires additional review when modified.
This driver will have external input - INI data file.
OpenIniFile(), PreProcessDataFile(), ProfileGetSection(), ProfileGetEntry()
will receive untrusted input and do basic validation.
Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#define IS_HYPHEN(a) ((a) == '-')
#define IS_NULL(a) ((a) == '\0')
// This is default allocation. Reallocation will happen if it is not enough.
#define MAX_LINE_LENGTH 512
typedef struct _INI_SECTION_ITEM SECTION_ITEM;
struct _INI_SECTION_ITEM {
CHAR8 *PtrSection;
UINTN SecNameLen;
CHAR8 *PtrEntry;
CHAR8 *PtrValue;
SECTION_ITEM *PtrNext;
};
typedef struct _INI_COMMENT_LINE COMMENT_LINE;
struct _INI_COMMENT_LINE {
CHAR8 *PtrComment;
COMMENT_LINE *PtrNext;
};
typedef struct {
SECTION_ITEM *SectionHead;
COMMENT_LINE *CommentHead;
} INI_PARSING_LIB_CONTEXT;
/**
Return if the digital char is valid.
@param[in] DigitalChar The digital char to be checked.
@param[in] IncludeHex If it include HEX char.
@retval TRUE The digital char is valid.
@retval FALSE The digital char is invalid.
**/
BOOLEAN
IsValidDigitalChar (
IN CHAR8 DigitalChar,
IN BOOLEAN IncludeHex
)
{
if ((DigitalChar >= '0') && (DigitalChar <= '9')) {
return TRUE;
}
if (IncludeHex) {
if ((DigitalChar >= 'a') && (DigitalChar <= 'f')) {
return TRUE;
}
if ((DigitalChar >= 'A') && (DigitalChar <= 'F')) {
return TRUE;
}
}
return FALSE;
}
/**
Return if the name char is valid.
@param[in] NameChar The name char to be checked.
@retval TRUE The name char is valid.
@retval FALSE The name char is invalid.
**/
BOOLEAN
IsValidNameChar (
IN CHAR8 NameChar
)
{
if ((NameChar >= 'a') && (NameChar <= 'z')) {
return TRUE;
}
if ((NameChar >= 'A') && (NameChar <= 'Z')) {
return TRUE;
}
if ((NameChar >= '0') && (NameChar <= '9')) {
return TRUE;
}
if (NameChar == '_') {
return TRUE;
}
return FALSE;
}
/**
Return if the digital string is valid.
@param[in] Digital The digital to be checked.
@param[in] Length The length of digital string in bytes.
@param[in] IncludeHex If it include HEX char.
@retval TRUE The digital string is valid.
@retval FALSE The digital string is invalid.
**/
BOOLEAN
IsValidDigital (
IN CHAR8 *Digital,
IN UINTN Length,
IN BOOLEAN IncludeHex
)
{
UINTN Index;
for (Index = 0; Index < Length; Index++) {
if (!IsValidDigitalChar (Digital[Index], IncludeHex)) {
return FALSE;
}
}
return TRUE;
}
/**
Return if the decimal string is valid.
@param[in] Decimal The decimal string to be checked.
@param[in] Length The length of decimal string in bytes.
@retval TRUE The decimal string is valid.
@retval FALSE The decimal string is invalid.
**/
BOOLEAN
IsValidDecimalString (
IN CHAR8 *Decimal,
IN UINTN Length
)
{
return IsValidDigital (Decimal, Length, FALSE);
}
/**
Return if the hexadecimal string is valid.
@param[in] Hex The hexadecimal string to be checked.
@param[in] Length The length of hexadecimal string in bytes.
@retval TRUE The hexadecimal string is valid.
@retval FALSE The hexadecimal string is invalid.
**/
BOOLEAN
IsValidHexString (
IN CHAR8 *Hex,
IN UINTN Length
)
{
if (Length <= 2) {
return FALSE;
}
if (Hex[0] != '0') {
return FALSE;
}
if ((Hex[1] != 'x') && (Hex[1] != 'X')) {
return FALSE;
}
return IsValidDigital (&Hex[2], Length - 2, TRUE);
}
/**
Return if the name string is valid.
@param[in] Name The name to be checked.
@param[in] Length The length of name string in bytes.
@retval TRUE The name string is valid.
@retval FALSE The name string is invalid.
**/
BOOLEAN
IsValidName (
IN CHAR8 *Name,
IN UINTN Length
)
{
UINTN Index;
for (Index = 0; Index < Length; Index++) {
if (!IsValidNameChar (Name[Index])) {
return FALSE;
}
}
return TRUE;
}
/**
Return if the value string is valid GUID.
@param[in] Value The value to be checked.
@param[in] Length The length of value string in bytes.
@retval TRUE The value string is valid GUID.
@retval FALSE The value string is invalid GUID.
**/
BOOLEAN
IsValidGuid (
IN CHAR8 *Value,
IN UINTN Length
)
{
if (Length != sizeof ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") - 1) {
return FALSE;
}
if (!IS_HYPHEN (Value[8])) {
return FALSE;
}
if (!IS_HYPHEN (Value[13])) {
return FALSE;
}
if (!IS_HYPHEN (Value[18])) {
return FALSE;
}
if (!IS_HYPHEN (Value[23])) {
return FALSE;
}
if (!IsValidDigital (&Value[0], 8, TRUE)) {
return FALSE;
}
if (!IsValidDigital (&Value[9], 4, TRUE)) {
return FALSE;
}
if (!IsValidDigital (&Value[14], 4, TRUE)) {
return FALSE;
}
if (!IsValidDigital (&Value[19], 4, TRUE)) {
return FALSE;
}
if (!IsValidDigital (&Value[24], 12, TRUE)) {
return FALSE;
}
return TRUE;
}
/**
Return if the value string is valid.
@param[in] Value The value to be checked.
@param[in] Length The length of value string in bytes.
@retval TRUE The name string is valid.
@retval FALSE The name string is invalid.
**/
BOOLEAN
IsValidValue (
IN CHAR8 *Value,
IN UINTN Length
)
{
if (IsValidName (Value, Length) || IsValidGuid (Value, Length)) {
return TRUE;
}
return FALSE;
}
/**
Dump an INI config file context.
@param[in] Context INI Config file context.
**/
VOID
DumpIniSection (
IN VOID *Context
)
{
INI_PARSING_LIB_CONTEXT *IniContext;
SECTION_ITEM *PtrSection;
SECTION_ITEM *Section;
if (Context == NULL) {
return;
}
IniContext = Context;
Section = IniContext->SectionHead;
while (Section != NULL) {
PtrSection = Section;
Section = Section->PtrNext;
if (PtrSection->PtrSection != NULL) {
DEBUG ((DEBUG_VERBOSE, "Section - %a\n", PtrSection->PtrSection));
}
if (PtrSection->PtrEntry != NULL) {
DEBUG ((DEBUG_VERBOSE, " Entry - %a\n", PtrSection->PtrEntry));
}
if (PtrSection->PtrValue != NULL) {
DEBUG ((DEBUG_VERBOSE, " Value - %a\n", PtrSection->PtrValue));
}
}
}
/**
Copy one line data from buffer data to the line buffer.
@param[in] Buffer Buffer data.
@param[in] BufferSize Buffer Size.
@param[in, out] LineBuffer Line buffer to store the found line data.
@param[in, out] LineSize On input, size of the input line buffer.
On output, size of the actual line buffer.
@retval EFI_BUFFER_TOO_SMALL The size of input line buffer is not enough.
@retval EFI_SUCCESS Copy line data into the line buffer.
**/
EFI_STATUS
ProfileGetLine (
IN UINT8 *Buffer,
IN UINTN BufferSize,
IN OUT UINT8 *LineBuffer,
IN OUT UINTN *LineSize
)
{
UINTN Length;
UINT8 *PtrBuf;
UINTN PtrEnd;
PtrBuf = Buffer;
PtrEnd = (UINTN)Buffer + BufferSize;
//
// 0x0D indicates a line break. Otherwise there is no line break
//
while ((UINTN)PtrBuf < PtrEnd) {
if ((*PtrBuf == 0x0D) || (*PtrBuf == 0x0A)) {
break;
}
PtrBuf++;
}
if ((UINTN)PtrBuf >= (PtrEnd - 1)) {
//
// The buffer ends without any line break
// or it is the last character of the buffer
//
Length = BufferSize;
} else if (*(PtrBuf + 1) == 0x0A) {
//
// Further check if a 0x0A follows. If yes, count 0xA
//
Length = (UINTN)PtrBuf - (UINTN)Buffer + 2;
} else {
Length = (UINTN)PtrBuf - (UINTN)Buffer + 1;
}
if (Length > (*LineSize)) {
*LineSize = Length;
return EFI_BUFFER_TOO_SMALL;
}
SetMem (LineBuffer, *LineSize, 0x0);
*LineSize = Length;
CopyMem (LineBuffer, Buffer, Length);
return EFI_SUCCESS;
}
/**
Trim Buffer by removing all CR, LF, TAB, and SPACE chars in its head and tail.
@param[in, out] Buffer On input, buffer data to be trimmed.
On output, the trimmed buffer.
@param[in, out] BufferSize On input, size of original buffer data.
On output, size of the trimmed buffer.
**/
VOID
ProfileTrim (
IN OUT UINT8 *Buffer,
IN OUT UINTN *BufferSize
)
{
UINTN Length;
UINT8 *PtrBuf;
UINT8 *PtrEnd;
if (*BufferSize == 0) {
return;
}
//
// Trim the tail first, include CR, LF, TAB, and SPACE.
//
Length = *BufferSize;
PtrBuf = (UINT8 *)((UINTN)Buffer + Length - 1);
while (PtrBuf >= Buffer) {
if ( (*PtrBuf != 0x0D) && (*PtrBuf != 0x0A)
&& (*PtrBuf != 0x20) && (*PtrBuf != 0x09))
{
break;
}
PtrBuf--;
}
//
// all spaces, a blank line, return directly;
//
if (PtrBuf < Buffer) {
*BufferSize = 0;
return;
}
Length = (UINTN)PtrBuf - (UINTN)Buffer + 1;
PtrEnd = PtrBuf;
PtrBuf = Buffer;
//
// Now skip the heading CR, LF, TAB and SPACE
//
while (PtrBuf <= PtrEnd) {
if ( (*PtrBuf != 0x0D) && (*PtrBuf != 0x0A)
&& (*PtrBuf != 0x20) && (*PtrBuf != 0x09))
{
break;
}
PtrBuf++;
}
//
// If no heading CR, LF, TAB or SPACE, directly return
//
if (PtrBuf == Buffer) {
*BufferSize = Length;
return;
}
*BufferSize = (UINTN)PtrEnd - (UINTN)PtrBuf + 1;
//
// The first Buffer..PtrBuf characters are CR, LF, TAB or SPACE.
// Now move out all these characters.
//
while (PtrBuf <= PtrEnd) {
*Buffer = *PtrBuf;
Buffer++;
PtrBuf++;
}
return;
}
/**
Insert new comment item into comment head.
@param[in] Buffer Comment buffer to be added.
@param[in] BufferSize Size of comment buffer.
@param[in, out] CommentHead Comment Item head entry.
@retval EFI_OUT_OF_RESOURCES No enough memory is allocated.
@retval EFI_SUCCESS New comment item is inserted.
**/
EFI_STATUS
ProfileGetComments (
IN UINT8 *Buffer,
IN UINTN BufferSize,
IN OUT COMMENT_LINE **CommentHead
)
{
COMMENT_LINE *CommentItem;
CommentItem = NULL;
CommentItem = AllocatePool (sizeof (COMMENT_LINE));
if (CommentItem == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CommentItem->PtrNext = *CommentHead;
*CommentHead = CommentItem;
//
// Add a trailing '\0'
//
CommentItem->PtrComment = AllocatePool (BufferSize + 1);
if (CommentItem->PtrComment == NULL) {
FreePool (CommentItem);
return EFI_OUT_OF_RESOURCES;
}
CopyMem (CommentItem->PtrComment, Buffer, BufferSize);
*(CommentItem->PtrComment + BufferSize) = '\0';
return EFI_SUCCESS;
}
/**
Add new section item into Section head.
@param[in] Buffer Section item data buffer.
@param[in] BufferSize Size of section item.
@param[in, out] SectionHead Section item head entry.
@retval EFI_OUT_OF_RESOURCES No enough memory is allocated.
@retval EFI_SUCCESS Section item is NULL or Section item is added.
**/
EFI_STATUS
ProfileGetSection (
IN UINT8 *Buffer,
IN UINTN BufferSize,
IN OUT SECTION_ITEM **SectionHead
)
{
SECTION_ITEM *SectionItem;
UINTN Length;
UINT8 *PtrBuf;
UINT8 *PtrEnd;
ASSERT (BufferSize >= 1);
//
// The first character of Buffer is '[', now we want for ']'
//
PtrEnd = (UINT8 *)((UINTN)Buffer + BufferSize - 1);
PtrBuf = (UINT8 *)((UINTN)Buffer + 1);
while (PtrBuf <= PtrEnd) {
if (*PtrBuf == ']') {
break;
}
PtrBuf++;
}
if (PtrBuf > PtrEnd) {
//
// Not found. Invalid line
//
return EFI_NOT_FOUND;
}
if (PtrBuf <= Buffer + 1) {
// Empty name
return EFI_NOT_FOUND;
}
//
// excluding the heading '[' and tailing ']'
//
Length = PtrBuf - Buffer - 1;
ProfileTrim (
Buffer + 1,
&Length
);
//
// Invalid line if the section name is null
//
if (Length == 0) {
return EFI_NOT_FOUND;
}
if (!IsValidName ((CHAR8 *)Buffer + 1, Length)) {
return EFI_INVALID_PARAMETER;
}
SectionItem = AllocatePool (sizeof (SECTION_ITEM));
if (SectionItem == NULL) {
return EFI_OUT_OF_RESOURCES;
}
SectionItem->PtrSection = NULL;
SectionItem->SecNameLen = Length;
SectionItem->PtrEntry = NULL;
SectionItem->PtrValue = NULL;
SectionItem->PtrNext = *SectionHead;
*SectionHead = SectionItem;
//
// Add a trailing '\0'
//
SectionItem->PtrSection = AllocatePool (Length + 1);
if (SectionItem->PtrSection == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// excluding the heading '['
//
CopyMem (SectionItem->PtrSection, Buffer + 1, Length);
*(SectionItem->PtrSection + Length) = '\0';
return EFI_SUCCESS;
}
/**
Add new section entry and entry value into Section head.
@param[in] Buffer Section entry data buffer.
@param[in] BufferSize Size of section entry.
@param[in, out] SectionHead Section item head entry.
@retval EFI_OUT_OF_RESOURCES No enough memory is allocated.
@retval EFI_SUCCESS Section entry is added.
@retval EFI_NOT_FOUND Section entry is not found.
@retval EFI_INVALID_PARAMETER Section entry is invalid.
**/
EFI_STATUS
ProfileGetEntry (
IN UINT8 *Buffer,
IN UINTN BufferSize,
IN OUT SECTION_ITEM **SectionHead
)
{
EFI_STATUS Status;
SECTION_ITEM *SectionItem;
SECTION_ITEM *PtrSection;
UINTN Length;
UINT8 *PtrBuf;
UINT8 *PtrEnd;
Status = EFI_SUCCESS;
PtrBuf = Buffer;
PtrEnd = (UINT8 *)((UINTN)Buffer + BufferSize - 1);
//
// First search for '='
//
while (PtrBuf <= PtrEnd) {
if (*PtrBuf == '=') {
break;
}
PtrBuf++;
}
if (PtrBuf > PtrEnd) {
//
// Not found. Invalid line
//
return EFI_NOT_FOUND;
}
if (PtrBuf <= Buffer) {
// Empty name
return EFI_NOT_FOUND;
}
//
// excluding the tailing '='
//
Length = PtrBuf - Buffer;
ProfileTrim (
Buffer,
&Length
);
//
// Invalid line if the entry name is null
//
if (Length == 0) {
return EFI_NOT_FOUND;
}
if (!IsValidName ((CHAR8 *)Buffer, Length)) {
return EFI_INVALID_PARAMETER;
}
//
// Omit this line if no section header has been found before
//
if (*SectionHead == NULL) {
return Status;
}
PtrSection = *SectionHead;
SectionItem = AllocatePool (sizeof (SECTION_ITEM));
if (SectionItem == NULL) {
return EFI_OUT_OF_RESOURCES;
}
SectionItem->PtrSection = NULL;
SectionItem->PtrEntry = NULL;
SectionItem->PtrValue = NULL;
SectionItem->SecNameLen = PtrSection->SecNameLen;
SectionItem->PtrNext = *SectionHead;
*SectionHead = SectionItem;
//
// SectionName, add a trailing '\0'
//
SectionItem->PtrSection = AllocatePool (PtrSection->SecNameLen + 1);
if (SectionItem->PtrSection == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (SectionItem->PtrSection, PtrSection->PtrSection, PtrSection->SecNameLen + 1);
//
// EntryName, add a trailing '\0'
//
SectionItem->PtrEntry = AllocatePool (Length + 1);
if (SectionItem->PtrEntry == NULL) {
FreePool (SectionItem->PtrSection);
return EFI_OUT_OF_RESOURCES;
}
CopyMem (SectionItem->PtrEntry, Buffer, Length);
*(SectionItem->PtrEntry + Length) = '\0';
//
// Next search for '#' or ';'
//
PtrBuf = PtrBuf + 1;
Buffer = PtrBuf;
while (PtrBuf <= PtrEnd) {
if ((*PtrBuf == '#') || (*PtrBuf == ';')) {
break;
}
PtrBuf++;
}
if (PtrBuf <= Buffer) {
// Empty name
FreePool (SectionItem->PtrEntry);
FreePool (SectionItem->PtrSection);
return EFI_NOT_FOUND;
}
Length = PtrBuf - Buffer;
ProfileTrim (
Buffer,
&Length
);
//
// Invalid line if the entry value is null
//
if (Length == 0) {
FreePool (SectionItem->PtrEntry);
FreePool (SectionItem->PtrSection);
return EFI_NOT_FOUND;
}
if (!IsValidValue ((CHAR8 *)Buffer, Length)) {
FreePool (SectionItem->PtrEntry);
FreePool (SectionItem->PtrSection);
return EFI_INVALID_PARAMETER;
}
//
// EntryValue, add a trailing '\0'
//
SectionItem->PtrValue = AllocatePool (Length + 1);
if (SectionItem->PtrValue == NULL) {
FreePool (SectionItem->PtrEntry);
FreePool (SectionItem->PtrSection);
return EFI_OUT_OF_RESOURCES;
}
CopyMem (SectionItem->PtrValue, Buffer, Length);
*(SectionItem->PtrValue + Length) = '\0';
return EFI_SUCCESS;
}
/**
Free all comment entry and section entry.
@param[in] Section Section entry list.
@param[in] Comment Comment entry list.
**/
VOID
FreeAllList (
IN SECTION_ITEM *Section,
IN COMMENT_LINE *Comment
)
{
SECTION_ITEM *PtrSection;
COMMENT_LINE *PtrComment;
while (Section != NULL) {
PtrSection = Section;
Section = Section->PtrNext;
if (PtrSection->PtrEntry != NULL) {
FreePool (PtrSection->PtrEntry);
}
if (PtrSection->PtrSection != NULL) {
FreePool (PtrSection->PtrSection);
}
if (PtrSection->PtrValue != NULL) {
FreePool (PtrSection->PtrValue);
}
FreePool (PtrSection);
}
while (Comment != NULL) {
PtrComment = Comment;
Comment = Comment->PtrNext;
if (PtrComment->PtrComment != NULL) {
FreePool (PtrComment->PtrComment);
}
FreePool (PtrComment);
}
return;
}
/**
Get section entry value.
@param[in] Section Section entry list.
@param[in] SectionName Section name.
@param[in] EntryName Section entry name.
@param[out] EntryValue Point to the got entry value.
@retval EFI_NOT_FOUND Section is not found.
@retval EFI_SUCCESS Section entry value is got.
**/
EFI_STATUS
UpdateGetProfileString (
IN SECTION_ITEM *Section,
IN CHAR8 *SectionName,
IN CHAR8 *EntryName,
OUT CHAR8 **EntryValue
)
{
*EntryValue = NULL;
while (Section != NULL) {
if (AsciiStrCmp ((CONST CHAR8 *)Section->PtrSection, (CONST CHAR8 *)SectionName) == 0) {
if (Section->PtrEntry != NULL) {
if (AsciiStrCmp ((CONST CHAR8 *)Section->PtrEntry, (CONST CHAR8 *)EntryName) == 0) {
break;
}
}
}
Section = Section->PtrNext;
}
if (Section == NULL) {
return EFI_NOT_FOUND;
}
*EntryValue = Section->PtrValue;
return EFI_SUCCESS;
}
/**
Pre process config data buffer into Section entry list and Comment entry list.
@param[in] DataBuffer Config raw file buffer.
@param[in] BufferSize Size of raw buffer.
@param[in, out] SectionHead Pointer to the section entry list.
@param[in, out] CommentHead Pointer to the comment entry list.
@retval EFI_OUT_OF_RESOURCES No enough memory is allocated.
@retval EFI_SUCCESS Config data buffer is preprocessed.
@retval EFI_NOT_FOUND Config data buffer is invalid, because Section or Entry is not found.
@retval EFI_INVALID_PARAMETER Config data buffer is invalid, because Section or Entry is invalid.
**/
EFI_STATUS
PreProcessDataFile (
IN UINT8 *DataBuffer,
IN UINTN BufferSize,
IN OUT SECTION_ITEM **SectionHead,
IN OUT COMMENT_LINE **CommentHead
)
{
EFI_STATUS Status;
CHAR8 *Source;
CHAR8 *CurrentPtr;
CHAR8 *BufferEnd;
CHAR8 *PtrLine;
UINTN LineLength;
UINTN SourceLength;
UINTN MaxLineLength;
*SectionHead = NULL;
*CommentHead = NULL;
BufferEnd = (CHAR8 *)((UINTN)DataBuffer + BufferSize);
CurrentPtr = (CHAR8 *)DataBuffer;
MaxLineLength = MAX_LINE_LENGTH;
Status = EFI_SUCCESS;
PtrLine = AllocatePool (MaxLineLength);
if (PtrLine == NULL) {
return EFI_OUT_OF_RESOURCES;
}
while (CurrentPtr < BufferEnd) {
Source = CurrentPtr;
SourceLength = (UINTN)BufferEnd - (UINTN)CurrentPtr;
LineLength = MaxLineLength;
//
// With the assumption that line length is less than 512
// characters. Otherwise BUFFER_TOO_SMALL will be returned.
//
Status = ProfileGetLine (
(UINT8 *)Source,
SourceLength,
(UINT8 *)PtrLine,
&LineLength
);
if (EFI_ERROR (Status)) {
if (Status == EFI_BUFFER_TOO_SMALL) {
//
// If buffer too small, re-allocate the buffer according
// to the returned LineLength and try again.
//
FreePool (PtrLine);
PtrLine = NULL;
PtrLine = AllocatePool (LineLength);
if (PtrLine == NULL) {
Status = EFI_OUT_OF_RESOURCES;
break;
}
SourceLength = LineLength;
Status = ProfileGetLine (
(UINT8 *)Source,
SourceLength,
(UINT8 *)PtrLine,
&LineLength
);
if (EFI_ERROR (Status)) {
break;
}
MaxLineLength = LineLength;
} else {
break;
}
}
CurrentPtr = (CHAR8 *)((UINTN)CurrentPtr + LineLength);
//
// Line got. Trim the line before processing it.
//
ProfileTrim (
(UINT8 *)PtrLine,
&LineLength
);
//
// Blank line
//
if (LineLength == 0) {
continue;
}
if ((PtrLine[0] == '#') || (PtrLine[0] == ';')) {
Status = ProfileGetComments (
(UINT8 *)PtrLine,
LineLength,
CommentHead
);
} else if (PtrLine[0] == '[') {
Status = ProfileGetSection (
(UINT8 *)PtrLine,
LineLength,
SectionHead
);
} else {
Status = ProfileGetEntry (
(UINT8 *)PtrLine,
LineLength,
SectionHead
);
}
if (EFI_ERROR (Status)) {
break;
}
}
//
// Free buffer
//
FreePool (PtrLine);
return Status;
}
/**
Open an INI config file and return a context.
@param[in] DataBuffer Config raw file buffer.
@param[in] BufferSize Size of raw buffer.
@return Config data buffer is opened and context is returned.
@retval NULL No enough memory is allocated.
@retval NULL Config data buffer is invalid.
**/
VOID *
EFIAPI
OpenIniFile (
IN UINT8 *DataBuffer,
IN UINTN BufferSize
)
{
EFI_STATUS Status;
INI_PARSING_LIB_CONTEXT *IniContext;
if ((DataBuffer == NULL) || (BufferSize == 0)) {
return NULL;
}
IniContext = AllocateZeroPool (sizeof (INI_PARSING_LIB_CONTEXT));
if (IniContext == NULL) {
return NULL;
}
//
// First process the data buffer and get all sections and entries
//
Status = PreProcessDataFile (
DataBuffer,
BufferSize,
&IniContext->SectionHead,
&IniContext->CommentHead
);
if (EFI_ERROR (Status)) {
FreePool (IniContext);
return NULL;
}
DEBUG_CODE_BEGIN ();
DumpIniSection (IniContext);
DEBUG_CODE_END ();
return IniContext;
}
/**
Get section entry string value.
@param[in] Context INI Config file context.
@param[in] SectionName Section name.
@param[in] EntryName Section entry name.
@param[out] EntryValue Point to the got entry string value.
@retval EFI_SUCCESS Section entry string value is got.
@retval EFI_NOT_FOUND Section is not found.
**/
EFI_STATUS
EFIAPI
GetStringFromDataFile (
IN VOID *Context,
IN CHAR8 *SectionName,
IN CHAR8 *EntryName,
OUT CHAR8 **EntryValue
)
{
INI_PARSING_LIB_CONTEXT *IniContext;
EFI_STATUS Status;
if ((Context == NULL) || (SectionName == NULL) || (EntryName == NULL) || (EntryValue == NULL)) {
return EFI_INVALID_PARAMETER;
}
IniContext = Context;
*EntryValue = NULL;
Status = UpdateGetProfileString (
IniContext->SectionHead,
SectionName,
EntryName,
EntryValue
);
return Status;
}
/**
Get section entry GUID value.
@param[in] Context INI Config file context.
@param[in] SectionName Section name.
@param[in] EntryName Section entry name.
@param[out] Guid Point to the got GUID value.
@retval EFI_SUCCESS Section entry GUID value is got.
@retval EFI_NOT_FOUND Section is not found.
**/
EFI_STATUS
EFIAPI
GetGuidFromDataFile (
IN VOID *Context,
IN CHAR8 *SectionName,
IN CHAR8 *EntryName,
OUT EFI_GUID *Guid
)
{
CHAR8 *Value;
EFI_STATUS Status;
RETURN_STATUS RStatus;
if ((Context == NULL) || (SectionName == NULL) || (EntryName == NULL) || (Guid == NULL)) {
return EFI_INVALID_PARAMETER;
}
Status = GetStringFromDataFile (
Context,
SectionName,
EntryName,
&Value
);
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
ASSERT (Value != NULL);
RStatus = AsciiStrToGuid (Value, Guid);
if (RETURN_ERROR (RStatus) || (Value[GUID_STRING_LENGTH] != '\0')) {
return EFI_NOT_FOUND;
}
return EFI_SUCCESS;
}
/**
Get section entry decimal UINTN value.
@param[in] Context INI Config file context.
@param[in] SectionName Section name.
@param[in] EntryName Section entry name.
@param[out] Data Point to the got decimal UINTN value.
@retval EFI_SUCCESS Section entry decimal UINTN value is got.
@retval EFI_NOT_FOUND Section is not found.
**/
EFI_STATUS
EFIAPI
GetDecimalUintnFromDataFile (
IN VOID *Context,
IN CHAR8 *SectionName,
IN CHAR8 *EntryName,
OUT UINTN *Data
)
{
CHAR8 *Value;
EFI_STATUS Status;
if ((Context == NULL) || (SectionName == NULL) || (EntryName == NULL) || (Data == NULL)) {
return EFI_INVALID_PARAMETER;
}
Status = GetStringFromDataFile (
Context,
SectionName,
EntryName,
&Value
);
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
ASSERT (Value != NULL);
if (!IsValidDecimalString (Value, AsciiStrLen (Value))) {
return EFI_NOT_FOUND;
}
*Data = AsciiStrDecimalToUintn (Value);
return EFI_SUCCESS;
}
/**
Get section entry hexadecimal UINTN value.
@param[in] Context INI Config file context.
@param[in] SectionName Section name.
@param[in] EntryName Section entry name.
@param[out] Data Point to the got hexadecimal UINTN value.
@retval EFI_SUCCESS Section entry hexadecimal UINTN value is got.
@retval EFI_NOT_FOUND Section is not found.
**/
EFI_STATUS
EFIAPI
GetHexUintnFromDataFile (
IN VOID *Context,
IN CHAR8 *SectionName,
IN CHAR8 *EntryName,
OUT UINTN *Data
)
{
CHAR8 *Value;
EFI_STATUS Status;
if ((Context == NULL) || (SectionName == NULL) || (EntryName == NULL) || (Data == NULL)) {
return EFI_INVALID_PARAMETER;
}
Status = GetStringFromDataFile (
Context,
SectionName,
EntryName,
&Value
);
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
ASSERT (Value != NULL);
if (!IsValidHexString (Value, AsciiStrLen (Value))) {
return EFI_NOT_FOUND;
}
*Data = AsciiStrHexToUintn (Value);
return EFI_SUCCESS;
}
/**
Get section entry hexadecimal UINT64 value.
@param[in] Context INI Config file context.
@param[in] SectionName Section name.
@param[in] EntryName Section entry name.
@param[out] Data Point to the got hexadecimal UINT64 value.
@retval EFI_SUCCESS Section entry hexadecimal UINT64 value is got.
@retval EFI_NOT_FOUND Section is not found.
**/
EFI_STATUS
EFIAPI
GetHexUint64FromDataFile (
IN VOID *Context,
IN CHAR8 *SectionName,
IN CHAR8 *EntryName,
OUT UINT64 *Data
)
{
CHAR8 *Value;
EFI_STATUS Status;
if ((Context == NULL) || (SectionName == NULL) || (EntryName == NULL) || (Data == NULL)) {
return EFI_INVALID_PARAMETER;
}
Status = GetStringFromDataFile (
Context,
SectionName,
EntryName,
&Value
);
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
ASSERT (Value != NULL);
if (!IsValidHexString (Value, AsciiStrLen (Value))) {
return EFI_NOT_FOUND;
}
*Data = AsciiStrHexToUint64 (Value);
return EFI_SUCCESS;
}
/**
Close an INI config file and free the context.
@param[in] Context INI Config file context.
**/
VOID
EFIAPI
CloseIniFile (
IN VOID *Context
)
{
INI_PARSING_LIB_CONTEXT *IniContext;
if (Context == NULL) {
return;
}
IniContext = Context;
FreeAllList (IniContext->SectionHead, IniContext->CommentHead);
return;
}