]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/UPT/Library/StringUtils.py
BaseTools: Fix old python2 idioms
[mirror_edk2.git] / BaseTools / Source / Python / UPT / Library / StringUtils.py
1 ## @file
2 # This file is used to define common string related functions used in parsing
3 # process
4 #
5 # Copyright (c) 2011 - 2018, 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 StringUtils
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 is 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 is 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 is not 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 is not 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 not isinstance(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 IndexNotFound = True
973 for File in Files:
974 if File.upper().startswith(FileName.upper()) and File.upper().endswith('.UNI'):
975 Index = File.upper().replace(FileName.upper(), '').replace('.UNI', '')
976 if Index:
977 try:
978 Index = int(Index)
979 except Exception:
980 Index = -1
981 else:
982 IndexNotFound = False
983 Index = 0
984 if Index > LargestIndex:
985 LargestIndex = Index + 1
986
987 if LargestIndex > -1 and not IndexNotFound:
988 return os.path.normpath(os.path.join(FilePath, FileName + str(LargestIndex) + '.uni'))
989 else:
990 return os.path.normpath(os.path.join(FilePath, FileName + '.uni'))