mirror of https://github.com/acidanthera/audk.git
1278 lines
33 KiB
C
1278 lines
33 KiB
C
/** @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;
|
|
}
|