/** @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; | |
} |