## @file ParserValidate.py # Functions for parser validation # # Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.
# # This program and the accompanying materials are licensed and made available # under the terms and conditions of the BSD License which accompanies this # distribution. The full text of the license may be found at # http://opensource.org/licenses/bsd-license.php # # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. # ''' PaserValidate ''' import os.path import re import platform from Library.DataType import MODULE_LIST from Library.DataType import COMPONENT_TYPE_LIST from Library.DataType import PCD_USAGE_TYPE_LIST_OF_MODULE from Library.DataType import TAB_SPACE_SPLIT from Library.String import GetSplitValueList from Library.ExpressionValidate import IsValidBareCString from Library.ExpressionValidate import IsValidFeatureFlagExp from Common.MultipleWorkspace import MultipleWorkspace as mws ## __HexDigit() method # # Whether char input is a Hex data bit # # @param TempChar: The char to test # def __HexDigit(TempChar): if (TempChar >= 'a' and TempChar <= 'f') or \ (TempChar >= 'A' and TempChar <= 'F') \ or (TempChar >= '0' and TempChar <= '9'): return True else: return False ## IsValidHex() method # # Whether char input is a Hex data. # # @param TempChar: The char to test # def IsValidHex(HexStr): if not HexStr.upper().startswith("0X"): return False CharList = [c for c in HexStr[2:] if not __HexDigit(c)] if len(CharList) == 0: return True else: return False ## Judge the input string is valid bool type or not. # # ::= {"TRUE"} {"true"} {"True"} {"0x1"} {"0x01"} # ::= {"FALSE"} {"false"} {"False"} {"0x0"} {"0x00"} # ::= {} {} # # @param BoolString: A string contained the value need to be judged. # def IsValidBoolType(BoolString): # # Valid Ture # if BoolString == 'TRUE' or \ BoolString == 'True' or \ BoolString == 'true' or \ BoolString == '0x1' or \ BoolString == '0x01': return True # # Valid False # elif BoolString == 'FALSE' or \ BoolString == 'False' or \ BoolString == 'false' or \ BoolString == '0x0' or \ BoolString == '0x00': return True # # Invalid bool type # else: return False ## Is Valid Module Type List or not # # @param ModuleTypeList: A list contain ModuleType strings need to be # judged. # def IsValidInfMoudleTypeList(ModuleTypeList): for ModuleType in ModuleTypeList: return IsValidInfMoudleType(ModuleType) ## Is Valid Module Type or not # # @param ModuleType: A string contain ModuleType need to be judged. # def IsValidInfMoudleType(ModuleType): if ModuleType in MODULE_LIST: return True else: return False ## Is Valid Component Type or not # # @param ComponentType: A string contain ComponentType need to be judged. # def IsValidInfComponentType(ComponentType): if ComponentType.upper() in COMPONENT_TYPE_LIST: return True else: return False ## Is valid Tool Family or not # # @param ToolFamily: A string contain Tool Family need to be judged. # Famlily := [A-Z]([a-zA-Z0-9])* # def IsValidToolFamily(ToolFamily): ReIsValieFamily = re.compile(r"^[A-Z]+[A-Za-z0-9]{0,}$", re.DOTALL) if ReIsValieFamily.match(ToolFamily) == None: return False return True ## Is valid Tool TagName or not # # The TagName sample is MYTOOLS and VS2005. # # @param TagName: A string contain Tool TagName need to be judged. # def IsValidToolTagName(TagName): if TagName.strip() == '': return True if TagName.strip() == '*': return True if not IsValidWord(TagName): return False return True ## Is valid arch or not # # @param Arch The arch string need to be validated # ::= (a-zA-Z)(A-Za-z0-9){0,} # ::= {"IA32"} {"X64"} {"IPF"} {"EBC"} {} # {"common"} # @param Arch: Input arch # def IsValidArch(Arch): if Arch == 'common': return True ReIsValieArch = re.compile(r"^[a-zA-Z]+[a-zA-Z0-9]{0,}$", re.DOTALL) if ReIsValieArch.match(Arch) == None: return False return True ## Is valid family or not # # ::= {"MSFT"} {"GCC"} {"INTEL"} {} {"*"} # ::= [A-Z][A-Za-z0-9]{0,} # # @param family: The family string need to be validated # def IsValidFamily(Family): Family = Family.strip() if Family == '*': return True if Family == '': return True ReIsValidFamily = re.compile(r"^[A-Z]+[A-Za-z0-9]{0,}$", re.DOTALL) if ReIsValidFamily.match(Family) == None: return False return True ## Is valid build option name or not # # @param BuildOptionName: The BuildOptionName string need to be validated # def IsValidBuildOptionName(BuildOptionName): if not BuildOptionName: return False ToolOptionList = GetSplitValueList(BuildOptionName, '_', 4) if len(ToolOptionList) != 5: return False ReIsValidBuildOption1 = re.compile(r"^\s*(\*)|([A-Z][a-zA-Z0-9]*)$") ReIsValidBuildOption2 = re.compile(r"^\s*(\*)|([a-zA-Z][a-zA-Z0-9]*)$") if ReIsValidBuildOption1.match(ToolOptionList[0]) == None: return False if ReIsValidBuildOption1.match(ToolOptionList[1]) == None: return False if ReIsValidBuildOption2.match(ToolOptionList[2]) == None: return False if ToolOptionList[3] == "*" and ToolOptionList[4] not in ['FAMILY', 'DLL', 'DPATH']: return False return True ## IsValidToken # # Check if pattern string matches total token # # @param ReString: regular string # @param Token: Token to be matched # def IsValidToken(ReString, Token): Match = re.compile(ReString).match(Token) return Match and Match.start() == 0 and Match.end() == len(Token) ## IsValidPath # # Check if path exist # # @param Path: Absolute path or relative path to be checked # @param Root: Root path # def IsValidPath(Path, Root): Path = Path.strip() OrigPath = Path.replace('\\', '/') Path = os.path.normpath(Path).replace('\\', '/') Root = os.path.normpath(Root).replace('\\', '/') FullPath = mws.join(Root, Path) if not os.path.exists(FullPath): return False # # If Path is absolute path. # It should be in Root. # if os.path.isabs(Path): if not Path.startswith(Root): return False return True # # Check illegal character # for Rel in ['/', './', '../']: if OrigPath.startswith(Rel): return False for Rel in ['//', '/./', '/../']: if Rel in OrigPath: return False for Rel in ['/.', '/..', '/']: if OrigPath.endswith(Rel): return False Path = Path.rstrip('/') # # Check relative path # for Word in Path.split('/'): if not IsValidWord(Word): return False return True ## IsValidInstallPath # # Check if an install path valid or not. # # Absolute path or path starts with '.' or path contains '..' are invalid. # # @param Path: path to be checked # def IsValidInstallPath(Path): if platform.platform().find("Windows") >= 0: if os.path.isabs(Path): return False else: if Path[1:2] == ':': return False if os.path.isabs(Path): return False if Path.startswith('.'): return False if Path.find('..') != -1: return False return True ## IsValidCFormatGuid # # Check if GUID format has the from of {8,4,4,{2,2,2,2,2,2,2,2}} # # @param Guid: Guid to be checked # def IsValidCFormatGuid(Guid): # # Valid: { 0xf0b11735, 0x87a0, 0x4193, {0xb2, 0x66, 0x53, 0x8c, 0x38, # 0xaf, 0x48, 0xce }} # Invalid: { 0xf0b11735, 0x87a0, 0x4193, {0xb2, 0x66, 0x53, 0x8c, 0x38, # 0xaf, 0x48, 0xce }} 0x123 # Invalid: { 0xf0b1 1735, 0x87a0, 0x4193, {0xb2, 0x66, 0x53, 0x8c, 0x38, # 0xaf, 0x48, 0xce }} # List = ['{', 10, ',', 6, ',', 6, ',{', 4, ',', 4, ',', 4, ',', 4, ',', 4, ',', 4, ',', 4, ',', 4, '}}'] Index = 0 Value = '' SepValue = '' for Char in Guid: if Char not in '{},\t ': Value += Char continue if Value: try: # # Index may out of bound # if not SepValue or SepValue != List[Index]: return False Index += 1 SepValue = '' if not Value.startswith('0x') and not Value.startswith('0X'): return False # # Index may out of bound # if type(List[Index]) != type(1) or \ len(Value) > List[Index] or len(Value) < 3: return False # # Check if string can be converted to integer # Throw exception if not # int(Value, 16) except BaseException: # # Exception caught means invalid format # return False Value = '' Index += 1 if Char in '{},': SepValue += Char return SepValue == '}}' and Value == '' ## IsValidPcdType # # Check whether the PCD type is valid # # @param PcdTypeString: The PcdType string need to be checked. # def IsValidPcdType(PcdTypeString): if PcdTypeString.upper() in PCD_USAGE_TYPE_LIST_OF_MODULE: return True else: return False ## IsValidWord # # Check whether the word is valid. # ::= (a-zA-Z0-9_)(a-zA-Z0-9_-){0,} Alphanumeric characters with # optional # dash "-" and/or underscore "_" characters. No whitespace # characters are permitted. # # @param Word: The word string need to be checked. # def IsValidWord(Word): if not Word: return False # # The first char should be alpha, _ or Digit. # if not Word[0].isalnum() and \ not Word[0] == '_' and \ not Word[0].isdigit(): return False LastChar = '' for Char in Word[1:]: if (not Char.isalpha()) and \ (not Char.isdigit()) and \ Char != '-' and \ Char != '_' and \ Char != '.': return False if Char == '.' and LastChar == '.': return False LastChar = Char return True ## IsValidSimpleWord # # Check whether the SimpleWord is valid. # ::= (a-zA-Z0-9)(a-zA-Z0-9_-){0,} # A word that cannot contain a period character. # # @param Word: The word string need to be checked. # def IsValidSimpleWord(Word): ReIsValidSimpleWord = \ re.compile(r"^[0-9A-Za-z][0-9A-Za-z\-_]*$", re.DOTALL) Word = Word.strip() if not Word: return False if not ReIsValidSimpleWord.match(Word): return False return True ## IsValidDecVersion # # Check whether the decimal version is valid. # ::= (0-9){1,} ["." (0-9){1,}] # # @param Word: The word string need to be checked. # def IsValidDecVersion(Word): if Word.find('.') > -1: ReIsValidDecVersion = re.compile(r"[0-9]+\.?[0-9]+$") else: ReIsValidDecVersion = re.compile(r"[0-9]+$") if ReIsValidDecVersion.match(Word) == None: return False return True ## IsValidHexVersion # # Check whether the hex version is valid. # ::= "0x" # ::= {4} # ::= {4} # # @param Word: The word string need to be checked. # def IsValidHexVersion(Word): ReIsValidHexVersion = re.compile(r"[0][xX][0-9A-Fa-f]{8}$", re.DOTALL) if ReIsValidHexVersion.match(Word) == None: return False return True ## IsValidBuildNumber # # Check whether the BUILD_NUMBER is valid. # ["BUILD_NUMBER" "=" {1,4} ] # # @param Word: The BUILD_NUMBER string need to be checked. # def IsValidBuildNumber(Word): ReIsValieBuildNumber = re.compile(r"[0-9]{1,4}$", re.DOTALL) if ReIsValieBuildNumber.match(Word) == None: return False return True ## IsValidDepex # # Check whether the Depex is valid. # # @param Word: The Depex string need to be checked. # def IsValidDepex(Word): Index = Word.upper().find("PUSH") if Index > -1: return IsValidCFormatGuid(Word[Index+4:].strip()) ReIsValidCName = re.compile(r"^[A-Za-z_][0-9A-Za-z_\s\.]*$", re.DOTALL) if ReIsValidCName.match(Word) == None: return False return True ## IsValidNormalizedString # # Check # ::= [{} {}]{1,} # ::= 0x20 # # @param String: string to be checked # def IsValidNormalizedString(String): if String == '': return True for Char in String: if Char == '\t': return False StringList = GetSplitValueList(String, TAB_SPACE_SPLIT) for Item in StringList: if not Item: continue if not IsValidWord(Item): return False return True ## IsValidIdString # # Check whether the IdString is valid. # # @param IdString: The IdString need to be checked. # def IsValidIdString(String): if IsValidSimpleWord(String.strip()): return True if String.strip().startswith('"') and \ String.strip().endswith('"'): String = String[1:-1] if String.strip() == "": return True if IsValidNormalizedString(String): return True return False ## IsValidVersionString # # Check whether the VersionString is valid. # ::= [ []{0,} []{0,} ] {0,} # ::= {} {} # ::= 0x09 # ::= 0x20 # ::= (0x21 - 0x7E) # # @param VersionString: The VersionString need to be checked. # def IsValidVersionString(VersionString): VersionString = VersionString.strip() for Char in VersionString: if not (Char >= 0x21 and Char <= 0x7E): return False return True ## IsValidPcdValue # # Check whether the PcdValue is valid. # # @param VersionString: The PcdValue need to be checked. # def IsValidPcdValue(PcdValue): for Char in PcdValue: if Char == '\n' or Char == '\t' or Char == '\f': return False # # # if IsValidFeatureFlagExp(PcdValue, True)[0]: return True # # ::= {} {} # ::= {(0-9)} {(1-9)(0-9){1,}} # ::= "0x" {1,} # ::= (a-fA-F0-9) # if IsValidHex(PcdValue): return True ReIsValidIntegerSingle = re.compile(r"^\s*[0-9]\s*$", re.DOTALL) if ReIsValidIntegerSingle.match(PcdValue) != None: return True ReIsValidIntegerMulti = re.compile(r"^\s*[1-9][0-9]+\s*$", re.DOTALL) if ReIsValidIntegerMulti.match(PcdValue) != None: return True # # ::= {} {} {"$(" ")"} # ::= {} {} # ReIsValidStringType = re.compile(r"^\s*[\"L].*[\"]\s*$") if ReIsValidStringType.match(PcdValue): IsTrue = False if PcdValue.strip().startswith('L\"'): StringValue = PcdValue.strip().lstrip('L\"').rstrip('\"') if IsValidBareCString(StringValue): IsTrue = True elif PcdValue.strip().startswith('\"'): StringValue = PcdValue.strip().lstrip('\"').rstrip('\"') if IsValidBareCString(StringValue): IsTrue = True if IsTrue: return IsTrue # # ::= {} {} {} # ::= "{" [] {0,} "}" # ::= ["," ]{0,} # ::= (a-fA-F0-9) # ::= "0x" {1,2} # if IsValidCFormatGuid(PcdValue): return True ReIsValidByteHex = re.compile(r"^\s*0x[0-9a-fA-F]{1,2}\s*$", re.DOTALL) if PcdValue.strip().startswith('{') and PcdValue.strip().endswith('}') : StringValue = PcdValue.strip().lstrip('{').rstrip('}') ValueList = StringValue.split(',') AllValidFlag = True for ValueItem in ValueList: if not ReIsValidByteHex.match(ValueItem.strip()): AllValidFlag = False if AllValidFlag: return True # # NList # AllValidFlag = True ValueList = PcdValue.split(',') for ValueItem in ValueList: if not ReIsValidByteHex.match(ValueItem.strip()): AllValidFlag = False if AllValidFlag: return True return False ## IsValidCVariableName # # Check whether the PcdValue is valid. # # @param VersionString: The PcdValue need to be checked. # def IsValidCVariableName(CName): ReIsValidCName = re.compile(r"^[A-Za-z_][0-9A-Za-z_]*$", re.DOTALL) if ReIsValidCName.match(CName) == None: return False return True ## IsValidIdentifier # # ::= {0,} # ::= (a-zA-Z0-9_) # ::= (a-zA-Z_) # # @param Ident: identifier to be checked # def IsValidIdentifier(Ident): ReIdent = re.compile(r"^[A-Za-z_][0-9A-Za-z_]*$", re.DOTALL) if ReIdent.match(Ident) == None: return False return True ## IsValidDecVersionVal # # {(0-9){1,} "." (0-99)} # # @param Ver: version to be checked # def IsValidDecVersionVal(Ver): ReVersion = re.compile(r"[0-9]+(\.[0-9]{1,2})$") if ReVersion.match(Ver) == None: return False return True ## IsValidLibName # # (A-Z)(a-zA-Z0-9){0,} and could not be "NULL" # def IsValidLibName(LibName): if LibName == 'NULL': return False ReLibName = re.compile("^[A-Z]+[a-zA-Z0-9]*$") if not ReLibName.match(LibName): return False return True # IsValidUserId # # ::= (a-zA-Z)(a-zA-Z0-9_.){0,} # Words that contain period "." must be encapsulated in double quotation marks. # def IsValidUserId(UserId): UserId = UserId.strip() Quoted = False if UserId.startswith('"') and UserId.endswith('"'): Quoted = True UserId = UserId[1:-1] if not UserId or not UserId[0].isalpha(): return False for Char in UserId[1:]: if not Char.isalnum() and not Char in '_.': return False if Char == '.' and not Quoted: return False return True # # Check if a UTF16-LE file has a BOM header # def CheckUTF16FileHeader(File): FileIn = open(File, 'rb').read(2) if FileIn != '\xff\xfe': return False return True