--- /dev/null
+/** @file\r
+ This library parses the INI configuration file.\r
+\r
+ The INI file format is:\r
+ ================\r
+ [SectionName]\r
+ EntryName=EntryValue\r
+ ================\r
+\r
+ Where:\r
+ 1) SectionName is an ASCII string. The valid format is [A-Za-z0-9_]+\r
+ 2) EntryName is an ASCII string. The valid format is [A-Za-z0-9_]+\r
+ 3) EntryValue can be:\r
+ 3.1) an ASCII String. The valid format is [A-Za-z0-9_]+\r
+ 3.2) a GUID. The valid format is xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, where x is [A-Fa-f0-9]\r
+ 3.3) a decimal value. The valid format is [0-9]+\r
+ 3.4) a heximal value. The valid format is 0x[A-Fa-f0-9]+\r
+ 4) '#' or ';' can be used as comment at anywhere.\r
+ 5) TAB(0x20) or SPACE(0x9) can be used as separator.\r
+ 6) LF(\n, 0xA) or CR(\r, 0xD) can be used as line break.\r
+\r
+ Caution: This module requires additional review when modified.\r
+ This driver will have external input - INI data file.\r
+\r
+ OpenIniFile(), PreProcessDataFile(), ProfileGetSection(), ProfileGetEntry()\r
+ will receive untrusted input and do basic validation.\r
+\r
+ Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions\r
+ of the BSD License which accompanies this distribution. The\r
+ full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Uefi.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+\r
+#define IS_HYPHEN(a) ((a) == '-')\r
+#define IS_NULL(a) ((a) == '\0')\r
+\r
+// This is default allocation. Reallocation will happen if it is not enough.\r
+#define MAX_LINE_LENGTH 512\r
+\r
+typedef struct _SECTION_ITEM SECTION_ITEM;\r
+struct _SECTION_ITEM {\r
+ CHAR8 *PtrSection;\r
+ UINTN SecNameLen;\r
+ CHAR8 *PtrEntry;\r
+ CHAR8 *PtrValue;\r
+ SECTION_ITEM *PtrNext;\r
+};\r
+\r
+typedef struct _COMMENT_LINE COMMENT_LINE;\r
+struct _COMMENT_LINE {\r
+ CHAR8 *PtrComment;\r
+ COMMENT_LINE *PtrNext;\r
+};\r
+\r
+typedef struct {\r
+ SECTION_ITEM *SectionHead;\r
+ COMMENT_LINE *CommentHead;\r
+} INI_PARSING_LIB_CONTEXT;\r
+\r
+/**\r
+ Return if the digital char is valid.\r
+\r
+ @param[in] DigitalChar The digital char to be checked.\r
+ @param[in] IncludeHex If it include HEX char.\r
+\r
+ @retval TRUE The digital char is valid.\r
+ @retval FALSE The digital char is invalid.\r
+**/\r
+BOOLEAN\r
+IsValidDigitalChar (\r
+ IN CHAR8 DigitalChar,\r
+ IN BOOLEAN IncludeHex\r
+ )\r
+{\r
+ if (DigitalChar >= '0' && DigitalChar <= '9') {\r
+ return TRUE;\r
+ }\r
+ if (IncludeHex) {\r
+ if (DigitalChar >= 'a' && DigitalChar <= 'f') {\r
+ return TRUE;\r
+ }\r
+ if (DigitalChar >= 'A' && DigitalChar <= 'F') {\r
+ return TRUE;\r
+ }\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Return if the name char is valid.\r
+\r
+ @param[in] NameChar The name char to be checked.\r
+\r
+ @retval TRUE The name char is valid.\r
+ @retval FALSE The name char is invalid.\r
+**/\r
+BOOLEAN\r
+IsValidNameChar (\r
+ IN CHAR8 NameChar\r
+ )\r
+{\r
+ if (NameChar >= 'a' && NameChar <= 'z') {\r
+ return TRUE;\r
+ }\r
+ if (NameChar >= 'A' && NameChar <= 'Z') {\r
+ return TRUE;\r
+ }\r
+ if (NameChar >= '0' && NameChar <= '9') {\r
+ return TRUE;\r
+ }\r
+ if (NameChar == '_') {\r
+ return TRUE;\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Return if the digital string is valid.\r
+\r
+ @param[in] Digital The digital to be checked.\r
+ @param[in] Length The length of digital string in bytes.\r
+ @param[in] IncludeHex If it include HEX char.\r
+\r
+ @retval TRUE The digital string is valid.\r
+ @retval FALSE The digital string is invalid.\r
+**/\r
+BOOLEAN\r
+IsValidDigital (\r
+ IN CHAR8 *Digital,\r
+ IN UINTN Length,\r
+ IN BOOLEAN IncludeHex\r
+ )\r
+{\r
+ UINTN Index;\r
+ for (Index = 0; Index < Length; Index++) {\r
+ if (!IsValidDigitalChar(Digital[Index], IncludeHex)) {\r
+ return FALSE;\r
+ }\r
+ }\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Return if the decimal string is valid.\r
+\r
+ @param[in] Decimal The decimal string to be checked.\r
+ @param[in] Length The length of decimal string in bytes.\r
+\r
+ @retval TRUE The decimal string is valid.\r
+ @retval FALSE The decimal string is invalid.\r
+**/\r
+BOOLEAN\r
+IsValidDecimalString (\r
+ IN CHAR8 *Decimal,\r
+ IN UINTN Length\r
+ )\r
+{\r
+ return IsValidDigital(Decimal, Length, FALSE);\r
+}\r
+\r
+/**\r
+ Return if the heximal string is valid.\r
+\r
+ @param[in] Hex The heximal string to be checked.\r
+ @param[in] Length The length of heximal string in bytes.\r
+\r
+ @retval TRUE The heximal string is valid.\r
+ @retval FALSE The heximal string is invalid.\r
+**/\r
+BOOLEAN\r
+IsValidHexString (\r
+ IN CHAR8 *Hex,\r
+ IN UINTN Length\r
+ )\r
+{\r
+ if (Length <= 2) {\r
+ return FALSE;\r
+ }\r
+ if (Hex[0] != '0') {\r
+ return FALSE;\r
+ }\r
+ if (Hex[1] != 'x' && Hex[1] != 'X') {\r
+ return FALSE;\r
+ }\r
+ return IsValidDigital(&Hex[2], Length - 2, TRUE);\r
+}\r
+\r
+/**\r
+ Return if the name string is valid.\r
+\r
+ @param[in] Name The name to be checked.\r
+ @param[in] Length The length of name string in bytes.\r
+\r
+ @retval TRUE The name string is valid.\r
+ @retval FALSE The name string is invalid.\r
+**/\r
+BOOLEAN\r
+IsValidName (\r
+ IN CHAR8 *Name,\r
+ IN UINTN Length\r
+ )\r
+{\r
+ UINTN Index;\r
+ for (Index = 0; Index < Length; Index++) {\r
+ if (!IsValidNameChar(Name[Index])) {\r
+ return FALSE;\r
+ }\r
+ }\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Return if the value string is valid GUID.\r
+\r
+ @param[in] Value The value to be checked.\r
+ @param[in] Length The length of value string in bytes.\r
+\r
+ @retval TRUE The value string is valid GUID.\r
+ @retval FALSE The value string is invalid GUID.\r
+**/\r
+BOOLEAN\r
+IsValidGuid (\r
+ IN CHAR8 *Value,\r
+ IN UINTN Length\r
+ )\r
+{\r
+ if (Length != sizeof("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") - 1) {\r
+ return FALSE;\r
+ }\r
+ if (!IS_HYPHEN(Value[8])) {\r
+ return FALSE;\r
+ }\r
+ if (!IS_HYPHEN(Value[13])) {\r
+ return FALSE;\r
+ }\r
+ if (!IS_HYPHEN(Value[18])) {\r
+ return FALSE;\r
+ }\r
+ if (!IS_HYPHEN(Value[23])) {\r
+ return FALSE;\r
+ }\r
+ if (!IsValidDigital(&Value[0], 8, TRUE)) {\r
+ return FALSE;\r
+ }\r
+ if (!IsValidDigital(&Value[9], 4, TRUE)) {\r
+ return FALSE;\r
+ }\r
+ if (!IsValidDigital(&Value[14], 4, TRUE)) {\r
+ return FALSE;\r
+ }\r
+ if (!IsValidDigital(&Value[19], 4, TRUE)) {\r
+ return FALSE;\r
+ }\r
+ if (!IsValidDigital(&Value[24], 12, TRUE)) {\r
+ return FALSE;\r
+ }\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Return if the value string is valid.\r
+\r
+ @param[in] Value The value to be checked.\r
+ @param[in] Length The length of value string in bytes.\r
+\r
+ @retval TRUE The name string is valid.\r
+ @retval FALSE The name string is invalid.\r
+**/\r
+BOOLEAN\r
+IsValidValue (\r
+ IN CHAR8 *Value,\r
+ IN UINTN Length\r
+ )\r
+{\r
+ if (IsValidName(Value, Length) || IsValidGuid(Value, Length)) {\r
+ return TRUE;\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Dump an INI config file context.\r
+\r
+ @param[in] Context INI Config file context.\r
+**/\r
+VOID\r
+DumpIniSection (\r
+ IN VOID *Context\r
+ )\r
+{\r
+ INI_PARSING_LIB_CONTEXT *IniContext;\r
+ SECTION_ITEM *PtrSection;\r
+ SECTION_ITEM *Section;\r
+\r
+ if (Context == NULL) {\r
+ return;\r
+ }\r
+\r
+ IniContext = Context;\r
+ Section = IniContext->SectionHead;\r
+\r
+ while (Section != NULL) {\r
+ PtrSection = Section;\r
+ Section = Section->PtrNext;\r
+ if (PtrSection->PtrSection != NULL) {\r
+ DEBUG((DEBUG_VERBOSE, "Section - %a\n", PtrSection->PtrSection));\r
+ }\r
+ if (PtrSection->PtrEntry != NULL) {\r
+ DEBUG ((DEBUG_VERBOSE, " Entry - %a\n", PtrSection->PtrEntry));\r
+ }\r
+ if (PtrSection->PtrValue != NULL) {\r
+ DEBUG((DEBUG_VERBOSE, " Value - %a\n", PtrSection->PtrValue));\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Copy one line data from buffer data to the line buffer.\r
+\r
+ @param[in] Buffer Buffer data.\r
+ @param[in] BufferSize Buffer Size.\r
+ @param[in, out] LineBuffer Line buffer to store the found line data.\r
+ @param[in, out] LineSize On input, size of the input line buffer.\r
+ On output, size of the actual line buffer.\r
+\r
+ @retval EFI_BUFFER_TOO_SMALL The size of input line buffer is not enough.\r
+ @retval EFI_SUCCESS Copy line data into the line buffer.\r
+\r
+**/\r
+EFI_STATUS\r
+ProfileGetLine (\r
+ IN UINT8 *Buffer,\r
+ IN UINTN BufferSize,\r
+ IN OUT UINT8 *LineBuffer,\r
+ IN OUT UINTN *LineSize\r
+ )\r
+{\r
+ UINTN Length;\r
+ UINT8 *PtrBuf;\r
+ UINTN PtrEnd;\r
+\r
+ PtrBuf = Buffer;\r
+ PtrEnd = (UINTN)Buffer + BufferSize;\r
+\r
+ //\r
+ // 0x0D indicates a line break. Otherwise there is no line break\r
+ //\r
+ while ((UINTN)PtrBuf < PtrEnd) {\r
+ if (*PtrBuf == 0x0D || *PtrBuf == 0x0A) {\r
+ break;\r
+ }\r
+ PtrBuf++;\r
+ }\r
+\r
+ if ((UINTN)PtrBuf >= (PtrEnd - 1)) {\r
+ //\r
+ // The buffer ends without any line break\r
+ // or it is the last character of the buffer\r
+ //\r
+ Length = BufferSize;\r
+ } else if (*(PtrBuf + 1) == 0x0A) {\r
+ //\r
+ // Further check if a 0x0A follows. If yes, count 0xA\r
+ //\r
+ Length = (UINTN) PtrBuf - (UINTN) Buffer + 2;\r
+ } else {\r
+ Length = (UINTN) PtrBuf - (UINTN) Buffer + 1;\r
+ }\r
+\r
+ if (Length > (*LineSize)) {\r
+ *LineSize = Length;\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ SetMem (LineBuffer, *LineSize, 0x0);\r
+ *LineSize = Length;\r
+ CopyMem (LineBuffer, Buffer, Length);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Trim Buffer by removing all CR, LF, TAB, and SPACE chars in its head and tail.\r
+\r
+ @param[in, out] Buffer On input, buffer data to be trimed.\r
+ On output, the trimmed buffer.\r
+ @param[in, out] BufferSize On input, size of original buffer data.\r
+ On output, size of the trimmed buffer.\r
+\r
+**/\r
+VOID\r
+ProfileTrim (\r
+ IN OUT UINT8 *Buffer,\r
+ IN OUT UINTN *BufferSize\r
+ )\r
+{\r
+ UINTN Length;\r
+ UINT8 *PtrBuf;\r
+ UINT8 *PtrEnd;\r
+\r
+ if (*BufferSize == 0) {\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Trim the tail first, include CR, LF, TAB, and SPACE.\r
+ //\r
+ Length = *BufferSize;\r
+ PtrBuf = (UINT8 *) ((UINTN) Buffer + Length - 1);\r
+ while (PtrBuf >= Buffer) {\r
+ if ((*PtrBuf != 0x0D) && (*PtrBuf != 0x0A )\r
+ && (*PtrBuf != 0x20) && (*PtrBuf != 0x09)) {\r
+ break;\r
+ }\r
+ PtrBuf --;\r
+ }\r
+\r
+ //\r
+ // all spaces, a blank line, return directly;\r
+ //\r
+ if (PtrBuf < Buffer) {\r
+ *BufferSize = 0;\r
+ return;\r
+ }\r
+\r
+ Length = (UINTN)PtrBuf - (UINTN)Buffer + 1;\r
+ PtrEnd = PtrBuf;\r
+ PtrBuf = Buffer;\r
+\r
+ //\r
+ // Now skip the heading CR, LF, TAB and SPACE\r
+ //\r
+ while (PtrBuf <= PtrEnd) {\r
+ if ((*PtrBuf != 0x0D) && (*PtrBuf != 0x0A )\r
+ && (*PtrBuf != 0x20) && (*PtrBuf != 0x09)) {\r
+ break;\r
+ }\r
+ PtrBuf++;\r
+ }\r
+\r
+ //\r
+ // If no heading CR, LF, TAB or SPACE, directly return\r
+ //\r
+ if (PtrBuf == Buffer) {\r
+ *BufferSize = Length;\r
+ return;\r
+ }\r
+\r
+ *BufferSize = (UINTN)PtrEnd - (UINTN)PtrBuf + 1;\r
+\r
+ //\r
+ // The first Buffer..PtrBuf characters are CR, LF, TAB or SPACE.\r
+ // Now move out all these characters.\r
+ //\r
+ while (PtrBuf <= PtrEnd) {\r
+ *Buffer = *PtrBuf;\r
+ Buffer++;\r
+ PtrBuf++;\r
+ }\r
+\r
+ return;\r
+}\r
+\r
+/**\r
+ Insert new comment item into comment head.\r
+\r
+ @param[in] Buffer Comment buffer to be added.\r
+ @param[in] BufferSize Size of comment buffer.\r
+ @param[in, out] CommentHead Comment Item head entry.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES No enough memory is allocated.\r
+ @retval EFI_SUCCESS New comment item is inserted.\r
+\r
+**/\r
+EFI_STATUS\r
+ProfileGetComments (\r
+ IN UINT8 *Buffer,\r
+ IN UINTN BufferSize,\r
+ IN OUT COMMENT_LINE **CommentHead\r
+ )\r
+{\r
+ COMMENT_LINE *CommentItem;\r
+\r
+ CommentItem = NULL;\r
+ CommentItem = AllocatePool (sizeof (COMMENT_LINE));\r
+ if (CommentItem == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ CommentItem->PtrNext = *CommentHead;\r
+ *CommentHead = CommentItem;\r
+\r
+ //\r
+ // Add a trailing '\0'\r
+ //\r
+ CommentItem->PtrComment = AllocatePool (BufferSize + 1);\r
+ if (CommentItem->PtrComment == NULL) {\r
+ FreePool (CommentItem);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ CopyMem (CommentItem->PtrComment, Buffer, BufferSize);\r
+ *(CommentItem->PtrComment + BufferSize) = '\0';\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Add new section item into Section head.\r
+\r
+ @param[in] Buffer Section item data buffer.\r
+ @param[in] BufferSize Size of section item.\r
+ @param[in, out] SectionHead Section item head entry.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES No enough memory is allocated.\r
+ @retval EFI_SUCCESS Section item is NULL or Section item is added.\r
+\r
+**/\r
+EFI_STATUS\r
+ProfileGetSection (\r
+ IN UINT8 *Buffer,\r
+ IN UINTN BufferSize,\r
+ IN OUT SECTION_ITEM **SectionHead\r
+ )\r
+{\r
+ SECTION_ITEM *SectionItem;\r
+ UINTN Length;\r
+ UINT8 *PtrBuf;\r
+ UINT8 *PtrEnd;\r
+\r
+ ASSERT(BufferSize >= 1);\r
+ //\r
+ // The first character of Buffer is '[', now we want for ']'\r
+ //\r
+ PtrEnd = (UINT8 *)((UINTN)Buffer + BufferSize - 1);\r
+ PtrBuf = (UINT8 *)((UINTN)Buffer + 1);\r
+ while (PtrBuf <= PtrEnd) {\r
+ if (*PtrBuf == ']') {\r
+ break;\r
+ }\r
+ PtrBuf ++;\r
+ }\r
+ if (PtrBuf > PtrEnd) {\r
+ //\r
+ // Not found. Invalid line\r
+ //\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ if (PtrBuf <= Buffer + 1) {\r
+ // Empty name\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // excluding the heading '[' and tailing ']'\r
+ //\r
+ Length = PtrBuf - Buffer - 1;\r
+ ProfileTrim (\r
+ Buffer + 1,\r
+ &Length\r
+ );\r
+\r
+ //\r
+ // Invalid line if the section name is null\r
+ //\r
+ if (Length == 0) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ if (!IsValidName((CHAR8 *)Buffer + 1, Length)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ SectionItem = AllocatePool (sizeof (SECTION_ITEM));\r
+ if (SectionItem == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ SectionItem->PtrSection = NULL;\r
+ SectionItem->SecNameLen = Length;\r
+ SectionItem->PtrEntry = NULL;\r
+ SectionItem->PtrValue = NULL;\r
+ SectionItem->PtrNext = *SectionHead;\r
+ *SectionHead = SectionItem;\r
+\r
+ //\r
+ // Add a trailing '\0'\r
+ //\r
+ SectionItem->PtrSection = AllocatePool (Length + 1);\r
+ if (SectionItem->PtrSection == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // excluding the heading '['\r
+ //\r
+ CopyMem (SectionItem->PtrSection, Buffer + 1, Length);\r
+ *(SectionItem->PtrSection + Length) = '\0';\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Add new section entry and entry value into Section head.\r
+\r
+ @param[in] Buffer Section entry data buffer.\r
+ @param[in] BufferSize Size of section entry.\r
+ @param[in, out] SectionHead Section item head entry.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES No enough memory is allocated.\r
+ @retval EFI_SUCCESS Section entry is added.\r
+ @retval EFI_NOT_FOUND Section entry is not found.\r
+ @retval EFI_INVALID_PARAMETER Section entry is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+ProfileGetEntry (\r
+ IN UINT8 *Buffer,\r
+ IN UINTN BufferSize,\r
+ IN OUT SECTION_ITEM **SectionHead\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ SECTION_ITEM *SectionItem;\r
+ SECTION_ITEM *PtrSection;\r
+ UINTN Length;\r
+ UINT8 *PtrBuf;\r
+ UINT8 *PtrEnd;\r
+\r
+ Status = EFI_SUCCESS;\r
+ PtrBuf = Buffer;\r
+ PtrEnd = (UINT8 *) ((UINTN)Buffer + BufferSize - 1);\r
+\r
+ //\r
+ // First search for '='\r
+ //\r
+ while (PtrBuf <= PtrEnd) {\r
+ if (*PtrBuf == '=') {\r
+ break;\r
+ }\r
+ PtrBuf++;\r
+ }\r
+ if (PtrBuf > PtrEnd) {\r
+ //\r
+ // Not found. Invalid line\r
+ //\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ if (PtrBuf <= Buffer) {\r
+ // Empty name\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // excluding the tailing '='\r
+ //\r
+ Length = PtrBuf - Buffer;\r
+ ProfileTrim (\r
+ Buffer,\r
+ &Length\r
+ );\r
+\r
+ //\r
+ // Invalid line if the entry name is null\r
+ //\r
+ if (Length == 0) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ if (!IsValidName((CHAR8 *)Buffer, Length)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Omit this line if no section header has been found before\r
+ //\r
+ if (*SectionHead == NULL) {\r
+ return Status;\r
+ }\r
+ PtrSection = *SectionHead;\r
+\r
+ SectionItem = AllocatePool (sizeof (SECTION_ITEM));\r
+ if (SectionItem == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ SectionItem->PtrSection = NULL;\r
+ SectionItem->PtrEntry = NULL;\r
+ SectionItem->PtrValue = NULL;\r
+ SectionItem->SecNameLen = PtrSection->SecNameLen;\r
+ SectionItem->PtrNext = *SectionHead;\r
+ *SectionHead = SectionItem;\r
+\r
+ //\r
+ // SectionName, add a trailing '\0'\r
+ //\r
+ SectionItem->PtrSection = AllocatePool (PtrSection->SecNameLen + 1);\r
+ if (SectionItem->PtrSection == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ CopyMem (SectionItem->PtrSection, PtrSection->PtrSection, PtrSection->SecNameLen + 1);\r
+\r
+ //\r
+ // EntryName, add a trailing '\0'\r
+ //\r
+ SectionItem->PtrEntry = AllocatePool (Length + 1);\r
+ if (SectionItem->PtrEntry == NULL) {\r
+ FreePool(SectionItem->PtrSection);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ CopyMem (SectionItem->PtrEntry, Buffer, Length);\r
+ *(SectionItem->PtrEntry + Length) = '\0';\r
+\r
+ //\r
+ // Next search for '#' or ';'\r
+ //\r
+ PtrBuf = PtrBuf + 1;\r
+ Buffer = PtrBuf;\r
+ while (PtrBuf <= PtrEnd) {\r
+ if (*PtrBuf == '#' || *PtrBuf == ';') {\r
+ break;\r
+ }\r
+ PtrBuf++;\r
+ }\r
+ if (PtrBuf <= Buffer) {\r
+ // Empty name\r
+ FreePool(SectionItem->PtrEntry);\r
+ FreePool(SectionItem->PtrSection);\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ Length = PtrBuf - Buffer;\r
+ ProfileTrim (\r
+ Buffer,\r
+ &Length\r
+ );\r
+\r
+ //\r
+ // Invalid line if the entry value is null\r
+ //\r
+ if (Length == 0) {\r
+ FreePool(SectionItem->PtrEntry);\r
+ FreePool(SectionItem->PtrSection);\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ if (!IsValidValue((CHAR8 *)Buffer, Length)) {\r
+ FreePool(SectionItem->PtrEntry);\r
+ FreePool(SectionItem->PtrSection);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // EntryValue, add a trailing '\0'\r
+ //\r
+ SectionItem->PtrValue = AllocatePool (Length + 1);\r
+ if (SectionItem->PtrValue == NULL) {\r
+ FreePool(SectionItem->PtrEntry);\r
+ FreePool(SectionItem->PtrSection);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ CopyMem (SectionItem->PtrValue, Buffer, Length);\r
+ *(SectionItem->PtrValue + Length) = '\0';\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Free all comment entry and section entry.\r
+\r
+ @param[in] Section Section entry list.\r
+ @param[in] Comment Comment entry list.\r
+\r
+**/\r
+VOID\r
+FreeAllList (\r
+ IN SECTION_ITEM *Section,\r
+ IN COMMENT_LINE *Comment\r
+ )\r
+{\r
+ SECTION_ITEM *PtrSection;\r
+ COMMENT_LINE *PtrComment;\r
+\r
+ while (Section != NULL) {\r
+ PtrSection = Section;\r
+ Section = Section->PtrNext;\r
+ if (PtrSection->PtrEntry != NULL) {\r
+ FreePool (PtrSection->PtrEntry);\r
+ }\r
+ if (PtrSection->PtrSection != NULL) {\r
+ FreePool (PtrSection->PtrSection);\r
+ }\r
+ if (PtrSection->PtrValue != NULL) {\r
+ FreePool (PtrSection->PtrValue);\r
+ }\r
+ FreePool (PtrSection);\r
+ }\r
+\r
+ while (Comment != NULL) {\r
+ PtrComment = Comment;\r
+ Comment = Comment->PtrNext;\r
+ if (PtrComment->PtrComment != NULL) {\r
+ FreePool (PtrComment->PtrComment);\r
+ }\r
+ FreePool (PtrComment);\r
+ }\r
+\r
+ return;\r
+}\r
+\r
+/**\r
+ Get section entry value.\r
+\r
+ @param[in] Section Section entry list.\r
+ @param[in] SectionName Section name.\r
+ @param[in] EntryName Section entry name.\r
+ @param[out] EntryValue Point to the got entry value.\r
+\r
+ @retval EFI_NOT_FOUND Section is not found.\r
+ @retval EFI_SUCCESS Section entry value is got.\r
+\r
+**/\r
+EFI_STATUS\r
+UpdateGetProfileString (\r
+ IN SECTION_ITEM *Section,\r
+ IN CHAR8 *SectionName,\r
+ IN CHAR8 *EntryName,\r
+ OUT CHAR8 **EntryValue\r
+ )\r
+{\r
+ *EntryValue = NULL;\r
+\r
+ while (Section != NULL) {\r
+ if (AsciiStrCmp ((CONST CHAR8 *) Section->PtrSection, (CONST CHAR8 *) SectionName) == 0) {\r
+ if (Section->PtrEntry != NULL) {\r
+ if (AsciiStrCmp ((CONST CHAR8 *) Section->PtrEntry, (CONST CHAR8 *) EntryName) == 0) {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ Section = Section->PtrNext;\r
+ }\r
+\r
+ if (Section == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ *EntryValue = Section->PtrValue;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Converts a list of string to a specified buffer.\r
+\r
+ @param[out] Buf The output buffer that contains the string.\r
+ @param[in] BufferLength The length of the buffer\r
+ @param[in] Str The input string that contains the hex number\r
+\r
+ @retval EFI_SUCCESS The string was successfully converted to the buffer.\r
+\r
+**/\r
+EFI_STATUS\r
+AsciiStrToBuf (\r
+ OUT UINT8 *Buf,\r
+ IN UINTN BufferLength,\r
+ IN CHAR8 *Str\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINTN StrLength;\r
+ UINT8 Digit;\r
+ UINT8 Byte;\r
+\r
+ Digit = 0;\r
+\r
+ //\r
+ // Two hex char make up one byte\r
+ //\r
+ StrLength = BufferLength * 2;\r
+\r
+ for(Index = 0; Index < StrLength; Index++, Str++) {\r
+\r
+ if ((*Str >= 'a') && (*Str <= 'f')) {\r
+ Digit = (UINT8) (*Str - 'a' + 0x0A);\r
+ } else if ((*Str >= 'A') && (*Str <= 'F')) {\r
+ Digit = (UINT8) (*Str - 'A' + 0x0A);\r
+ } else if ((*Str >= '0') && (*Str <= '9')) {\r
+ Digit = (UINT8) (*Str - '0');\r
+ } else {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // For odd characters, write the upper nibble for each buffer byte,\r
+ // and for even characters, the lower nibble.\r
+ //\r
+ if ((Index & 1) == 0) {\r
+ Byte = (UINT8) (Digit << 4);\r
+ } else {\r
+ Byte = Buf[Index / 2];\r
+ Byte &= 0xF0;\r
+ Byte = (UINT8) (Byte | Digit);\r
+ }\r
+\r
+ Buf[Index / 2] = Byte;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Converts a string to GUID value.\r
+ Guid Format is xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r
+\r
+ @param[in] Str The registry format GUID string that contains the GUID value.\r
+ @param[out] Guid A pointer to the converted GUID value.\r
+\r
+ @retval EFI_SUCCESS The GUID string was successfully converted to the GUID value.\r
+ @retval EFI_UNSUPPORTED The input string is not in registry format.\r
+ @return others Some error occurred when converting part of GUID value.\r
+\r
+**/\r
+EFI_STATUS\r
+AsciiStrToGuid (\r
+ IN CHAR8 *Str,\r
+ OUT EFI_GUID *Guid\r
+ )\r
+{\r
+ //\r
+ // Get the first UINT32 data\r
+ //\r
+ Guid->Data1 = (UINT32) AsciiStrHexToUint64 (Str);\r
+ while (!IS_HYPHEN (*Str) && !IS_NULL (*Str)) {\r
+ Str ++;\r
+ }\r
+\r
+ if (IS_HYPHEN (*Str)) {\r
+ Str++;\r
+ } else {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Get the second UINT16 data\r
+ //\r
+ Guid->Data2 = (UINT16) AsciiStrHexToUint64 (Str);\r
+ while (!IS_HYPHEN (*Str) && !IS_NULL (*Str)) {\r
+ Str ++;\r
+ }\r
+\r
+ if (IS_HYPHEN (*Str)) {\r
+ Str++;\r
+ } else {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Get the third UINT16 data\r
+ //\r
+ Guid->Data3 = (UINT16) AsciiStrHexToUint64 (Str);\r
+ while (!IS_HYPHEN (*Str) && !IS_NULL (*Str)) {\r
+ Str ++;\r
+ }\r
+\r
+ if (IS_HYPHEN (*Str)) {\r
+ Str++;\r
+ } else {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Get the following 8 bytes data\r
+ //\r
+ AsciiStrToBuf (&Guid->Data4[0], 2, Str);\r
+ //\r
+ // Skip 2 byte hex chars\r
+ //\r
+ Str += 2 * 2;\r
+\r
+ if (IS_HYPHEN (*Str)) {\r
+ Str++;\r
+ } else {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ AsciiStrToBuf (&Guid->Data4[2], 6, Str);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Pre process config data buffer into Section entry list and Comment entry list.\r
+\r
+ @param[in] DataBuffer Config raw file buffer.\r
+ @param[in] BufferSize Size of raw buffer.\r
+ @param[in, out] SectionHead Pointer to the section entry list.\r
+ @param[in, out] CommentHead Pointer to the comment entry list.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES No enough memory is allocated.\r
+ @retval EFI_SUCCESS Config data buffer is preprocessed.\r
+ @retval EFI_NOT_FOUND Config data buffer is invalid, because Section or Entry is not found.\r
+ @retval EFI_INVALID_PARAMETER Config data buffer is invalid, because Section or Entry is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+PreProcessDataFile (\r
+ IN UINT8 *DataBuffer,\r
+ IN UINTN BufferSize,\r
+ IN OUT SECTION_ITEM **SectionHead,\r
+ IN OUT COMMENT_LINE **CommentHead\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CHAR8 *Source;\r
+ CHAR8 *CurrentPtr;\r
+ CHAR8 *BufferEnd;\r
+ CHAR8 *PtrLine;\r
+ UINTN LineLength;\r
+ UINTN SourceLength;\r
+ UINTN MaxLineLength;\r
+\r
+ *SectionHead = NULL;\r
+ *CommentHead = NULL;\r
+ BufferEnd = (CHAR8 *) ( (UINTN) DataBuffer + BufferSize);\r
+ CurrentPtr = (CHAR8 *) DataBuffer;\r
+ MaxLineLength = MAX_LINE_LENGTH;\r
+ Status = EFI_SUCCESS;\r
+\r
+ PtrLine = AllocatePool (MaxLineLength);\r
+ if (PtrLine == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ while (CurrentPtr < BufferEnd) {\r
+ Source = CurrentPtr;\r
+ SourceLength = (UINTN)BufferEnd - (UINTN)CurrentPtr;\r
+ LineLength = MaxLineLength;\r
+ //\r
+ // With the assumption that line length is less than 512\r
+ // characters. Otherwise BUFFER_TOO_SMALL will be returned.\r
+ //\r
+ Status = ProfileGetLine (\r
+ (UINT8 *) Source,\r
+ SourceLength,\r
+ (UINT8 *) PtrLine,\r
+ &LineLength\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status == EFI_BUFFER_TOO_SMALL) {\r
+ //\r
+ // If buffer too small, re-allocate the buffer according\r
+ // to the returned LineLength and try again.\r
+ //\r
+ FreePool (PtrLine);\r
+ PtrLine = NULL;\r
+ PtrLine = AllocatePool (LineLength);\r
+ if (PtrLine == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ break;\r
+ }\r
+ SourceLength = LineLength;\r
+ Status = ProfileGetLine (\r
+ (UINT8 *) Source,\r
+ SourceLength,\r
+ (UINT8 *) PtrLine,\r
+ &LineLength\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+ MaxLineLength = LineLength;\r
+ } else {\r
+ break;\r
+ }\r
+ }\r
+ CurrentPtr = (CHAR8 *) ( (UINTN) CurrentPtr + LineLength);\r
+\r
+ //\r
+ // Line got. Trim the line before processing it.\r
+ //\r
+ ProfileTrim (\r
+ (UINT8 *) PtrLine,\r
+ &LineLength\r
+ );\r
+\r
+ //\r
+ // Blank line\r
+ //\r
+ if (LineLength == 0) {\r
+ continue;\r
+ }\r
+\r
+ if (PtrLine[0] == '#' || PtrLine[0] == ';') {\r
+ Status = ProfileGetComments (\r
+ (UINT8 *) PtrLine,\r
+ LineLength,\r
+ CommentHead\r
+ );\r
+ } else if (PtrLine[0] == '[') {\r
+ Status = ProfileGetSection (\r
+ (UINT8 *) PtrLine,\r
+ LineLength,\r
+ SectionHead\r
+ );\r
+ } else {\r
+ Status = ProfileGetEntry (\r
+ (UINT8 *) PtrLine,\r
+ LineLength,\r
+ SectionHead\r
+ );\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Free buffer\r
+ //\r
+ FreePool (PtrLine);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Open an INI config file and return a context.\r
+\r
+ @param[in] DataBuffer Config raw file buffer.\r
+ @param[in] BufferSize Size of raw buffer.\r
+\r
+ @return Config data buffer is opened and context is returned.\r
+ @retval NULL No enough memory is allocated.\r
+ @retval NULL Config data buffer is invalid.\r
+**/\r
+VOID *\r
+EFIAPI\r
+OpenIniFile (\r
+ IN UINT8 *DataBuffer,\r
+ IN UINTN BufferSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ INI_PARSING_LIB_CONTEXT *IniContext;\r
+\r
+ if (DataBuffer == NULL || BufferSize == 0) {\r
+ return NULL;\r
+ }\r
+\r
+ IniContext = AllocateZeroPool(sizeof(INI_PARSING_LIB_CONTEXT));\r
+ if (IniContext == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // First process the data buffer and get all sections and entries\r
+ //\r
+ Status = PreProcessDataFile (\r
+ DataBuffer,\r
+ BufferSize,\r
+ &IniContext->SectionHead,\r
+ &IniContext->CommentHead\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ FreePool(IniContext);\r
+ return NULL;\r
+ }\r
+ DEBUG_CODE_BEGIN ();\r
+ DumpIniSection(IniContext);\r
+ DEBUG_CODE_END ();\r
+ return IniContext;\r
+}\r
+\r
+/**\r
+ Get section entry string value.\r
+\r
+ @param[in] Context INI Config file context.\r
+ @param[in] SectionName Section name.\r
+ @param[in] EntryName Section entry name.\r
+ @param[out] EntryValue Point to the got entry string value.\r
+\r
+ @retval EFI_SUCCESS Section entry string value is got.\r
+ @retval EFI_NOT_FOUND Section is not found.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+GetStringFromDataFile(\r
+ IN VOID *Context,\r
+ IN CHAR8 *SectionName,\r
+ IN CHAR8 *EntryName,\r
+ OUT CHAR8 **EntryValue\r
+ )\r
+{\r
+ INI_PARSING_LIB_CONTEXT *IniContext;\r
+ EFI_STATUS Status;\r
+\r
+ if (Context == NULL || SectionName == NULL || EntryName == NULL || EntryValue == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ IniContext = Context;\r
+\r
+ *EntryValue = NULL;\r
+ Status = UpdateGetProfileString (\r
+ IniContext->SectionHead,\r
+ SectionName,\r
+ EntryName,\r
+ EntryValue\r
+ );\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Get section entry GUID value.\r
+\r
+ @param[in] Context INI Config file context.\r
+ @param[in] SectionName Section name.\r
+ @param[in] EntryName Section entry name.\r
+ @param[out] Guid Point to the got GUID value.\r
+\r
+ @retval EFI_SUCCESS Section entry GUID value is got.\r
+ @retval EFI_NOT_FOUND Section is not found.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+GetGuidFromDataFile (\r
+ IN VOID *Context,\r
+ IN CHAR8 *SectionName,\r
+ IN CHAR8 *EntryName,\r
+ OUT EFI_GUID *Guid\r
+ )\r
+{\r
+ CHAR8 *Value;\r
+ EFI_STATUS Status;\r
+\r
+ if (Context == NULL || SectionName == NULL || EntryName == NULL || Guid == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = GetStringFromDataFile(\r
+ Context,\r
+ SectionName,\r
+ EntryName,\r
+ &Value\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ if (!IsValidGuid(Value, AsciiStrLen(Value))) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ Status = AsciiStrToGuid(Value, Guid);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Get section entry decimal UINTN value.\r
+\r
+ @param[in] Context INI Config file context.\r
+ @param[in] SectionName Section name.\r
+ @param[in] EntryName Section entry name.\r
+ @param[out] Data Point to the got decimal UINTN value.\r
+\r
+ @retval EFI_SUCCESS Section entry decimal UINTN value is got.\r
+ @retval EFI_NOT_FOUND Section is not found.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+GetDecimalUintnFromDataFile (\r
+ IN VOID *Context,\r
+ IN CHAR8 *SectionName,\r
+ IN CHAR8 *EntryName,\r
+ OUT UINTN *Data\r
+ )\r
+{\r
+ CHAR8 *Value;\r
+ EFI_STATUS Status;\r
+\r
+ if (Context == NULL || SectionName == NULL || EntryName == NULL || Data == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = GetStringFromDataFile(\r
+ Context,\r
+ SectionName,\r
+ EntryName,\r
+ &Value\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ if (!IsValidDecimalString(Value, AsciiStrLen(Value))) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ *Data = AsciiStrDecimalToUintn(Value);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Get section entry heximal UINTN value.\r
+\r
+ @param[in] Context INI Config file context.\r
+ @param[in] SectionName Section name.\r
+ @param[in] EntryName Section entry name.\r
+ @param[out] Data Point to the got heximal UINTN value.\r
+\r
+ @retval EFI_SUCCESS Section entry heximal UINTN value is got.\r
+ @retval EFI_NOT_FOUND Section is not found.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+GetHexUintnFromDataFile (\r
+ IN VOID *Context,\r
+ IN CHAR8 *SectionName,\r
+ IN CHAR8 *EntryName,\r
+ OUT UINTN *Data\r
+ )\r
+{\r
+ CHAR8 *Value;\r
+ EFI_STATUS Status;\r
+\r
+ if (Context == NULL || SectionName == NULL || EntryName == NULL || Data == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = GetStringFromDataFile(\r
+ Context,\r
+ SectionName,\r
+ EntryName,\r
+ &Value\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ if (!IsValidHexString(Value, AsciiStrLen(Value))) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ *Data = AsciiStrHexToUintn(Value);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Get section entry heximal UINT64 value.\r
+\r
+ @param[in] Context INI Config file context.\r
+ @param[in] SectionName Section name.\r
+ @param[in] EntryName Section entry name.\r
+ @param[out] Data Point to the got heximal UINT64 value.\r
+\r
+ @retval EFI_SUCCESS Section entry heximal UINT64 value is got.\r
+ @retval EFI_NOT_FOUND Section is not found.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+GetHexUint64FromDataFile (\r
+ IN VOID *Context,\r
+ IN CHAR8 *SectionName,\r
+ IN CHAR8 *EntryName,\r
+ OUT UINT64 *Data\r
+ )\r
+{\r
+ CHAR8 *Value;\r
+ EFI_STATUS Status;\r
+\r
+ if (Context == NULL || SectionName == NULL || EntryName == NULL || Data == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = GetStringFromDataFile(\r
+ Context,\r
+ SectionName,\r
+ EntryName,\r
+ &Value\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ if (!IsValidHexString(Value, AsciiStrLen(Value))) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ *Data = AsciiStrHexToUint64(Value);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Close an INI config file and free the context.\r
+\r
+ @param[in] Context INI Config file context.\r
+**/\r
+VOID\r
+EFIAPI\r
+CloseIniFile (\r
+ IN VOID *Context\r
+ )\r
+{\r
+ INI_PARSING_LIB_CONTEXT *IniContext;\r
+\r
+ if (Context == NULL) {\r
+ return ;\r
+ }\r
+\r
+ IniContext = Context;\r
+ FreeAllList(IniContext->SectionHead, IniContext->CommentHead);\r
+\r
+ return;\r
+}\r