]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/UPT/Library/String.py
Sync BaseTools Branch (version r2271) to EDKII main trunk.
[mirror_edk2.git] / BaseTools / Source / Python / UPT / Library / String.py
1 ## @file
2 # This file is used to define common string related functions used in parsing
3 # process
4 #
5 # Copyright (c) 2011, 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 ## RemoveBlockComment
647 #
648 # Remove comment block
649 #
650 # @param Lines: Block Comment Lines
651 #
652 def RemoveBlockComment(Lines):
653 IsFindBlockComment = False
654 ReservedLine = ''
655 NewLines = []
656
657 for Line in Lines:
658 Line = Line.strip()
659 #
660 # Remove comment block
661 #
662 if Line.find(DataType.TAB_COMMENT_EDK1_START) > -1:
663 ReservedLine = GetSplitValueList(Line, DataType.TAB_COMMENT_EDK1_START, 1)[0]
664 IsFindBlockComment = True
665 if Line.find(DataType.TAB_COMMENT_EDK1_END) > -1:
666 Line = ReservedLine + GetSplitValueList(Line, DataType.TAB_COMMENT_EDK1_END, 1)[1]
667 ReservedLine = ''
668 IsFindBlockComment = False
669 if IsFindBlockComment:
670 NewLines.append('')
671 continue
672 NewLines.append(Line)
673 return NewLines
674
675 ## GetStringOfList
676 #
677 # Get String of a List
678 #
679 # @param Lines: string list
680 # @param Split: split character
681 #
682 def GetStringOfList(List, Split = ' '):
683 if type(List) != type([]):
684 return List
685 Str = ''
686 for Item in List:
687 Str = Str + Item + Split
688 return Str.strip()
689
690 ## Get HelpTextList
691 #
692 # Get HelpTextList from HelpTextClassList
693 #
694 # @param HelpTextClassList: Help Text Class List
695 #
696 def GetHelpTextList(HelpTextClassList):
697 List = []
698 if HelpTextClassList:
699 for HelpText in HelpTextClassList:
700 if HelpText.String.endswith('\n'):
701 HelpText.String = HelpText.String[0: len(HelpText.String) - len('\n')]
702 List.extend(HelpText.String.split('\n'))
703 return List
704
705 ## Get String Array Length
706 #
707 # Get String Array Length
708 #
709 # @param String: the source string
710 #
711 def StringArrayLength(String):
712 if isinstance(String, unicode):
713 return (len(String) + 1) * 2 + 1
714 elif String.startswith('L"'):
715 return (len(String) - 3 + 1) * 2
716 elif String.startswith('"'):
717 return (len(String) - 2 + 1)
718 else:
719 return len(String.split()) + 1
720
721 ## RemoveDupOption
722 #
723 # Remove Dup Option
724 #
725 # @param OptionString: the option string
726 # @param Which: Which flag
727 # @param Against: Against flag
728 #
729 def RemoveDupOption(OptionString, Which="/I", Against=None):
730 OptionList = OptionString.split()
731 ValueList = []
732 if Against:
733 ValueList += Against
734 for Index in range(len(OptionList)):
735 Opt = OptionList[Index]
736 if not Opt.startswith(Which):
737 continue
738 if len(Opt) > len(Which):
739 Val = Opt[len(Which):]
740 else:
741 Val = ""
742 if Val in ValueList:
743 OptionList[Index] = ""
744 else:
745 ValueList.append(Val)
746 return " ".join(OptionList)
747
748 ## Check if the string is HexDgit
749 #
750 # Return true if all characters in the string are digits and there is at
751 # least one character
752 # or valid Hexs (started with 0x, following by hexdigit letters)
753 # , false otherwise.
754 # @param string: input string
755 #
756 def IsHexDigit(Str):
757 try:
758 int(Str, 10)
759 return True
760 except ValueError:
761 if len(Str) > 2 and Str.upper().startswith('0X'):
762 try:
763 int(Str, 16)
764 return True
765 except ValueError:
766 return False
767 return False
768
769 ## Check if the string is HexDgit and its interger value within limit of UINT32
770 #
771 # Return true if all characters in the string are digits and there is at
772 # least one character
773 # or valid Hexs (started with 0x, following by hexdigit letters)
774 # , false otherwise.
775 # @param string: input string
776 #
777 def IsHexDigitUINT32(Str):
778 try:
779 Value = int(Str, 10)
780 if (Value <= 0xFFFFFFFF) and (Value >= 0):
781 return True
782 except ValueError:
783 if len(Str) > 2 and Str.upper().startswith('0X'):
784 try:
785 Value = int(Str, 16)
786 if (Value <= 0xFFFFFFFF) and (Value >= 0):
787 return True
788 except ValueError:
789 return False
790 return False
791
792 ## CleanSpecialChar
793 #
794 # The ASCII text files of type INF, DEC, INI are edited by developers,
795 # and may contain characters that cannot be directly translated to strings that
796 # are conformant with the UDP XML Schema. Any characters in this category
797 # (0x00-0x08, TAB [0x09], 0x0B, 0x0C, 0x0E-0x1F, 0x80-0xFF)
798 # must be converted to a space character[0x20] as part of the parsing process.
799 #
800 def ConvertSpecialChar(Lines):
801 RetLines = []
802 for line in Lines:
803 ReMatchSpecialChar = re.compile(r"[\x00-\x08]|\x09|\x0b|\x0c|[\x0e-\x1f]|[\x7f-\xff]")
804 RetLines.append(ReMatchSpecialChar.sub(' ', line))
805
806 return RetLines
807
808 ## __GetTokenList
809 #
810 # Assume Str is a valid feature flag expression.
811 # Return a list which contains tokens: alpha numeric token and other token
812 # Whitespace are not stripped
813 #
814 def __GetTokenList(Str):
815 InQuote = False
816 Token = ''
817 TokenOP = ''
818 PreChar = ''
819 List = []
820 for Char in Str:
821 if InQuote:
822 Token += Char
823 if Char == '"' and PreChar != '\\':
824 InQuote = not InQuote
825 List.append(Token)
826 Token = ''
827 continue
828 if Char == '"':
829 if Token and Token != 'L':
830 List.append(Token)
831 Token = ''
832 if TokenOP:
833 List.append(TokenOP)
834 TokenOP = ''
835 InQuote = not InQuote
836 Token += Char
837 continue
838
839 if not (Char.isalnum() or Char in '_'):
840 TokenOP += Char
841 if Token:
842 List.append(Token)
843 Token = ''
844 else:
845 Token += Char
846 if TokenOP:
847 List.append(TokenOP)
848 TokenOP = ''
849
850 if PreChar == '\\' and Char == '\\':
851 PreChar = ''
852 else:
853 PreChar = Char
854 if Token:
855 List.append(Token)
856 if TokenOP:
857 List.append(TokenOP)
858 return List
859
860 ## ConvertNEToNOTEQ
861 #
862 # Convert NE operator to NOT EQ
863 # For example: 1 NE 2 -> 1 NOT EQ 2
864 #
865 # @param Expr: Feature flag expression to be converted
866 #
867 def ConvertNEToNOTEQ(Expr):
868 List = __GetTokenList(Expr)
869 for Index in range(len(List)):
870 if List[Index] == 'NE':
871 List[Index] = 'NOT EQ'
872 return ''.join(List)
873
874 ## ConvertNOTEQToNE
875 #
876 # Convert NOT EQ operator to NE
877 # For example: 1 NOT NE 2 -> 1 NE 2
878 #
879 # @param Expr: Feature flag expression to be converted
880 #
881 def ConvertNOTEQToNE(Expr):
882 List = __GetTokenList(Expr)
883 HasNOT = False
884 RetList = []
885 for Token in List:
886 if HasNOT and Token == 'EQ':
887 # At least, 'NOT' is in the list
888 while not RetList[-1].strip():
889 RetList.pop()
890 RetList[-1] = 'NE'
891 HasNOT = False
892 continue
893 if Token == 'NOT':
894 HasNOT = True
895 elif Token.strip():
896 HasNOT = False
897 RetList.append(Token)
898
899 return ''.join(RetList)
900
901 ## SplitPcdEntry
902 #
903 # Split an PCD entry string to Token.CName and PCD value and FFE.
904 # NOTE: PCD Value and FFE can contain "|" in it's expression. And in INF specification, have below rule.
905 # When using the characters "|" or "||" in an expression, the expression must be encapsulated in
906 # open "(" and close ")" parenthesis.
907 #
908 # @param String An PCD entry string need to be split.
909 #
910 # @return List [PcdTokenCName, Value, FFE]
911 #
912 def SplitPcdEntry(String):
913 if not String:
914 return ['', '',''], False
915
916 PcdTokenCName = ''
917 PcdValue = ''
918 PcdFeatureFlagExp = ''
919
920 ValueList = GetSplitValueList(String, "|", 1)
921
922 #
923 # Only contain TokenCName
924 #
925 if len(ValueList) == 1:
926 return [ValueList[0]], True
927
928 NewValueList = []
929
930 if len(ValueList) == 2:
931 PcdTokenCName = ValueList[0]
932 ValueList = GetSplitValueList(ValueList[1], "|")
933
934 RemainCount = 0
935 for Item in ValueList:
936 ParenthesisCount = 0
937 for Char in Item:
938 if Char == "(":
939 ParenthesisCount += 1
940 if Char == ")":
941 ParenthesisCount -= 1
942
943 #
944 # An individual item
945 #
946 if RemainCount == 0 and ParenthesisCount >= 0:
947 NewValueList.append(Item)
948 RemainCount = ParenthesisCount
949 elif RemainCount > 0 and RemainCount + ParenthesisCount >= 0:
950 NewValueList[-1] = NewValueList[-1] + '|' + Item
951 RemainCount = RemainCount + ParenthesisCount
952 elif RemainCount > 0 and RemainCount + ParenthesisCount < 0:
953 #
954 # ERROR, return
955 #
956 return ['', '', ''], False
957
958 if len(NewValueList) == 1:
959 PcdValue = NewValueList[0]
960 return [PcdTokenCName, PcdValue], True
961 elif len(NewValueList) == 2:
962 PcdValue = NewValueList[0]
963 PcdFeatureFlagExp = NewValueList[1]
964 return [PcdTokenCName, PcdValue, PcdFeatureFlagExp], True
965 else:
966 return ['', '', ''], False
967
968 return ['', '', ''], False