Home | History | Annotate | Download | only in Library
      1 ## @file

      2 # This file is used to define common string related functions used in parsing

      3 # process

      4 #

      5 # Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>

      6 #

      7 # This program and the accompanying materials are licensed and made available

      8 # under the terms and conditions of the BSD License which accompanies this

      9 # distribution. The full text of the license may be found at

     10 # http://opensource.org/licenses/bsd-license.php

     11 #

     12 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

     13 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

     14 #

     15 '''
     16 String
     17 '''
     18 ##

     19 # Import Modules

     20 #

     21 import re
     22 import os.path
     23 from string import strip
     24 import Logger.Log as Logger
     25 import Library.DataType as DataType
     26 from Logger.ToolError import FORMAT_INVALID
     27 from Logger.ToolError import PARSER_ERROR
     28 from Logger import StringTable as ST
     29 
     30 #

     31 # Regular expression for matching macro used in DSC/DEC/INF file inclusion

     32 #

     33 gMACRO_PATTERN = re.compile("\$\(([_A-Z][_A-Z0-9]*)\)", re.UNICODE)
     34 
     35 ## GetSplitValueList

     36 #

     37 # Get a value list from a string with multiple values splited with SplitTag

     38 # The default SplitTag is DataType.TAB_VALUE_SPLIT

     39 # 'AAA|BBB|CCC' -> ['AAA', 'BBB', 'CCC']

     40 #

     41 # @param String:    The input string to be splitted

     42 # @param SplitTag:  The split key, default is DataType.TAB_VALUE_SPLIT

     43 # @param MaxSplit:  The max number of split values, default is -1

     44 #

     45 #

     46 def GetSplitValueList(String, SplitTag=DataType.TAB_VALUE_SPLIT, MaxSplit= -1):
     47     return map(lambda l: l.strip(), String.split(SplitTag, MaxSplit))
     48 
     49 ## MergeArches

     50 #

     51 # Find a key's all arches in dict, add the new arch to the list

     52 # If not exist any arch, set the arch directly

     53 #

     54 # @param Dict:  The input value for Dict

     55 # @param Key:   The input value for Key

     56 # @param Arch:  The Arch to be added or merged

     57 #

     58 def MergeArches(Dict, Key, Arch):
     59     if Key in Dict.keys():
     60         Dict[Key].append(Arch)
     61     else:
     62         Dict[Key] = Arch.split()
     63 
     64 ## GenDefines

     65 #

     66 # Parse a string with format "DEFINE <VarName> = <PATH>"

     67 # Generate a map Defines[VarName] = PATH

     68 # Return False if invalid format

     69 #

     70 # @param String:   String with DEFINE statement

     71 # @param Arch:     Supportted Arch

     72 # @param Defines:  DEFINE statement to be parsed

     73 #

     74 def GenDefines(String, Arch, Defines):
     75     if String.find(DataType.TAB_DEFINE + ' ') > -1:
     76         List = String.replace(DataType.TAB_DEFINE + ' ', '').\
     77         split(DataType.TAB_EQUAL_SPLIT)
     78         if len(List) == 2:
     79             Defines[(CleanString(List[0]), Arch)] = CleanString(List[1])
     80             return 0
     81         else:
     82             return -1
     83     return 1
     84 
     85 ## GetLibraryClassesWithModuleType

     86 #

     87 # Get Library Class definition when no module type defined

     88 #

     89 # @param Lines:             The content to be parsed

     90 # @param Key:               Reserved

     91 # @param KeyValues:         To store data after parsing

     92 # @param CommentCharacter:  Comment char, used to ignore comment content

     93 #

     94 def GetLibraryClassesWithModuleType(Lines, Key, KeyValues, CommentCharacter):
     95     NewKey = SplitModuleType(Key)
     96     Lines = Lines.split(DataType.TAB_SECTION_END, 1)[1]
     97     LineList = Lines.splitlines()
     98     for Line in LineList:
     99         Line = CleanString(Line, CommentCharacter)
    100         if Line != '' and Line[0] != CommentCharacter:
    101             KeyValues.append([CleanString(Line, CommentCharacter), NewKey[1]])
    102 
    103     return True
    104 
    105 ## GetDynamics

    106 #

    107 # Get Dynamic Pcds

    108 #

    109 # @param Lines:             The content to be parsed

    110 # @param Key:               Reserved

    111 # @param KeyValues:         To store data after parsing

    112 # @param CommentCharacter:  Comment char, used to ignore comment content

    113 #

    114 def GetDynamics(Lines, Key, KeyValues, CommentCharacter):
    115     #

    116     # Get SkuId Name List

    117     #

    118     SkuIdNameList = SplitModuleType(Key)
    119 
    120     Lines = Lines.split(DataType.TAB_SECTION_END, 1)[1]
    121     LineList = Lines.splitlines()
    122     for Line in LineList:
    123         Line = CleanString(Line, CommentCharacter)
    124         if Line != '' and Line[0] != CommentCharacter:
    125             KeyValues.append([CleanString(Line, CommentCharacter), SkuIdNameList[1]])
    126 
    127     return True
    128 
    129 ## SplitModuleType

    130 #

    131 # Split ModuleType out of section defien to get key

    132 # [LibraryClass.Arch.ModuleType|ModuleType|ModuleType] -> [

    133 # 'LibraryClass.Arch', ['ModuleType', 'ModuleType', 'ModuleType'] ]

    134 #

    135 # @param Key:  String to be parsed

    136 #

    137 def SplitModuleType(Key):
    138     KeyList = Key.split(DataType.TAB_SPLIT)
    139     #

    140     # Fill in for arch

    141     #

    142     KeyList.append('')
    143     #

    144     # Fill in for moduletype

    145     #

    146     KeyList.append('')
    147     ReturnValue = []
    148     KeyValue = KeyList[0]
    149     if KeyList[1] != '':
    150         KeyValue = KeyValue + DataType.TAB_SPLIT + KeyList[1]
    151     ReturnValue.append(KeyValue)
    152     ReturnValue.append(GetSplitValueList(KeyList[2]))
    153 
    154     return ReturnValue
    155 
    156 ## Replace macro in string

    157 #

    158 # This method replace macros used in given string. The macros are given in a

    159 # dictionary.

    160 #

    161 # @param String             String to be processed

    162 # @param MacroDefinitions   The macro definitions in the form of dictionary

    163 # @param SelfReplacement    To decide whether replace un-defined macro to ''

    164 # @param Line:              The content contain line string and line number

    165 # @param FileName:        The meta-file file name

    166 #

    167 def ReplaceMacro(String, MacroDefinitions=None, SelfReplacement=False, Line=None, FileName=None, Flag=False):
    168     LastString = String
    169     if MacroDefinitions == None:
    170         MacroDefinitions = {}
    171     while MacroDefinitions:
    172         QuotedStringList = []
    173         HaveQuotedMacroFlag = False
    174         if not Flag:
    175             MacroUsed = gMACRO_PATTERN.findall(String)
    176         else:
    177             ReQuotedString = re.compile('\"')
    178             QuotedStringList = ReQuotedString.split(String)
    179             if len(QuotedStringList) >= 3:
    180                 HaveQuotedMacroFlag = True
    181             Count = 0
    182             MacroString = ""
    183             for QuotedStringItem in QuotedStringList:
    184                 Count += 1
    185                 if Count % 2 != 0:
    186                     MacroString += QuotedStringItem
    187 
    188                 if Count == len(QuotedStringList) and Count % 2 == 0:
    189                     MacroString += QuotedStringItem
    190 
    191             MacroUsed = gMACRO_PATTERN.findall(MacroString)
    192         #

    193         # no macro found in String, stop replacing

    194         #

    195         if len(MacroUsed) == 0:
    196             break
    197         for Macro in MacroUsed:
    198             if Macro not in MacroDefinitions:
    199                 if SelfReplacement:
    200                     String = String.replace("$(%s)" % Macro, '')
    201                     Logger.Debug(5, "Delete undefined MACROs in file %s line %d: %s!" % (FileName, Line[1], Line[0]))
    202                 continue
    203             if not HaveQuotedMacroFlag:
    204                 String = String.replace("$(%s)" % Macro, MacroDefinitions[Macro])
    205             else:
    206                 Count = 0
    207                 for QuotedStringItem in QuotedStringList:
    208                     Count += 1
    209                     if Count % 2 != 0:
    210                         QuotedStringList[Count - 1] = QuotedStringList[Count - 1].replace("$(%s)" % Macro,
    211                                                                         MacroDefinitions[Macro])
    212                     elif Count == len(QuotedStringList) and Count % 2 == 0:
    213                         QuotedStringList[Count - 1] = QuotedStringList[Count - 1].replace("$(%s)" % Macro,
    214                                                                         MacroDefinitions[Macro])
    215 
    216         RetString = ''
    217         if HaveQuotedMacroFlag:
    218             Count = 0
    219             for QuotedStringItem in QuotedStringList:
    220                 Count += 1
    221                 if Count != len(QuotedStringList):
    222                     RetString += QuotedStringList[Count - 1] + "\""
    223                 else:
    224                     RetString += QuotedStringList[Count - 1]
    225 
    226             String = RetString
    227 
    228         #

    229         # in case there's macro not defined

    230         #

    231         if String == LastString:
    232             break
    233         LastString = String
    234 
    235     return String
    236 
    237 ## NormPath

    238 #

    239 # Create a normal path

    240 # And replace DFEINE in the path

    241 #

    242 # @param Path:     The input value for Path to be converted

    243 # @param Defines:  A set for DEFINE statement

    244 #

    245 def NormPath(Path, Defines=None):
    246     IsRelativePath = False
    247     if Defines == None:
    248         Defines = {}
    249     if Path:
    250         if Path[0] == '.':
    251             IsRelativePath = True
    252         #

    253         # Replace with Define

    254         #

    255         if Defines:
    256             Path = ReplaceMacro(Path, Defines)
    257         #

    258         # To local path format

    259         #

    260         Path = os.path.normpath(Path)
    261 
    262     if IsRelativePath and Path[0] != '.':
    263         Path = os.path.join('.', Path)
    264     return Path
    265 
    266 ## CleanString

    267 #

    268 # Remove comments in a string

    269 # Remove spaces

    270 #

    271 # @param Line:              The string to be cleaned

    272 # @param CommentCharacter:  Comment char, used to ignore comment content,

    273 #                           default is DataType.TAB_COMMENT_SPLIT

    274 #

    275 def CleanString(Line, CommentCharacter=DataType.TAB_COMMENT_SPLIT, AllowCppStyleComment=False):
    276     #

    277     # remove whitespace

    278     #

    279     Line = Line.strip()
    280     #

    281     # Replace EDK1's comment character

    282     #

    283     if AllowCppStyleComment:
    284         Line = Line.replace(DataType.TAB_COMMENT_EDK1_SPLIT, CommentCharacter)
    285     #

    286     # remove comments, but we should escape comment character in string

    287     #

    288     InString = False
    289     for Index in range(0, len(Line)):
    290         if Line[Index] == '"':
    291             InString = not InString
    292         elif Line[Index] == CommentCharacter and not InString:
    293             Line = Line[0: Index]
    294             break
    295     #

    296     # remove whitespace again

    297     #

    298     Line = Line.strip()
    299 
    300     return Line
    301 
    302 ## CleanString2

    303 #

    304 # Split comments in a string

    305 # Remove spaces

    306 #

    307 # @param Line:              The string to be cleaned

    308 # @param CommentCharacter:  Comment char, used to ignore comment content,

    309 #                           default is DataType.TAB_COMMENT_SPLIT

    310 #

    311 def CleanString2(Line, CommentCharacter=DataType.TAB_COMMENT_SPLIT, AllowCppStyleComment=False):
    312     #

    313     # remove whitespace

    314     #

    315     Line = Line.strip()
    316     #

    317     # Replace EDK1's comment character

    318     #

    319     if AllowCppStyleComment:
    320         Line = Line.replace(DataType.TAB_COMMENT_EDK1_SPLIT, CommentCharacter)
    321     #

    322     # separate comments and statements

    323     #

    324     LineParts = Line.split(CommentCharacter, 1)
    325     #

    326     # remove whitespace again

    327     #

    328     Line = LineParts[0].strip()
    329     if len(LineParts) > 1:
    330         Comment = LineParts[1].strip()
    331         #

    332         # Remove prefixed and trailing comment characters

    333         #

    334         Start = 0
    335         End = len(Comment)
    336         while Start < End and Comment.startswith(CommentCharacter, Start, End):
    337             Start += 1
    338         while End >= 0 and Comment.endswith(CommentCharacter, Start, End):
    339             End -= 1
    340         Comment = Comment[Start:End]
    341         Comment = Comment.strip()
    342     else:
    343         Comment = ''
    344 
    345     return Line, Comment
    346 
    347 ## GetMultipleValuesOfKeyFromLines

    348 #

    349 # Parse multiple strings to clean comment and spaces

    350 # The result is saved to KeyValues

    351 #

    352 # @param Lines:             The content to be parsed

    353 # @param Key:               Reserved

    354 # @param KeyValues:         To store data after parsing

    355 # @param CommentCharacter:  Comment char, used to ignore comment content

    356 #

    357 def GetMultipleValuesOfKeyFromLines(Lines, Key, KeyValues, CommentCharacter):
    358     if Key:
    359         pass
    360     if KeyValues:
    361         pass
    362     Lines = Lines.split(DataType.TAB_SECTION_END, 1)[1]
    363     LineList = Lines.split('\n')
    364     for Line in LineList:
    365         Line = CleanString(Line, CommentCharacter)
    366         if Line != '' and Line[0] != CommentCharacter:
    367             KeyValues += [Line]
    368     return True
    369 
    370 ## GetDefineValue

    371 #

    372 # Parse a DEFINE statement to get defined value

    373 # DEFINE Key Value

    374 #

    375 # @param String:            The content to be parsed

    376 # @param Key:               The key of DEFINE statement

    377 # @param CommentCharacter:  Comment char, used to ignore comment content

    378 #

    379 def GetDefineValue(String, Key, CommentCharacter):
    380     if CommentCharacter:
    381         pass
    382     String = CleanString(String)
    383     return String[String.find(Key + ' ') + len(Key + ' ') : ]
    384 
    385 ## GetSingleValueOfKeyFromLines

    386 #

    387 # Parse multiple strings as below to get value of each definition line

    388 # Key1 = Value1

    389 # Key2 = Value2

    390 # The result is saved to Dictionary

    391 #

    392 # @param Lines:                The content to be parsed

    393 # @param Dictionary:           To store data after parsing

    394 # @param CommentCharacter:     Comment char, be used to ignore comment content

    395 # @param KeySplitCharacter:    Key split char, between key name and key value.

    396 #                              Key1 = Value1, '=' is the key split char

    397 # @param ValueSplitFlag:       Value split flag, be used to decide if has

    398 #                              multiple values

    399 # @param ValueSplitCharacter:  Value split char, be used to split multiple

    400 #                              values. Key1 = Value1|Value2, '|' is the value

    401 #                              split char

    402 #

    403 def GetSingleValueOfKeyFromLines(Lines, Dictionary, CommentCharacter, KeySplitCharacter, \
    404                                  ValueSplitFlag, ValueSplitCharacter):
    405     Lines = Lines.split('\n')
    406     Keys = []
    407     Value = ''
    408     DefineValues = ['']
    409     SpecValues = ['']
    410 
    411     for Line in Lines:
    412         #

    413         # Handle DEFINE and SPEC

    414         #

    415         if Line.find(DataType.TAB_INF_DEFINES_DEFINE + ' ') > -1:
    416             if '' in DefineValues:
    417                 DefineValues.remove('')
    418             DefineValues.append(GetDefineValue(Line, DataType.TAB_INF_DEFINES_DEFINE, CommentCharacter))
    419             continue
    420         if Line.find(DataType.TAB_INF_DEFINES_SPEC + ' ') > -1:
    421             if '' in SpecValues:
    422                 SpecValues.remove('')
    423             SpecValues.append(GetDefineValue(Line, DataType.TAB_INF_DEFINES_SPEC, CommentCharacter))
    424             continue
    425 
    426         #

    427         # Handle Others

    428         #

    429         LineList = Line.split(KeySplitCharacter, 1)
    430         if len(LineList) >= 2:
    431             Key = LineList[0].split()
    432             if len(Key) == 1 and Key[0][0] != CommentCharacter:
    433                 #

    434                 # Remove comments and white spaces

    435                 #

    436                 LineList[1] = CleanString(LineList[1], CommentCharacter)
    437                 if ValueSplitFlag:
    438                     Value = map(strip, LineList[1].split(ValueSplitCharacter))
    439                 else:
    440                     Value = CleanString(LineList[1], CommentCharacter).splitlines()
    441 
    442                 if Key[0] in Dictionary:
    443                     if Key[0] not in Keys:
    444                         Dictionary[Key[0]] = Value
    445                         Keys.append(Key[0])
    446                     else:
    447                         Dictionary[Key[0]].extend(Value)
    448                 else:
    449                     Dictionary[DataType.TAB_INF_DEFINES_MACRO][Key[0]] = Value[0]
    450 
    451     if DefineValues == []:
    452         DefineValues = ['']
    453     if SpecValues == []:
    454         SpecValues = ['']
    455     Dictionary[DataType.TAB_INF_DEFINES_DEFINE] = DefineValues
    456     Dictionary[DataType.TAB_INF_DEFINES_SPEC] = SpecValues
    457 
    458     return True
    459 
    460 ## The content to be parsed

    461 #

    462 # Do pre-check for a file before it is parsed

    463 # Check $()

    464 # Check []

    465 #

    466 # @param FileName:       Used for error report

    467 # @param FileContent:    File content to be parsed

    468 # @param SupSectionTag:  Used for error report

    469 #

    470 def PreCheck(FileName, FileContent, SupSectionTag):
    471     if SupSectionTag:
    472         pass
    473     LineNo = 0
    474     IsFailed = False
    475     NewFileContent = ''
    476     for Line in FileContent.splitlines():
    477         LineNo = LineNo + 1
    478         #

    479         # Clean current line

    480         #

    481         Line = CleanString(Line)
    482         #

    483         # Remove commented line

    484         #

    485         if Line.find(DataType.TAB_COMMA_SPLIT) == 0:
    486             Line = ''
    487         #

    488         # Check $()

    489         #

    490         if Line.find('$') > -1:
    491             if Line.find('$(') < 0 or Line.find(')') < 0:
    492                 Logger.Error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError=Logger.IS_RAISE_ERROR)
    493         #

    494         # Check []

    495         #

    496         if Line.find('[') > -1 or Line.find(']') > -1:
    497             #

    498             # Only get one '[' or one ']'

    499             #

    500             if not (Line.find('[') > -1 and Line.find(']') > -1):
    501                 Logger.Error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError=Logger.IS_RAISE_ERROR)
    502         #

    503         # Regenerate FileContent

    504         #

    505         NewFileContent = NewFileContent + Line + '\r\n'
    506 
    507     if IsFailed:
    508         Logger.Error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError=Logger.IS_RAISE_ERROR)
    509 
    510     return NewFileContent
    511 
    512 ## CheckFileType

    513 #

    514 # Check if the Filename is including ExtName

    515 # Return True if it exists

    516 # Raise a error message if it not exists

    517 #

    518 # @param CheckFilename:      Name of the file to be checked

    519 # @param ExtName:            Ext name of the file to be checked

    520 # @param ContainerFilename:  The container file which describes the file to be

    521 #                            checked, used for error report

    522 # @param SectionName:        Used for error report

    523 # @param Line:               The line in container file which defines the file

    524 #                            to be checked

    525 #

    526 def CheckFileType(CheckFilename, ExtName, ContainerFilename, SectionName, Line, LineNo= -1):
    527     if CheckFilename != '' and CheckFilename != None:
    528         (Root, Ext) = os.path.splitext(CheckFilename)
    529         if Ext.upper() != ExtName.upper() and Root:
    530             ContainerFile = open(ContainerFilename, 'r').read()
    531             if LineNo == -1:
    532                 LineNo = GetLineNo(ContainerFile, Line)
    533             ErrorMsg = ST.ERR_SECTIONNAME_INVALID % (SectionName, CheckFilename, ExtName)
    534             Logger.Error("Parser", PARSER_ERROR, ErrorMsg, Line=LineNo, \
    535                          File=ContainerFilename, RaiseError=Logger.IS_RAISE_ERROR)
    536 
    537     return True
    538 
    539 ## CheckFileExist

    540 #

    541 # Check if the file exists

    542 # Return True if it exists

    543 # Raise a error message if it not exists

    544 #

    545 # @param CheckFilename:      Name of the file to be checked

    546 # @param WorkspaceDir:       Current workspace dir

    547 # @param ContainerFilename:  The container file which describes the file to

    548 #                            be checked, used for error report

    549 # @param SectionName:        Used for error report

    550 # @param Line:               The line in container file which defines the

    551 #                            file to be checked

    552 #

    553 def CheckFileExist(WorkspaceDir, CheckFilename, ContainerFilename, SectionName, Line, LineNo= -1):
    554     CheckFile = ''
    555     if CheckFilename != '' and CheckFilename != None:
    556         CheckFile = WorkspaceFile(WorkspaceDir, CheckFilename)
    557         if not os.path.isfile(CheckFile):
    558             ContainerFile = open(ContainerFilename, 'r').read()
    559             if LineNo == -1:
    560                 LineNo = GetLineNo(ContainerFile, Line)
    561             ErrorMsg = ST.ERR_CHECKFILE_NOTFOUND % (CheckFile, SectionName)
    562             Logger.Error("Parser", PARSER_ERROR, ErrorMsg,
    563                             File=ContainerFilename, Line=LineNo, RaiseError=Logger.IS_RAISE_ERROR)
    564     return CheckFile
    565 
    566 ## GetLineNo

    567 #

    568 # Find the index of a line in a file

    569 #

    570 # @param FileContent:  Search scope

    571 # @param Line:         Search key

    572 #

    573 def GetLineNo(FileContent, Line, IsIgnoreComment=True):
    574     LineList = FileContent.splitlines()
    575     for Index in range(len(LineList)):
    576         if LineList[Index].find(Line) > -1:
    577             #

    578             # Ignore statement in comment

    579             #

    580             if IsIgnoreComment:
    581                 if LineList[Index].strip()[0] == DataType.TAB_COMMENT_SPLIT:
    582                     continue
    583             return Index + 1
    584 
    585     return -1
    586 
    587 ## RaiseParserError

    588 #

    589 # Raise a parser error

    590 #

    591 # @param Line:     String which has error

    592 # @param Section:  Used for error report

    593 # @param File:     File which has the string

    594 # @param Format:   Correct format

    595 #

    596 def RaiseParserError(Line, Section, File, Format='', LineNo= -1):
    597     if LineNo == -1:
    598         LineNo = GetLineNo(open(os.path.normpath(File), 'r').read(), Line)
    599     ErrorMsg = ST.ERR_INVALID_NOTFOUND % (Line, Section)
    600     if Format != '':
    601         Format = "Correct format is " + Format
    602     Logger.Error("Parser", PARSER_ERROR, ErrorMsg, File=File, Line=LineNo, \
    603                  ExtraData=Format, RaiseError=Logger.IS_RAISE_ERROR)
    604 
    605 ## WorkspaceFile

    606 #

    607 # Return a full path with workspace dir

    608 #

    609 # @param WorkspaceDir:  Workspace dir

    610 # @param Filename:      Relative file name

    611 #

    612 def WorkspaceFile(WorkspaceDir, Filename):
    613     return os.path.join(NormPath(WorkspaceDir), NormPath(Filename))
    614 
    615 ## Split string

    616 #

    617 # Revmove '"' which startswith and endswith string

    618 #

    619 # @param String:  The string need to be splited

    620 #

    621 def SplitString(String):
    622     if String.startswith('\"'):
    623         String = String[1:]
    624     if String.endswith('\"'):
    625         String = String[:-1]
    626     return String
    627 
    628 ## Convert To Sql String

    629 #

    630 # Replace "'" with "''" in each item of StringList

    631 #

    632 # @param StringList:  A list for strings to be converted

    633 #

    634 def ConvertToSqlString(StringList):
    635     return map(lambda s: s.replace("'", "''") , StringList)
    636 
    637 ## Convert To Sql String

    638 #

    639 # Replace "'" with "''" in the String

    640 #

    641 # @param String:  A String to be converted

    642 #

    643 def ConvertToSqlString2(String):
    644     return String.replace("'", "''")
    645 
    646 ## GetStringOfList

    647 #

    648 # Get String of a List

    649 #

    650 # @param Lines: string list

    651 # @param Split: split character

    652 #

    653 def GetStringOfList(List, Split=' '):
    654     if type(List) != type([]):
    655         return List
    656     Str = ''
    657     for Item in List:
    658         Str = Str + Item + Split
    659     return Str.strip()
    660 
    661 ## Get HelpTextList

    662 #

    663 # Get HelpTextList from HelpTextClassList

    664 #

    665 # @param HelpTextClassList: Help Text Class List

    666 #

    667 def GetHelpTextList(HelpTextClassList):
    668     List = []
    669     if HelpTextClassList:
    670         for HelpText in HelpTextClassList:
    671             if HelpText.String.endswith('\n'):
    672                 HelpText.String = HelpText.String[0: len(HelpText.String) - len('\n')]
    673                 List.extend(HelpText.String.split('\n'))
    674     return List
    675 
    676 ## Get String Array Length

    677 #

    678 # Get String Array Length

    679 #

    680 # @param String: the source string

    681 #

    682 def StringArrayLength(String):
    683     if isinstance(String, unicode):
    684         return (len(String) + 1) * 2 + 1
    685     elif String.startswith('L"'):
    686         return (len(String) - 3 + 1) * 2
    687     elif String.startswith('"'):
    688         return (len(String) - 2 + 1)
    689     else:
    690         return len(String.split()) + 1
    691 
    692 ## RemoveDupOption

    693 #

    694 # Remove Dup Option

    695 #

    696 # @param OptionString: the option string

    697 # @param Which: Which flag

    698 # @param Against: Against flag

    699 #

    700 def RemoveDupOption(OptionString, Which="/I", Against=None):
    701     OptionList = OptionString.split()
    702     ValueList = []
    703     if Against:
    704         ValueList += Against
    705     for Index in range(len(OptionList)):
    706         Opt = OptionList[Index]
    707         if not Opt.startswith(Which):
    708             continue
    709         if len(Opt) > len(Which):
    710             Val = Opt[len(Which):]
    711         else:
    712             Val = ""
    713         if Val in ValueList:
    714             OptionList[Index] = ""
    715         else:
    716             ValueList.append(Val)
    717     return " ".join(OptionList)
    718 
    719 ## Check if the string is HexDgit

    720 #

    721 # Return true if all characters in the string are digits and there is at

    722 # least one character

    723 # or valid Hexs (started with 0x, following by hexdigit letters)

    724 # , false otherwise.

    725 # @param string: input string

    726 #

    727 def IsHexDigit(Str):
    728     try:
    729         int(Str, 10)
    730         return True
    731     except ValueError:
    732         if len(Str) > 2 and Str.upper().startswith('0X'):
    733             try:
    734                 int(Str, 16)
    735                 return True
    736             except ValueError:
    737                 return False
    738     return False
    739 
    740 ## Check if the string is HexDgit and its interger value within limit of UINT32

    741 #

    742 # Return true if all characters in the string are digits and there is at

    743 # least one character

    744 # or valid Hexs (started with 0x, following by hexdigit letters)

    745 # , false otherwise.

    746 # @param string: input string

    747 #

    748 def IsHexDigitUINT32(Str):
    749     try:
    750         Value = int(Str, 10)
    751         if (Value <= 0xFFFFFFFF) and (Value >= 0):
    752             return True
    753     except ValueError:
    754         if len(Str) > 2 and Str.upper().startswith('0X'):
    755             try:
    756                 Value = int(Str, 16)
    757                 if (Value <= 0xFFFFFFFF) and (Value >= 0):
    758                     return True
    759             except ValueError:
    760                 return False
    761     return False
    762 
    763 ## CleanSpecialChar

    764 #

    765 # The ASCII text files of type INF, DEC, INI are edited by developers,

    766 # and may contain characters that cannot be directly translated to strings that

    767 # are conformant with the UDP XML Schema.  Any characters in this category

    768 # (0x00-0x08, TAB [0x09], 0x0B, 0x0C, 0x0E-0x1F, 0x80-0xFF)

    769 # must be converted to a space character[0x20] as part of the parsing process.

    770 #

    771 def ConvertSpecialChar(Lines):
    772     RetLines = []
    773     for line in Lines:
    774         ReMatchSpecialChar = re.compile(r"[\x00-\x08]|\x09|\x0b|\x0c|[\x0e-\x1f]|[\x7f-\xff]")
    775         RetLines.append(ReMatchSpecialChar.sub(' ', line))
    776 
    777     return RetLines
    778 
    779 ## __GetTokenList

    780 #

    781 # Assume Str is a valid feature flag expression.

    782 # Return a list which contains tokens: alpha numeric token and other token

    783 # Whitespace are not stripped

    784 #

    785 def __GetTokenList(Str):
    786     InQuote = False
    787     Token = ''
    788     TokenOP = ''
    789     PreChar = ''
    790     List = []
    791     for Char in Str:
    792         if InQuote:
    793             Token += Char
    794             if Char == '"' and PreChar != '\\':
    795                 InQuote = not InQuote
    796                 List.append(Token)
    797                 Token = ''
    798             continue
    799         if Char == '"':
    800             if Token and Token != 'L':
    801                 List.append(Token)
    802                 Token = ''
    803             if TokenOP:
    804                 List.append(TokenOP)
    805                 TokenOP = ''
    806             InQuote = not InQuote
    807             Token += Char
    808             continue
    809 
    810         if not (Char.isalnum() or Char in '_'):
    811             TokenOP += Char
    812             if Token:
    813                 List.append(Token)
    814                 Token = ''
    815         else:
    816             Token += Char
    817             if TokenOP:
    818                 List.append(TokenOP)
    819                 TokenOP = ''
    820 
    821         if PreChar == '\\' and Char == '\\':
    822             PreChar = ''
    823         else:
    824             PreChar = Char
    825     if Token:
    826         List.append(Token)
    827     if TokenOP:
    828         List.append(TokenOP)
    829     return List
    830 
    831 ## ConvertNEToNOTEQ
    832 #
    833 # Convert NE operator to NOT EQ
    834 # For example: 1 NE 2 -> 1 NOT EQ 2
    835 #
    836 # @param Expr: Feature flag expression to be converted
    837 #
    838 def ConvertNEToNOTEQ(Expr):
    839     List = __GetTokenList(Expr)
    840     for Index in range(len(List)):
    841         if List[Index] == 'NE':
    842             List[Index] = 'NOT EQ'
    843     return ''.join(List)
    844 
    845 ## ConvertNOTEQToNE
    846 #
    847 # Convert NOT EQ operator to NE
    848 # For example: 1 NOT NE 2 -> 1 NE 2
    849 #
    850 # @param Expr: Feature flag expression to be converted
    851 #
    852 def ConvertNOTEQToNE(Expr):
    853     List = __GetTokenList(Expr)
    854     HasNOT = False
    855     RetList = []
    856     for Token in List:
    857         if HasNOT and Token == 'EQ':
    858             # At least, 'NOT' is in the list
    859             while not RetList[-1].strip():
    860                 RetList.pop()
    861             RetList[-1] = 'NE'
    862             HasNOT = False
    863             continue
    864         if Token == 'NOT':
    865             HasNOT = True
    866         elif Token.strip():
    867             HasNOT = False
    868         RetList.append(Token)
    869 
    870     return ''.join(RetList)
    871 
    872 ## SplitPcdEntry
    873 #
    874 # Split an PCD entry string to Token.CName and PCD value and FFE.
    875 # NOTE: PCD Value and FFE can contain "|" in it's expression. And in INF specification, have below rule.
    876 # When using the characters "|" or "||" in an expression, the expression must be encapsulated in
    877 # open "(" and close ")" parenthesis.
    878 #
    879 # @param String    An PCD entry string need to be split.
    880 #
    881 # @return List     [PcdTokenCName, Value, FFE]
    882 #
    883 def SplitPcdEntry(String):
    884     if not String:
    885         return ['', '', ''], False
    886 
    887     PcdTokenCName = ''
    888     PcdValue = ''
    889     PcdFeatureFlagExp = ''
    890 
    891     ValueList = GetSplitValueList(String, "|", 1)
    892 
    893     #
    894     # Only contain TokenCName
    895     #
    896     if len(ValueList) == 1:
    897         return [ValueList[0]], True
    898 
    899     NewValueList = []
    900 
    901     if len(ValueList) == 2:
    902         PcdTokenCName = ValueList[0]
    903 
    904         InQuote = False
    905         InParenthesis = False
    906         StrItem = ''
    907         for StrCh in ValueList[1]:
    908             if StrCh == '"':
    909                 InQuote = not InQuote
    910             elif StrCh == '(' or StrCh == ')':
    911                 InParenthesis = not InParenthesis
    912 
    913             if StrCh == '|':
    914                 if not InQuote or not InParenthesis:
    915                     NewValueList.append(StrItem.strip())
    916                     StrItem = ' '
    917                     continue
    918 
    919             StrItem += StrCh
    920 
    921         NewValueList.append(StrItem.strip())
    922 
    923         if len(NewValueList) == 1:
    924             PcdValue = NewValueList[0]
    925             return [PcdTokenCName, PcdValue], True
    926         elif len(NewValueList) == 2:
    927             PcdValue = NewValueList[0]
    928             PcdFeatureFlagExp = NewValueList[1]
    929             return [PcdTokenCName, PcdValue, PcdFeatureFlagExp], True
    930         else:
    931             return ['', '', ''], False
    932 
    933     return ['', '', ''], False
    934 
    935 ## Check if two arches matched?
    936 #
    937 # @param Arch1
    938 # @param Arch2
    939 #
    940 def IsMatchArch(Arch1, Arch2):
    941     if 'COMMON' in Arch1 or 'COMMON' in Arch2:
    942         return True
    943     if isinstance(Arch1, basestring) and isinstance(Arch2, basestring):
    944         if Arch1 == Arch2:
    945             return True
    946 
    947     if isinstance(Arch1, basestring) and isinstance(Arch2, list):
    948         return Arch1 in Arch2
    949 
    950     if isinstance(Arch2, basestring) and isinstance(Arch1, list):
    951         return Arch2 in Arch1
    952 
    953     if isinstance(Arch1, list) and isinstance(Arch2, list):
    954         for Item1 in Arch1:
    955             for Item2 in Arch2:
    956                 if Item1 == Item2:
    957                     return True
    958 
    959     return False
    960 
    961 # Search all files in FilePath to find the FileName with the largest index
    962 # Return the FileName with index +1 under the FilePath
    963 #
    964 def GetUniFileName(FilePath, FileName):
    965     Files = []
    966     try:
    967         Files = os.listdir(FilePath)
    968     except:
    969         pass
    970 
    971     LargestIndex = -1
    972     for File in Files:
    973         if File.upper().startswith(FileName.upper()) and File.upper().endswith('.UNI'):
    974             Index = File.upper().replace(FileName.upper(), '').replace('.UNI', '')
    975             if Index:
    976                 try:
    977                     Index = int(Index)
    978                 except Exception:
    979                     Index = -1
    980             else:
    981                 Index = 0
    982             if Index > LargestIndex:
    983                 LargestIndex = Index + 1
    984 
    985     if LargestIndex > -1:
    986         return os.path.normpath(os.path.join(FilePath, FileName + str(LargestIndex) + '.uni'))
    987     else:
    988         return os.path.normpath(os.path.join(FilePath, FileName + '.uni'))
    989