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 ''' 16 PaserValidate 17 ''' 18 19 import os.path 20 import re 21 import platform 22 23 from Library.DataType import MODULE_LIST 24 from Library.DataType import COMPONENT_TYPE_LIST 25 from Library.DataType import PCD_USAGE_TYPE_LIST_OF_MODULE 26 from Library.DataType import TAB_SPACE_SPLIT 27 from Library.String import GetSplitValueList 28 from Library.ExpressionValidate import IsValidBareCString 29 from Library.ExpressionValidate import IsValidFeatureFlagExp 30 from 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 # 38 def __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 # 52 def 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 # 69 def 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 # 99 def 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 # 107 def 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 # 117 def 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 # 129 def 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 # 141 def 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 # 158 def 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 # 173 def 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 # 190 def 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 # 223 def 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 # 234 def 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 # 286 def 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 # 310 def 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 # 371 def 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 # 387 def 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 # 421 def 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 # 440 def 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 # 458 def 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 # 472 def 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 # 485 def 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 # 504 def 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 # 528 def 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 # 553 def 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 # 567 def 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 # 655 def 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 # 670 def 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 # 683 def 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 # 696 def 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 # 710 def 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 # 728 def CheckUTF16FileHeader(File): 729 FileIn = open(File, 'rb').read(2) 730 if FileIn != '\xff\xfe': 731 return False 732 733 return True 734