1## @file ParserValidate.py 2# Functions for parser validation 3# 4# Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.<BR> 5# 6# This program and the accompanying materials are licensed and made available 7# under the terms and conditions of the BSD License which accompanies this 8# distribution. The full text of the license may be found at 9# http://opensource.org/licenses/bsd-license.php 10# 11# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 12# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 13# 14 15''' 16PaserValidate 17''' 18 19import os.path 20import re 21import platform 22 23from Library.DataType import MODULE_LIST 24from Library.DataType import COMPONENT_TYPE_LIST 25from Library.DataType import PCD_USAGE_TYPE_LIST_OF_MODULE 26from Library.DataType import TAB_SPACE_SPLIT 27from Library.String import GetSplitValueList 28from Library.ExpressionValidate import IsValidBareCString 29from Library.ExpressionValidate import IsValidFeatureFlagExp 30from Common.MultipleWorkspace import MultipleWorkspace as mws 31 32## __HexDigit() method 33# 34# Whether char input is a Hex data bit 35# 36# @param TempChar: The char to test 37# 38def __HexDigit(TempChar): 39 if (TempChar >= 'a' and TempChar <= 'f') or \ 40 (TempChar >= 'A' and TempChar <= 'F') \ 41 or (TempChar >= '0' and TempChar <= '9'): 42 return True 43 else: 44 return False 45 46## IsValidHex() method 47# 48# Whether char input is a Hex data. 49# 50# @param TempChar: The char to test 51# 52def IsValidHex(HexStr): 53 if not HexStr.upper().startswith("0X"): 54 return False 55 CharList = [c for c in HexStr[2:] if not __HexDigit(c)] 56 if len(CharList) == 0: 57 return True 58 else: 59 return False 60 61## Judge the input string is valid bool type or not. 62# 63# <TRUE> ::= {"TRUE"} {"true"} {"True"} {"0x1"} {"0x01"} 64# <FALSE> ::= {"FALSE"} {"false"} {"False"} {"0x0"} {"0x00"} 65# <BoolType> ::= {<TRUE>} {<FALSE>} 66# 67# @param BoolString: A string contained the value need to be judged. 68# 69def IsValidBoolType(BoolString): 70 # 71 # Valid Ture 72 # 73 if BoolString == 'TRUE' or \ 74 BoolString == 'True' or \ 75 BoolString == 'true' or \ 76 BoolString == '0x1' or \ 77 BoolString == '0x01': 78 return True 79 # 80 # Valid False 81 # 82 elif BoolString == 'FALSE' or \ 83 BoolString == 'False' or \ 84 BoolString == 'false' or \ 85 BoolString == '0x0' or \ 86 BoolString == '0x00': 87 return True 88 # 89 # Invalid bool type 90 # 91 else: 92 return False 93 94## Is Valid Module Type List or not 95# 96# @param ModuleTypeList: A list contain ModuleType strings need to be 97# judged. 98# 99def IsValidInfMoudleTypeList(ModuleTypeList): 100 for ModuleType in ModuleTypeList: 101 return IsValidInfMoudleType(ModuleType) 102 103## Is Valid Module Type or not 104# 105# @param ModuleType: A string contain ModuleType need to be judged. 106# 107def IsValidInfMoudleType(ModuleType): 108 if ModuleType in MODULE_LIST: 109 return True 110 else: 111 return False 112 113## Is Valid Component Type or not 114# 115# @param ComponentType: A string contain ComponentType need to be judged. 116# 117def IsValidInfComponentType(ComponentType): 118 if ComponentType.upper() in COMPONENT_TYPE_LIST: 119 return True 120 else: 121 return False 122 123 124## Is valid Tool Family or not 125# 126# @param ToolFamily: A string contain Tool Family need to be judged. 127# Famlily := [A-Z]([a-zA-Z0-9])* 128# 129def IsValidToolFamily(ToolFamily): 130 ReIsValieFamily = re.compile(r"^[A-Z]+[A-Za-z0-9]{0,}$", re.DOTALL) 131 if ReIsValieFamily.match(ToolFamily) == None: 132 return False 133 return True 134 135## Is valid Tool TagName or not 136# 137# The TagName sample is MYTOOLS and VS2005. 138# 139# @param TagName: A string contain Tool TagName need to be judged. 140# 141def IsValidToolTagName(TagName): 142 if TagName.strip() == '': 143 return True 144 if TagName.strip() == '*': 145 return True 146 if not IsValidWord(TagName): 147 return False 148 return True 149 150## Is valid arch or not 151# 152# @param Arch The arch string need to be validated 153# <OA> ::= (a-zA-Z)(A-Za-z0-9){0,} 154# <arch> ::= {"IA32"} {"X64"} {"IPF"} {"EBC"} {<OA>} 155# {"common"} 156# @param Arch: Input arch 157# 158def IsValidArch(Arch): 159 if Arch == 'common': 160 return True 161 ReIsValieArch = re.compile(r"^[a-zA-Z]+[a-zA-Z0-9]{0,}$", re.DOTALL) 162 if ReIsValieArch.match(Arch) == None: 163 return False 164 return True 165 166## Is valid family or not 167# 168# <Family> ::= {"MSFT"} {"GCC"} {"INTEL"} {<Usr>} {"*"} 169# <Usr> ::= [A-Z][A-Za-z0-9]{0,} 170# 171# @param family: The family string need to be validated 172# 173def IsValidFamily(Family): 174 Family = Family.strip() 175 if Family == '*': 176 return True 177 178 if Family == '': 179 return True 180 181 ReIsValidFamily = re.compile(r"^[A-Z]+[A-Za-z0-9]{0,}$", re.DOTALL) 182 if ReIsValidFamily.match(Family) == None: 183 return False 184 return True 185 186## Is valid build option name or not 187# 188# @param BuildOptionName: The BuildOptionName string need to be validated 189# 190def IsValidBuildOptionName(BuildOptionName): 191 if not BuildOptionName: 192 return False 193 194 ToolOptionList = GetSplitValueList(BuildOptionName, '_', 4) 195 196 if len(ToolOptionList) != 5: 197 return False 198 199 ReIsValidBuildOption1 = re.compile(r"^\s*(\*)|([A-Z][a-zA-Z0-9]*)$") 200 ReIsValidBuildOption2 = re.compile(r"^\s*(\*)|([a-zA-Z][a-zA-Z0-9]*)$") 201 202 if ReIsValidBuildOption1.match(ToolOptionList[0]) == None: 203 return False 204 205 if ReIsValidBuildOption1.match(ToolOptionList[1]) == None: 206 return False 207 208 if ReIsValidBuildOption2.match(ToolOptionList[2]) == None: 209 return False 210 211 if ToolOptionList[3] == "*" and ToolOptionList[4] not in ['FAMILY', 'DLL', 'DPATH']: 212 return False 213 214 return True 215 216## IsValidToken 217# 218# Check if pattern string matches total token 219# 220# @param ReString: regular string 221# @param Token: Token to be matched 222# 223def IsValidToken(ReString, Token): 224 Match = re.compile(ReString).match(Token) 225 return Match and Match.start() == 0 and Match.end() == len(Token) 226 227## IsValidPath 228# 229# Check if path exist 230# 231# @param Path: Absolute path or relative path to be checked 232# @param Root: Root path 233# 234def IsValidPath(Path, Root): 235 Path = Path.strip() 236 OrigPath = Path.replace('\\', '/') 237 238 Path = os.path.normpath(Path).replace('\\', '/') 239 Root = os.path.normpath(Root).replace('\\', '/') 240 FullPath = mws.join(Root, Path) 241 242 if not os.path.exists(FullPath): 243 return False 244 245 # 246 # If Path is absolute path. 247 # It should be in Root. 248 # 249 if os.path.isabs(Path): 250 if not Path.startswith(Root): 251 return False 252 return True 253 254 # 255 # Check illegal character 256 # 257 for Rel in ['/', './', '../']: 258 if OrigPath.startswith(Rel): 259 return False 260 for Rel in ['//', '/./', '/../']: 261 if Rel in OrigPath: 262 return False 263 for Rel in ['/.', '/..', '/']: 264 if OrigPath.endswith(Rel): 265 return False 266 267 Path = Path.rstrip('/') 268 269 # 270 # Check relative path 271 # 272 for Word in Path.split('/'): 273 if not IsValidWord(Word): 274 return False 275 276 return True 277 278## IsValidInstallPath 279# 280# Check if an install path valid or not. 281# 282# Absolute path or path starts with '.' or path contains '..' are invalid. 283# 284# @param Path: path to be checked 285# 286def IsValidInstallPath(Path): 287 if platform.platform().find("Windows") >= 0: 288 if os.path.isabs(Path): 289 return False 290 else: 291 if Path[1:2] == ':': 292 return False 293 if os.path.isabs(Path): 294 return False 295 if Path.startswith('.'): 296 return False 297 298 if Path.find('..') != -1: 299 return False 300 301 return True 302 303 304## IsValidCFormatGuid 305# 306# Check if GUID format has the from of {8,4,4,{2,2,2,2,2,2,2,2}} 307# 308# @param Guid: Guid to be checked 309# 310def IsValidCFormatGuid(Guid): 311 # 312 # Valid: { 0xf0b11735, 0x87a0, 0x4193, {0xb2, 0x66, 0x53, 0x8c, 0x38, 313 # 0xaf, 0x48, 0xce }} 314 # Invalid: { 0xf0b11735, 0x87a0, 0x4193, {0xb2, 0x66, 0x53, 0x8c, 0x38, 315 # 0xaf, 0x48, 0xce }} 0x123 316 # Invalid: { 0xf0b1 1735, 0x87a0, 0x4193, {0xb2, 0x66, 0x53, 0x8c, 0x38, 317 # 0xaf, 0x48, 0xce }} 318 # 319 List = ['{', 10, ',', 6, ',', 6, ',{', 4, ',', 4, ',', 4, 320 ',', 4, ',', 4, ',', 4, ',', 4, ',', 4, '}}'] 321 Index = 0 322 Value = '' 323 SepValue = '' 324 for Char in Guid: 325 if Char not in '{},\t ': 326 Value += Char 327 continue 328 if Value: 329 try: 330 # 331 # Index may out of bound 332 # 333 if not SepValue or SepValue != List[Index]: 334 return False 335 Index += 1 336 SepValue = '' 337 338 if not Value.startswith('0x') and not Value.startswith('0X'): 339 return False 340 341 # 342 # Index may out of bound 343 # 344 if type(List[Index]) != type(1) or \ 345 len(Value) > List[Index] or len(Value) < 3: 346 return False 347 348 # 349 # Check if string can be converted to integer 350 # Throw exception if not 351 # 352 int(Value, 16) 353 except BaseException: 354 # 355 # Exception caught means invalid format 356 # 357 return False 358 Value = '' 359 Index += 1 360 if Char in '{},': 361 SepValue += Char 362 363 return SepValue == '}}' and Value == '' 364 365## IsValidPcdType 366# 367# Check whether the PCD type is valid 368# 369# @param PcdTypeString: The PcdType string need to be checked. 370# 371def IsValidPcdType(PcdTypeString): 372 if PcdTypeString.upper() in PCD_USAGE_TYPE_LIST_OF_MODULE: 373 return True 374 else: 375 return False 376 377## IsValidWord 378# 379# Check whether the word is valid. 380# <Word> ::= (a-zA-Z0-9_)(a-zA-Z0-9_-){0,} Alphanumeric characters with 381# optional 382# dash "-" and/or underscore "_" characters. No whitespace 383# characters are permitted. 384# 385# @param Word: The word string need to be checked. 386# 387def IsValidWord(Word): 388 if not Word: 389 return False 390 # 391 # The first char should be alpha, _ or Digit. 392 # 393 if not Word[0].isalnum() and \ 394 not Word[0] == '_' and \ 395 not Word[0].isdigit(): 396 return False 397 398 LastChar = '' 399 for Char in Word[1:]: 400 if (not Char.isalpha()) and \ 401 (not Char.isdigit()) and \ 402 Char != '-' and \ 403 Char != '_' and \ 404 Char != '.': 405 return False 406 if Char == '.' and LastChar == '.': 407 return False 408 LastChar = Char 409 410 return True 411 412 413## IsValidSimpleWord 414# 415# Check whether the SimpleWord is valid. 416# <SimpleWord> ::= (a-zA-Z0-9)(a-zA-Z0-9_-){0,} 417# A word that cannot contain a period character. 418# 419# @param Word: The word string need to be checked. 420# 421def IsValidSimpleWord(Word): 422 ReIsValidSimpleWord = \ 423 re.compile(r"^[0-9A-Za-z][0-9A-Za-z\-_]*$", re.DOTALL) 424 Word = Word.strip() 425 if not Word: 426 return False 427 428 if not ReIsValidSimpleWord.match(Word): 429 return False 430 431 return True 432 433## IsValidDecVersion 434# 435# Check whether the decimal version is valid. 436# <DecVersion> ::= (0-9){1,} ["." (0-9){1,}] 437# 438# @param Word: The word string need to be checked. 439# 440def IsValidDecVersion(Word): 441 if Word.find('.') > -1: 442 ReIsValidDecVersion = re.compile(r"[0-9]+\.?[0-9]+$") 443 else: 444 ReIsValidDecVersion = re.compile(r"[0-9]+$") 445 if ReIsValidDecVersion.match(Word) == None: 446 return False 447 return True 448 449## IsValidHexVersion 450# 451# Check whether the hex version is valid. 452# <HexVersion> ::= "0x" <Major> <Minor> 453# <Major> ::= <HexDigit>{4} 454# <Minor> ::= <HexDigit>{4} 455# 456# @param Word: The word string need to be checked. 457# 458def IsValidHexVersion(Word): 459 ReIsValidHexVersion = re.compile(r"[0][xX][0-9A-Fa-f]{8}$", re.DOTALL) 460 if ReIsValidHexVersion.match(Word) == None: 461 return False 462 463 return True 464 465## IsValidBuildNumber 466# 467# Check whether the BUILD_NUMBER is valid. 468# ["BUILD_NUMBER" "=" <Integer>{1,4} <EOL>] 469# 470# @param Word: The BUILD_NUMBER string need to be checked. 471# 472def IsValidBuildNumber(Word): 473 ReIsValieBuildNumber = re.compile(r"[0-9]{1,4}$", re.DOTALL) 474 if ReIsValieBuildNumber.match(Word) == None: 475 return False 476 477 return True 478 479## IsValidDepex 480# 481# Check whether the Depex is valid. 482# 483# @param Word: The Depex string need to be checked. 484# 485def IsValidDepex(Word): 486 Index = Word.upper().find("PUSH") 487 if Index > -1: 488 return IsValidCFormatGuid(Word[Index+4:].strip()) 489 490 ReIsValidCName = re.compile(r"^[A-Za-z_][0-9A-Za-z_\s\.]*$", re.DOTALL) 491 if ReIsValidCName.match(Word) == None: 492 return False 493 494 return True 495 496## IsValidNormalizedString 497# 498# Check 499# <NormalizedString> ::= <DblQuote> [{<Word>} {<Space>}]{1,} <DblQuote> 500# <Space> ::= 0x20 501# 502# @param String: string to be checked 503# 504def IsValidNormalizedString(String): 505 if String == '': 506 return True 507 508 for Char in String: 509 if Char == '\t': 510 return False 511 512 StringList = GetSplitValueList(String, TAB_SPACE_SPLIT) 513 514 for Item in StringList: 515 if not Item: 516 continue 517 if not IsValidWord(Item): 518 return False 519 520 return True 521 522## IsValidIdString 523# 524# Check whether the IdString is valid. 525# 526# @param IdString: The IdString need to be checked. 527# 528def IsValidIdString(String): 529 if IsValidSimpleWord(String.strip()): 530 return True 531 532 if String.strip().startswith('"') and \ 533 String.strip().endswith('"'): 534 String = String[1:-1] 535 if String.strip() == "": 536 return True 537 if IsValidNormalizedString(String): 538 return True 539 540 return False 541 542## IsValidVersionString 543# 544# Check whether the VersionString is valid. 545# <AsciiString> ::= [ [<WhiteSpace>]{0,} [<AsciiChars>]{0,} ] {0,} 546# <WhiteSpace> ::= {<Tab>} {<Space>} 547# <Tab> ::= 0x09 548# <Space> ::= 0x20 549# <AsciiChars> ::= (0x21 - 0x7E) 550# 551# @param VersionString: The VersionString need to be checked. 552# 553def IsValidVersionString(VersionString): 554 VersionString = VersionString.strip() 555 for Char in VersionString: 556 if not (Char >= 0x21 and Char <= 0x7E): 557 return False 558 559 return True 560 561## IsValidPcdValue 562# 563# Check whether the PcdValue is valid. 564# 565# @param VersionString: The PcdValue need to be checked. 566# 567def IsValidPcdValue(PcdValue): 568 for Char in PcdValue: 569 if Char == '\n' or Char == '\t' or Char == '\f': 570 return False 571 572 # 573 # <Boolean> 574 # 575 if IsValidFeatureFlagExp(PcdValue, True)[0]: 576 return True 577 578 # 579 # <Number> ::= {<Integer>} {<HexNumber>} 580 # <Integer> ::= {(0-9)} {(1-9)(0-9){1,}} 581 # <HexNumber> ::= "0x" <HexDigit>{1,} 582 # <HexDigit> ::= (a-fA-F0-9) 583 # 584 if IsValidHex(PcdValue): 585 return True 586 587 ReIsValidIntegerSingle = re.compile(r"^\s*[0-9]\s*$", re.DOTALL) 588 if ReIsValidIntegerSingle.match(PcdValue) != None: 589 return True 590 591 ReIsValidIntegerMulti = re.compile(r"^\s*[1-9][0-9]+\s*$", re.DOTALL) 592 if ReIsValidIntegerMulti.match(PcdValue) != None: 593 return True 594 595 # 596 # <StringVal> ::= {<StringType>} {<Array>} {"$(" <MACRO> ")"} 597 # <StringType> ::= {<UnicodeString>} {<CString>} 598 # 599 ReIsValidStringType = re.compile(r"^\s*[\"L].*[\"]\s*$") 600 if ReIsValidStringType.match(PcdValue): 601 IsTrue = False 602 if PcdValue.strip().startswith('L\"'): 603 StringValue = PcdValue.strip().lstrip('L\"').rstrip('\"') 604 if IsValidBareCString(StringValue): 605 IsTrue = True 606 elif PcdValue.strip().startswith('\"'): 607 StringValue = PcdValue.strip().lstrip('\"').rstrip('\"') 608 if IsValidBareCString(StringValue): 609 IsTrue = True 610 if IsTrue: 611 return IsTrue 612 613 # 614 # <Array> ::= {<CArray>} {<NList>} {<CFormatGUID>} 615 # <CArray> ::= "{" [<NList>] <CArray>{0,} "}" 616 # <NList> ::= <HexByte> ["," <HexByte>]{0,} 617 # <HexDigit> ::= (a-fA-F0-9) 618 # <HexByte> ::= "0x" <HexDigit>{1,2} 619 # 620 if IsValidCFormatGuid(PcdValue): 621 return True 622 623 ReIsValidByteHex = re.compile(r"^\s*0x[0-9a-fA-F]{1,2}\s*$", re.DOTALL) 624 if PcdValue.strip().startswith('{') and PcdValue.strip().endswith('}') : 625 StringValue = PcdValue.strip().lstrip('{').rstrip('}') 626 ValueList = StringValue.split(',') 627 AllValidFlag = True 628 for ValueItem in ValueList: 629 if not ReIsValidByteHex.match(ValueItem.strip()): 630 AllValidFlag = False 631 632 if AllValidFlag: 633 return True 634 635 # 636 # NList 637 # 638 AllValidFlag = True 639 ValueList = PcdValue.split(',') 640 for ValueItem in ValueList: 641 if not ReIsValidByteHex.match(ValueItem.strip()): 642 AllValidFlag = False 643 644 if AllValidFlag: 645 return True 646 647 return False 648 649## IsValidCVariableName 650# 651# Check whether the PcdValue is valid. 652# 653# @param VersionString: The PcdValue need to be checked. 654# 655def IsValidCVariableName(CName): 656 ReIsValidCName = re.compile(r"^[A-Za-z_][0-9A-Za-z_]*$", re.DOTALL) 657 if ReIsValidCName.match(CName) == None: 658 return False 659 660 return True 661 662## IsValidIdentifier 663# 664# <Identifier> ::= <NonDigit> <Chars>{0,} 665# <Chars> ::= (a-zA-Z0-9_) 666# <NonDigit> ::= (a-zA-Z_) 667# 668# @param Ident: identifier to be checked 669# 670def IsValidIdentifier(Ident): 671 ReIdent = re.compile(r"^[A-Za-z_][0-9A-Za-z_]*$", re.DOTALL) 672 if ReIdent.match(Ident) == None: 673 return False 674 675 return True 676 677## IsValidDecVersionVal 678# 679# {(0-9){1,} "." (0-99)} 680# 681# @param Ver: version to be checked 682# 683def IsValidDecVersionVal(Ver): 684 ReVersion = re.compile(r"[0-9]+(\.[0-9]{1,2})$") 685 686 if ReVersion.match(Ver) == None: 687 return False 688 689 return True 690 691 692## IsValidLibName 693# 694# (A-Z)(a-zA-Z0-9){0,} and could not be "NULL" 695# 696def IsValidLibName(LibName): 697 if LibName == 'NULL': 698 return False 699 ReLibName = re.compile("^[A-Z]+[a-zA-Z0-9]*$") 700 if not ReLibName.match(LibName): 701 return False 702 703 return True 704 705# IsValidUserId 706# 707# <UserId> ::= (a-zA-Z)(a-zA-Z0-9_.){0,} 708# Words that contain period "." must be encapsulated in double quotation marks. 709# 710def IsValidUserId(UserId): 711 UserId = UserId.strip() 712 Quoted = False 713 if UserId.startswith('"') and UserId.endswith('"'): 714 Quoted = True 715 UserId = UserId[1:-1] 716 if not UserId or not UserId[0].isalpha(): 717 return False 718 for Char in UserId[1:]: 719 if not Char.isalnum() and not Char in '_.': 720 return False 721 if Char == '.' and not Quoted: 722 return False 723 return True 724 725# 726# Check if a UTF16-LE file has a BOM header 727# 728def CheckUTF16FileHeader(File): 729 FileIn = open(File, 'rb').read(2) 730 if FileIn != '\xff\xfe': 731 return False 732 733 return True 734