diff options
Diffstat (limited to '')
-rwxr-xr-x | src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/UPT/Xml/IniToXml.py | 496 |
1 files changed, 496 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/UPT/Xml/IniToXml.py b/src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/UPT/Xml/IniToXml.py new file mode 100755 index 00000000..a0c939de --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/UPT/Xml/IniToXml.py @@ -0,0 +1,496 @@ +## @file +# This file is for converting package information data file to xml file. +# +# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +''' +IniToXml +''' + +import os.path +import re +from time import strftime +from time import localtime + +import Logger.Log as Logger +from Logger.ToolError import UPT_INI_PARSE_ERROR +from Logger.ToolError import FILE_NOT_FOUND +from Library.Xml.XmlRoutines import CreateXmlElement +from Library.DataType import TAB_VALUE_SPLIT +from Library.DataType import TAB_EQUAL_SPLIT +from Library.DataType import TAB_SECTION_START +from Library.DataType import TAB_SECTION_END +from Logger import StringTable as ST +from Library.StringUtils import ConvertSpecialChar +from Library.ParserValidate import IsValidPath +from Library import GlobalData + +## log error: +# +# @param error: error +# @param File: File +# @param Line: Line +# +def IniParseError(Error, File, Line): + Logger.Error("UPT", UPT_INI_PARSE_ERROR, File=File, + Line=Line, ExtraData=Error) + +## __ValidatePath +# +# @param Path: Path to be checked +# +def __ValidatePath(Path, Root): + Path = Path.strip() + if os.path.isabs(Path) or not IsValidPath(Path, Root): + return False, ST.ERR_FILELIST_LOCATION % (Root, Path) + return True, '' + +## ValidateMiscFile +# +# @param Filename: File to be checked +# +def ValidateMiscFile(Filename): + Root = GlobalData.gWORKSPACE + return __ValidatePath(Filename, Root) + +## ValidateToolsFile +# +# @param Filename: File to be checked +# +def ValidateToolsFile(Filename): + Valid, Cause = False, '' + if not Valid and 'EDK_TOOLS_PATH' in os.environ: + Valid, Cause = __ValidatePath(Filename, os.environ['EDK_TOOLS_PATH']) + if not Valid: + Valid, Cause = __ValidatePath(Filename, GlobalData.gWORKSPACE) + return Valid, Cause + +## ParseFileList +# +# @param Line: Line +# @param Map: Map +# @param CurrentKey: CurrentKey +# @param PathFunc: Path validate function +# +def ParseFileList(Line, Map, CurrentKey, PathFunc): + FileList = ["", {}] + TokenList = Line.split(TAB_VALUE_SPLIT) + if len(TokenList) > 0: + Path = TokenList[0].strip().replace('\\', '/') + if not Path: + return False, ST.ERR_WRONG_FILELIST_FORMAT + Valid, Cause = PathFunc(Path) + if not Valid: + return Valid, Cause + FileList[0] = TokenList[0].strip() + for Token in TokenList[1:]: + Attr = Token.split(TAB_EQUAL_SPLIT) + if len(Attr) != 2 or not Attr[0].strip() or not Attr[1].strip(): + return False, ST.ERR_WRONG_FILELIST_FORMAT + + Key = Attr[0].strip() + Val = Attr[1].strip() + if Key not in ['OS', 'Executable']: + return False, ST.ERR_UNKNOWN_FILELIST_ATTR % Key + + if Key == 'OS' and Val not in ["Win32", "Win64", "Linux32", + "Linux64", "OS/X32", "OS/X64", + "GenericWin", "GenericNix"]: + return False, ST.ERR_FILELIST_ATTR % 'OS' + elif Key == 'Executable' and Val not in ['true', 'false']: + return False, ST.ERR_FILELIST_ATTR % 'Executable' + FileList[1][Key] = Val + + Map[CurrentKey].append(FileList) + return True, '' + +## Create header XML file +# +# @param DistMap: DistMap +# @param Root: Root +# +def CreateHeaderXml(DistMap, Root): + Element1 = CreateXmlElement('Name', DistMap['Name'], + [], [['BaseName', DistMap['BaseName']]]) + Element2 = CreateXmlElement('GUID', DistMap['GUID'], + [], [['Version', DistMap['Version']]]) + AttributeList = [['ReadOnly', DistMap['ReadOnly']], + ['RePackage', DistMap['RePackage']]] + NodeList = [Element1, + Element2, + ['Vendor', DistMap['Vendor']], + ['Date', DistMap['Date']], + ['Copyright', DistMap['Copyright']], + ['License', DistMap['License']], + ['Abstract', DistMap['Abstract']], + ['Description', DistMap['Description']], + ['Signature', DistMap['Signature']], + ['XmlSpecification', DistMap['XmlSpecification']], + ] + Root.appendChild(CreateXmlElement('DistributionHeader', '', + NodeList, AttributeList)) + +## Create tools XML file +# +# @param Map: Map +# @param Root: Root +# @param Tag: Tag +# +def CreateToolsXml(Map, Root, Tag): + # + # Check if all elements in this section are empty + # + for Key in Map: + if len(Map[Key]) > 0: + break + else: + return + + NodeList = [['Name', Map['Name']], + ['Copyright', Map['Copyright']], + ['License', Map['License']], + ['Abstract', Map['Abstract']], + ['Description', Map['Description']], + ] + HeaderNode = CreateXmlElement('Header', '', NodeList, []) + NodeList = [HeaderNode] + + for File in Map['FileList']: + AttrList = [] + for Key in File[1]: + AttrList.append([Key, File[1][Key]]) + NodeList.append(CreateXmlElement('Filename', File[0], [], AttrList)) + Root.appendChild(CreateXmlElement(Tag, '', NodeList, [])) + +## ValidateValues +# +# @param Key: Key +# @param Value: Value +# @param SectionName: SectionName +# +def ValidateValues(Key, Value, SectionName): + if SectionName == 'DistributionHeader': + Valid, Cause = ValidateRegValues(Key, Value) + if not Valid: + return Valid, Cause + Valid = __ValidateDistHeader(Key, Value) + if not Valid: + return Valid, ST.ERR_VALUE_INVALID % (Key, SectionName) + else: + Valid = __ValidateOtherHeader(Key, Value) + if not Valid: + return Valid, ST.ERR_VALUE_INVALID % (Key, SectionName) + return True, '' + +## ValidateRegValues +# +# @param Key: Key +# @param Value: Value +# +def ValidateRegValues(Key, Value): + ValidateMap = { + 'ReadOnly' : + ('true|false', ST.ERR_BOOLEAN_VALUE % (Key, Value)), + 'RePackage' : + ('true|false', ST.ERR_BOOLEAN_VALUE % (Key, Value)), + 'GUID' : + ('[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}' + '-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}', + ST.ERR_GUID_VALUE % Value), + 'Version' : ('[0-9]+(\.[0-9]+)?', ST.ERR_VERSION_VALUE % \ + (Key, Value)), + 'XmlSpecification' : ('1\.1', ST.ERR_VERSION_XMLSPEC % Value) + } + if Key not in ValidateMap: + return True, '' + Elem = ValidateMap[Key] + Match = re.compile(Elem[0]).match(Value) + if Match and Match.start() == 0 and Match.end() == len(Value): + return True, '' + return False, Elem[1] + +## __ValidateDistHeaderName +# +# @param Name: Name +# +def __ValidateDistHeaderName(Name): + if len(Name) < 1: + return False + + for Char in Name: + if ord(Char) < 0x20 or ord(Char) >= 0x7f: + return False + return True + +## __ValidateDistHeaderBaseName +# +# @param BaseName: BaseName +# +def __ValidateDistHeaderBaseName(BaseName): + if not BaseName: + return False +# if CheckLen and len(BaseName) < 2: +# return False + if not BaseName[0].isalnum() and BaseName[0] != '_': + return False + for Char in BaseName[1:]: + if not Char.isalnum() and Char not in '-_': + return False + return True + +## __ValidateDistHeaderAbstract +# +# @param Abstract: Abstract +# +def __ValidateDistHeaderAbstract(Abstract): + return '\t' not in Abstract and len(Abstract.splitlines()) == 1 + +## __ValidateOtherHeaderAbstract +# +# @param Abstract: Abstract +# +def __ValidateOtherHeaderAbstract(Abstract): + return __ValidateDistHeaderAbstract(Abstract) + +## __ValidateDistHeader +# +# @param Key: Key +# @param Value: Value +# +def __ValidateDistHeader(Key, Value): + ValidateMap = { + 'Name' : __ValidateDistHeaderName, + 'BaseName' : __ValidateDistHeaderBaseName, + 'Abstract' : __ValidateDistHeaderAbstract, + 'Vendor' : __ValidateDistHeaderAbstract + } + return not (Value and Key in ValidateMap and not ValidateMap[Key](Value)) + +## __ValidateOtherHeader +# +# @param Key: Key +# @param Value: Value +# +def __ValidateOtherHeader(Key, Value): + ValidateMap = { + 'Name' : __ValidateDistHeaderName, + 'Abstract' : __ValidateOtherHeaderAbstract + } + return not (Value and Key in ValidateMap and not ValidateMap[Key](Value)) + +## Convert ini file to xml file +# +# @param IniFile +# +def IniToXml(IniFile): + if not os.path.exists(IniFile): + Logger.Error("UPT", FILE_NOT_FOUND, ST.ERR_TEMPLATE_NOTFOUND % IniFile) + + DistMap = {'ReadOnly' : '', 'RePackage' : '', 'Name' : '', + 'BaseName' : '', 'GUID' : '', 'Version' : '', 'Vendor' : '', + 'Date' : '', 'Copyright' : '', 'License' : '', 'Abstract' : '', + 'Description' : '', 'Signature' : '', 'XmlSpecification' : '' + } + + ToolsMap = {'Name' : '', 'Copyright' : '', 'License' : '', + 'Abstract' : '', 'Description' : '', 'FileList' : []} + # + # Only FileList is a list: [['file1', {}], ['file2', {}], ...] + # + MiscMap = {'Name' : '', 'Copyright' : '', 'License' : '', + 'Abstract' : '', 'Description' : '', 'FileList' : []} + + SectionMap = { + 'DistributionHeader' : DistMap, + 'ToolsHeader' : ToolsMap, + 'MiscellaneousFilesHeader' : MiscMap + } + + PathValidator = { + 'ToolsHeader' : ValidateToolsFile, + 'MiscellaneousFilesHeader' : ValidateMiscFile + } + + ParsedSection = [] + + SectionName = '' + CurrentKey = '' + PreMap = None + Map = None + FileContent = ConvertSpecialChar(open(IniFile, 'r').readlines()) + LastIndex = 0 + for Index in range(0, len(FileContent)): + LastIndex = Index + Line = FileContent[Index].strip() + if Line == '' or Line.startswith(';'): + continue + if Line[0] == TAB_SECTION_START and Line[-1] == TAB_SECTION_END: + CurrentKey = '' + SectionName = Line[1:-1].strip() + if SectionName not in SectionMap: + IniParseError(ST.ERR_SECTION_NAME_INVALID % SectionName, + IniFile, Index+1) + + if SectionName in ParsedSection: + IniParseError(ST.ERR_SECTION_REDEFINE % SectionName, + IniFile, Index+1) + else: + ParsedSection.append(SectionName) + + Map = SectionMap[SectionName] + continue + if not Map: + IniParseError(ST.ERR_SECTION_NAME_NONE, IniFile, Index+1) + TokenList = Line.split(TAB_EQUAL_SPLIT, 1) + TempKey = TokenList[0].strip() + # + # Value spanned multiple or same keyword appears more than one time + # + if len(TokenList) < 2 or TempKey not in Map: + if CurrentKey == '': + IniParseError(ST.ERR_KEYWORD_INVALID % TempKey, + IniFile, Index+1) + elif CurrentKey == 'FileList': + # + # Special for FileList + # + Valid, Cause = ParseFileList(Line, Map, CurrentKey, + PathValidator[SectionName]) + if not Valid: + IniParseError(Cause, IniFile, Index+1) + + else: + # + # Multiple lines for one key such as license + # Or if string on the left side of '=' is not a keyword + # + Map[CurrentKey] = ''.join([Map[CurrentKey], '\n', Line]) + Valid, Cause = ValidateValues(CurrentKey, + Map[CurrentKey], SectionName) + if not Valid: + IniParseError(Cause, IniFile, Index+1) + continue + + if (TokenList[1].strip() == ''): + IniParseError(ST.ERR_EMPTY_VALUE, IniFile, Index+1) + + # + # A keyword found + # + CurrentKey = TempKey + if Map[CurrentKey]: + IniParseError(ST.ERR_KEYWORD_REDEFINE % CurrentKey, + IniFile, Index+1) + + if id(Map) != id(PreMap) and Map['Copyright']: + PreMap = Map + Copyright = Map['Copyright'].lower() + Pos = Copyright.find('copyright') + if Pos == -1: + IniParseError(ST.ERR_COPYRIGHT_CONTENT, IniFile, Index) + if not Copyright[Pos + len('copyright'):].lstrip(' ').startswith('('): + IniParseError(ST.ERR_COPYRIGHT_CONTENT, IniFile, Index) + + if CurrentKey == 'FileList': + Valid, Cause = ParseFileList(TokenList[1], Map, CurrentKey, + PathValidator[SectionName]) + if not Valid: + IniParseError(Cause, IniFile, Index+1) + else: + Map[CurrentKey] = TokenList[1].strip() + Valid, Cause = ValidateValues(CurrentKey, + Map[CurrentKey], SectionName) + if not Valid: + IniParseError(Cause, IniFile, Index+1) + + if id(Map) != id(PreMap) and Map['Copyright'] and 'copyright' not in Map['Copyright'].lower(): + IniParseError(ST.ERR_COPYRIGHT_CONTENT, IniFile, LastIndex) + + # + # Check mandatory keys + # + CheckMdtKeys(DistMap, IniFile, LastIndex, + (('ToolsHeader', ToolsMap), ('MiscellaneousFilesHeader', MiscMap)) + ) + + return CreateXml(DistMap, ToolsMap, MiscMap, IniFile) + + +## CheckMdtKeys +# +# @param MdtDistKeys: All mandatory keys +# @param DistMap: Dist content +# @param IniFile: Ini file +# @param LastIndex: Last index of Ini file +# @param Maps: Tools and Misc section name and map. (('section_name', map),*) +# +def CheckMdtKeys(DistMap, IniFile, LastIndex, Maps): + MdtDistKeys = ['Name', 'GUID', 'Version', 'Vendor', 'Copyright', 'License', 'Abstract', 'XmlSpecification'] + for Key in MdtDistKeys: + if Key not in DistMap or DistMap[Key] == '': + IniParseError(ST.ERR_KEYWORD_MANDATORY % Key, IniFile, LastIndex+1) + + if '.' not in DistMap['Version']: + DistMap['Version'] = DistMap['Version'] + '.0' + + DistMap['Date'] = str(strftime("%Y-%m-%dT%H:%M:%S", localtime())) + + # + # Check Tools Surface Area according to UPT Spec + # <Tools> {0,} + # <Header> ... </Header> {0,1} + # <Filename> ... </Filename> {1,} + # </Tools> + # <Header> + # <Name> xs:normalizedString </Name> {1} + # <Copyright> xs:string </Copyright> {0,1} + # <License> xs:string </License> {0,1} + # <Abstract> xs:normalizedString </Abstract> {0,1} + # <Description> xs:string </Description> {0,1} + # </Header> + # + for Item in Maps: + Map = Item[1] + NonEmptyKey = 0 + for Key in Map: + if Map[Key]: + NonEmptyKey += 1 + + if NonEmptyKey > 0 and not Map['FileList']: + IniParseError(ST.ERR_KEYWORD_MANDATORY % (Item[0] + '.FileList'), IniFile, LastIndex+1) + + if NonEmptyKey > 0 and not Map['Name']: + IniParseError(ST.ERR_KEYWORD_MANDATORY % (Item[0] + '.Name'), IniFile, LastIndex+1) + +## CreateXml +# +# @param DistMap: Dist Content +# @param ToolsMap: Tools Content +# @param MiscMap: Misc Content +# @param IniFile: Ini File +# +def CreateXml(DistMap, ToolsMap, MiscMap, IniFile): + Attrs = [['xmlns', 'http://www.uefi.org/2011/1.1'], + ['xmlns:xsi', 'http:/www.w3.org/2001/XMLSchema-instance'], + ] + Root = CreateXmlElement('DistributionPackage', '', [], Attrs) + CreateHeaderXml(DistMap, Root) + CreateToolsXml(ToolsMap, Root, 'Tools') + CreateToolsXml(MiscMap, Root, 'MiscellaneousFiles') + + FileAndExt = IniFile.rsplit('.', 1) + if len(FileAndExt) > 1: + FileName = FileAndExt[0] + '.xml' + else: + FileName = IniFile + '.xml' + File = open(FileName, 'w') + + try: + File.write(Root.toprettyxml(indent = ' ')) + finally: + File.close() + return FileName + |