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