## @file ParserValidate.py | |
# Functions for parser validation | |
# | |
# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR> | |
# | |
# SPDX-License-Identifier: BSD-2-Clause-Patent | |
# | |
''' | |
ParserValidate | |
''' | |
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.StringUtils 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"} {"True"} {"0x1"} {"0x01"} | |
# <FALSE> ::= {"FALSE"} {"false"} {"False"} {"0x0"} {"0x00"} | |
# <BoolType> ::= {<TRUE>} {<FALSE>} | |
# | |
# @param BoolString: A string contained the value need to be judged. | |
# | |
def IsValidBoolType(BoolString): | |
# | |
# Valid True | |
# | |
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. | |
# Family := [A-Z]([a-zA-Z0-9])* | |
# | |
def IsValidToolFamily(ToolFamily): | |
ReIsValidFamily = re.compile(r"^[A-Z]+[A-Za-z0-9]{0,}$", re.DOTALL) | |
if ReIsValidFamily.match(ToolFamily) is 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 | |
# <OA> ::= (a-zA-Z)(A-Za-z0-9){0,} | |
# <arch> ::= {"IA32"} {"X64"} {"IPF"} {"EBC"} {<OA>} | |
# {"common"} | |
# @param Arch: Input arch | |
# | |
def IsValidArch(Arch): | |
if Arch == 'common': | |
return True | |
ReIsValidArch = re.compile(r"^[a-zA-Z]+[a-zA-Z0-9]{0,}$", re.DOTALL) | |
if ReIsValidArch.match(Arch) is None: | |
return False | |
return True | |
## Is valid family or not | |
# | |
# <Family> ::= {"MSFT"} {"GCC"} {"INTEL"} {<Usr>} {"*"} | |
# <Usr> ::= [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) is 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]) is None: | |
return False | |
if ReIsValidBuildOption1.match(ToolOptionList[1]) is None: | |
return False | |
if ReIsValidBuildOption2.match(ToolOptionList[2]) is 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 not isinstance(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. | |
# <Word> ::= (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. | |
# <SimpleWord> ::= (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. | |
# <DecVersion> ::= (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) is None: | |
return False | |
return True | |
## IsValidHexVersion | |
# | |
# Check whether the hex version is valid. | |
# <HexVersion> ::= "0x" <Major> <Minor> | |
# <Major> ::= <HexDigit>{4} | |
# <Minor> ::= <HexDigit>{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) is None: | |
return False | |
return True | |
## IsValidBuildNumber | |
# | |
# Check whether the BUILD_NUMBER is valid. | |
# ["BUILD_NUMBER" "=" <Integer>{1,4} <EOL>] | |
# | |
# @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) is 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) is None: | |
return False | |
return True | |
## IsValidNormalizedString | |
# | |
# Check | |
# <NormalizedString> ::= <DblQuote> [{<Word>} {<Space>}]{1,} <DblQuote> | |
# <Space> ::= 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. | |
# <AsciiString> ::= [ [<WhiteSpace>]{0,} [<AsciiChars>]{0,} ] {0,} | |
# <WhiteSpace> ::= {<Tab>} {<Space>} | |
# <Tab> ::= 0x09 | |
# <Space> ::= 0x20 | |
# <AsciiChars> ::= (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 | |
# | |
# <Boolean> | |
# | |
if IsValidFeatureFlagExp(PcdValue, True)[0]: | |
return True | |
# | |
# <Number> ::= {<Integer>} {<HexNumber>} | |
# <Integer> ::= {(0-9)} {(1-9)(0-9){1,}} | |
# <HexNumber> ::= "0x" <HexDigit>{1,} | |
# <HexDigit> ::= (a-fA-F0-9) | |
# | |
if IsValidHex(PcdValue): | |
return True | |
ReIsValidIntegerSingle = re.compile(r"^\s*[0-9]\s*$", re.DOTALL) | |
if ReIsValidIntegerSingle.match(PcdValue) is not None: | |
return True | |
ReIsValidIntegerMulti = re.compile(r"^\s*[1-9][0-9]+\s*$", re.DOTALL) | |
if ReIsValidIntegerMulti.match(PcdValue) is not None: | |
return True | |
# | |
# <StringVal> ::= {<StringType>} {<Array>} {"$(" <MACRO> ")"} | |
# <StringType> ::= {<UnicodeString>} {<CString>} | |
# | |
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 | |
# | |
# <Array> ::= {<CArray>} {<NList>} {<CFormatGUID>} | |
# <CArray> ::= "{" [<NList>] <CArray>{0,} "}" | |
# <NList> ::= <HexByte> ["," <HexByte>]{0,} | |
# <HexDigit> ::= (a-fA-F0-9) | |
# <HexByte> ::= "0x" <HexDigit>{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) is None: | |
return False | |
return True | |
## IsValidIdentifier | |
# | |
# <Identifier> ::= <NonDigit> <Chars>{0,} | |
# <Chars> ::= (a-zA-Z0-9_) | |
# <NonDigit> ::= (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) is 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) is 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 | |
# | |
# <UserId> ::= (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 != b'\xff\xfe': | |
return False | |
return True |