Sync EDKII BaseTools to BaseTools project r1903.
[efi/edk2/.git] / edk2 / BaseTools / Source / Python / Common / Misc.py
1 ## @file
2 # Common routines used by all tools
3 #
4 # Copyright (c) 2007, Intel Corporation
5 # All rights reserved. This program and the accompanying materials
6 # are licensed and made available under the terms and conditions of the BSD License
7 # which accompanies this distribution.  The full text of the license may be found at
8 # http://opensource.org/licenses/bsd-license.php
9 #
10 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 #
13
14 ##
15 # Import Modules
16 #
17 import os
18 import sys
19 import string
20 import thread
21 import threading
22 import time
23 import re
24 import cPickle
25 import array
26 from UserDict import IterableUserDict
27 from UserList import UserList
28
29 from Common import EdkLogger as EdkLogger
30 from Common import GlobalData as GlobalData
31
32 from BuildToolError import *
33
34 ## Regular expression used to find out place holders in string template
35 gPlaceholderPattern = re.compile("\$\{([^$()\s]+)\}", re.MULTILINE|re.UNICODE)
36
37 ## Dictionary used to store file time stamp for quick re-access
38 gFileTimeStampCache = {}    # {file path : file time stamp}
39
40 ## Dictionary used to store dependencies of files
41 gDependencyDatabase = {}    # arch : {file path : [dependent files list]}
42
43 ## callback routine for processing variable option
44 #
45 # This function can be used to process variable number of option values. The
46 # typical usage of it is specify architecure list on command line.
47 # (e.g. <tool> -a IA32 X64 IPF)
48 #
49 # @param  Option        Standard callback function parameter
50 # @param  OptionString  Standard callback function parameter
51 # @param  Value         Standard callback function parameter
52 # @param  Parser        Standard callback function parameter
53 #
54 # @retval
55 #
56 def ProcessVariableArgument(Option, OptionString, Value, Parser):
57     assert Value is None
58     Value = []
59     RawArgs = Parser.rargs
60     while RawArgs:
61         Arg = RawArgs[0]
62         if (Arg[:2] == "--" and len(Arg) > 2) or \
63            (Arg[:1] == "-" and len(Arg) > 1 and Arg[1] != "-"):
64             break
65         Value.append(Arg)
66         del RawArgs[0]
67     setattr(Parser.values, Option.dest, Value)
68
69 ## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C structure style
70 #
71 #   @param      Guid    The GUID string
72 #
73 #   @retval     string  The GUID string in C structure style
74 #
75 def GuidStringToGuidStructureString(Guid):
76     GuidList = Guid.split('-')
77     Result = '{'
78     for Index in range(0,3,1):
79         Result = Result + '0x' + GuidList[Index] + ', '
80     Result = Result + '{0x' + GuidList[3][0:2] + ', 0x' + GuidList[3][2:4]
81     for Index in range(0,12,2):
82         Result = Result + ', 0x' + GuidList[4][Index:Index+2]
83     Result += '}}'
84     return Result
85
86 ## Convert GUID structure in byte array to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
87 #
88 #   @param      GuidValue   The GUID value in byte array
89 #
90 #   @retval     string      The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
91 #
92 def GuidStructureByteArrayToGuidString(GuidValue):
93     guidValueString = GuidValue.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
94     guidValueList = guidValueString.split(",")
95     if len(guidValueList) != 16:
96         return ''
97         #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
98     try:
99         return "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
100                 int(guidValueList[3], 16),
101                 int(guidValueList[2], 16),
102                 int(guidValueList[1], 16),
103                 int(guidValueList[0], 16),
104                 int(guidValueList[5], 16),
105                 int(guidValueList[4], 16),
106                 int(guidValueList[7], 16),
107                 int(guidValueList[6], 16),
108                 int(guidValueList[8], 16),
109                 int(guidValueList[9], 16),
110                 int(guidValueList[10], 16),
111                 int(guidValueList[11], 16),
112                 int(guidValueList[12], 16),
113                 int(guidValueList[13], 16),
114                 int(guidValueList[14], 16),
115                 int(guidValueList[15], 16)
116                 )
117     except:
118         return ''
119
120 ## Convert GUID string in C structure style to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
121 #
122 #   @param      GuidValue   The GUID value in C structure format
123 #
124 #   @retval     string      The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
125 #
126 def GuidStructureStringToGuidString(GuidValue):
127     guidValueString = GuidValue.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
128     guidValueList = guidValueString.split(",")
129     if len(guidValueList) != 11:
130         return ''
131         #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
132     try:
133         return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
134                 int(guidValueList[0], 16),
135                 int(guidValueList[1], 16),
136                 int(guidValueList[2], 16),
137                 int(guidValueList[3], 16),
138                 int(guidValueList[4], 16),
139                 int(guidValueList[5], 16),
140                 int(guidValueList[6], 16),
141                 int(guidValueList[7], 16),
142                 int(guidValueList[8], 16),
143                 int(guidValueList[9], 16),
144                 int(guidValueList[10], 16)
145                 )
146     except:
147         return ''
148
149 ## Convert GUID string in C structure style to xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
150 #
151 #   @param      GuidValue   The GUID value in C structure format
152 #
153 #   @retval     string      The GUID value in xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx format
154 #
155 def GuidStructureStringToGuidValueName(GuidValue):
156     guidValueString = GuidValue.lower().replace("{", "").replace("}", "").replace(" ", "")
157     guidValueList = guidValueString.split(",")
158     if len(guidValueList) != 11:
159         EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
160     return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (
161             int(guidValueList[0], 16),
162             int(guidValueList[1], 16),
163             int(guidValueList[2], 16),
164             int(guidValueList[3], 16),
165             int(guidValueList[4], 16),
166             int(guidValueList[5], 16),
167             int(guidValueList[6], 16),
168             int(guidValueList[7], 16),
169             int(guidValueList[8], 16),
170             int(guidValueList[9], 16),
171             int(guidValueList[10], 16)
172             )
173
174 ## Create directories
175 #
176 #   @param      Directory   The directory name
177 #
178 def CreateDirectory(Directory):
179     if Directory == None or Directory.strip() == "":
180         return True
181     try:
182         if not os.access(Directory, os.F_OK):
183             os.makedirs(Directory)
184     except:
185         return False
186     return True
187
188 ## Remove directories, including files and sub-directories in it
189 #
190 #   @param      Directory   The directory name
191 #
192 def RemoveDirectory(Directory, Recursively=False):
193     if Directory == None or Directory.strip() == "" or not os.path.exists(Directory):
194         return
195     if Recursively:
196         CurrentDirectory = os.getcwd()
197         os.chdir(Directory)
198         for File in os.listdir("."):
199             if os.path.isdir(File):
200                 RemoveDirectory(File, Recursively)
201             else:
202                 os.remove(File)
203         os.chdir(CurrentDirectory)
204     os.rmdir(Directory)
205
206 ## Check if given file is changed or not
207 #
208 #  This method is used to check if a file is changed or not between two build
209 #  actions. It makes use a cache to store files timestamp.
210 #
211 #   @param      File    The path of file
212 #
213 #   @retval     True    If the given file is changed, doesn't exist, or can't be
214 #                       found in timestamp cache
215 #   @retval     False   If the given file is changed
216 #
217 def IsChanged(File):
218     if not os.path.exists(File):
219         return True
220
221     FileState = os.stat(File)
222     TimeStamp = FileState[-2]
223
224     if File in gFileTimeStampCache and TimeStamp == gFileTimeStampCache[File]:
225         FileChanged = False
226     else:
227         FileChanged = True
228         gFileTimeStampCache[File] = TimeStamp
229
230     return FileChanged
231
232 ## Store content in file
233 #
234 #  This method is used to save file only when its content is changed. This is
235 #  quite useful for "make" system to decide what will be re-built and what won't.
236 #
237 #   @param      File            The path of file
238 #   @param      Content         The new content of the file
239 #   @param      IsBinaryFile    The flag indicating if the file is binary file or not
240 #
241 #   @retval     True            If the file content is changed and the file is renewed
242 #   @retval     False           If the file content is the same
243 #
244 def SaveFileOnChange(File, Content, IsBinaryFile=True):
245     if not IsBinaryFile:
246         Content = Content.replace("\n", os.linesep)
247
248     if os.path.exists(File):
249         try:
250             if Content == open(File, "rb").read():
251                 return False
252         except:
253             EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=File)
254
255     CreateDirectory(os.path.dirname(File))
256     try:
257         if GlobalData.gIsWindows:
258             try:
259                 from PyUtility import SaveFileToDisk
260                 if not SaveFileToDisk(File, Content):
261                     EdkLogger.error(None, FILE_CREATE_FAILURE, ExtraData=File)
262             except:
263                 Fd = open(File, "wb")
264                 Fd.write(Content)
265                 Fd.close()
266         else:
267             Fd = open(File, "wb")
268             Fd.write(Content)
269             Fd.close()
270     except:
271         EdkLogger.error(None, FILE_CREATE_FAILURE, ExtraData=File)
272
273     return True
274
275 ## Make a Python object persistent on file system
276 #
277 #   @param      Data    The object to be stored in file
278 #   @param      File    The path of file to store the object
279 #
280 def DataDump(Data, File):
281     Fd = None
282     try:
283         Fd = open(File, 'wb')
284         cPickle.dump(Data, Fd, cPickle.HIGHEST_PROTOCOL)
285     except:
286         EdkLogger.error("", FILE_OPEN_FAILURE, ExtraData=File, RaiseError=False)
287     finally:
288         if Fd != None:
289             Fd.close()
290
291 ## Restore a Python object from a file
292 #
293 #   @param      File    The path of file stored the object
294 #
295 #   @retval     object  A python object
296 #   @retval     None    If failure in file operation
297 #
298 def DataRestore(File):
299     Data = None
300     Fd = None
301     try:
302         Fd = open(File, 'rb')
303         Data = cPickle.load(Fd)
304     except Exception, e:
305         EdkLogger.verbose("Failed to load [%s]\n\t%s" % (File, str(e)))
306         Data = None
307     finally:
308         if Fd != None:
309             Fd.close()
310     return Data
311
312 ## Retrieve and cache the real path name in file system
313 #
314 #   @param      Root    The root directory of path relative to
315 #
316 #   @retval     str     The path string if the path exists
317 #   @retval     None    If path doesn't exist
318 #
319 class DirCache:
320     _CACHE_ = set()
321     _UPPER_CACHE_ = {}
322
323     def __init__(self, Root):
324         self._Root = Root
325         for F in os.listdir(Root):
326             self._CACHE_.add(F)
327             self._UPPER_CACHE_[F.upper()] = F
328
329     # =[] operator
330     def __getitem__(self, Path):
331         Path = Path[len(os.path.commonprefix([Path, self._Root])):]
332         if not Path:
333             return self._Root
334         if Path and Path[0] == os.path.sep:
335             Path = Path[1:]
336         if Path in self._CACHE_:
337             return os.path.join(self._Root, Path)
338         UpperPath = Path.upper()
339         if UpperPath in self._UPPER_CACHE_:
340             return os.path.join(self._Root, self._UPPER_CACHE_[UpperPath])
341
342         IndexList = []
343         LastSepIndex = -1
344         SepIndex = Path.find(os.path.sep)
345         while SepIndex > -1:
346             Parent = UpperPath[:SepIndex]
347             if Parent not in self._UPPER_CACHE_:
348                 break
349             LastSepIndex = SepIndex
350             SepIndex = Path.find(os.path.sep, LastSepIndex + 1)
351
352         if LastSepIndex == -1:
353             return None
354
355         Cwd = os.getcwd()
356         os.chdir(self._Root)
357         SepIndex = LastSepIndex
358         while SepIndex > -1:
359             Parent = Path[:SepIndex]
360             ParentKey = UpperPath[:SepIndex]
361             if ParentKey not in self._UPPER_CACHE_:
362                 os.chdir(Cwd)
363                 return None
364
365             if Parent in self._CACHE_:
366                 ParentDir = Parent
367             else:
368                 ParentDir = self._UPPER_CACHE_[ParentKey]
369             for F in os.listdir(ParentDir):
370                 Dir = os.path.join(ParentDir, F)
371                 self._CACHE_.add(Dir)
372                 self._UPPER_CACHE_[Dir.upper()] = Dir
373
374             SepIndex = Path.find(os.path.sep, SepIndex + 1)
375
376         os.chdir(Cwd)
377         if Path in self._CACHE_:
378             return os.path.join(self._Root, Path)
379         elif UpperPath in self._UPPER_CACHE_:
380             return os.path.join(self._Root, self._UPPER_CACHE_[UpperPath])
381         return None
382
383 ## Get all files of a directory
384 #
385 # @param Root:       Root dir
386 # @param SkipList :  The files need be skipped
387 #
388 # @retval  A list of all files
389 #
390 def GetFiles(Root, SkipList=None, FullPath = True):
391     OriPath = Root
392     FileList = []
393     for Root, Dirs, Files in os.walk(Root):
394         if SkipList:
395             for Item in SkipList:
396                 if Item in Dirs:
397                     Dirs.remove(Item)
398
399         for File in Files:
400             File = os.path.normpath(os.path.join(Root, File))
401             if not FullPath:
402                 File = File[len(OriPath) + 1:]
403             FileList.append(File)
404
405     return FileList
406
407 ## Check if gvien file exists or not
408 #
409 #   @param      File    File name or path to be checked
410 #   @param      Dir     The directory the file is relative to
411 #
412 #   @retval     True    if file exists
413 #   @retval     False   if file doesn't exists
414 #
415 def ValidFile(File, Ext=None):
416     if Ext != None:
417         Dummy, FileExt = os.path.splitext(File)
418         if FileExt.lower() != Ext.lower():
419             return False
420     if not os.path.exists(File):
421         return False
422     return True
423
424 def RealPath(File, Dir='', OverrideDir=''):
425     NewFile = os.path.normpath(os.path.join(Dir, File))
426     NewFile = GlobalData.gAllFiles[NewFile]
427     if not NewFile and OverrideDir:
428         NewFile = os.path.normpath(os.path.join(OverrideDir, File))
429         NewFile = GlobalData.gAllFiles[NewFile]
430     return NewFile
431
432 def RealPath2(File, Dir='', OverrideDir=''):
433     if OverrideDir:
434         NewFile = GlobalData.gAllFiles[os.path.normpath(os.path.join(OverrideDir, File))]
435         if NewFile:
436             if OverrideDir[-1] == os.path.sep:
437                 return NewFile[len(OverrideDir):], NewFile[0:len(OverrideDir)]
438             else:
439                 return NewFile[len(OverrideDir)+1:], NewFile[0:len(OverrideDir)]
440
441     NewFile = GlobalData.gAllFiles[os.path.normpath(os.path.join(Dir, File))]
442     if NewFile:
443         if Dir:
444             if Dir[-1] == os.path.sep:
445                 return NewFile[len(Dir):], NewFile[0:len(Dir)]
446             else:
447                 return NewFile[len(Dir)+1:], NewFile[0:len(Dir)]
448         else:
449             return NewFile, ''
450
451     return None, None
452
453 ## Check if gvien file exists or not
454 #
455 #
456 def ValidFile2(AllFiles, File, Ext=None, Workspace='', EfiSource='', EdkSource='', Dir='.', OverrideDir=''):
457     NewFile = File
458     if Ext != None:
459         Dummy, FileExt = os.path.splitext(File)
460         if FileExt.lower() != Ext.lower():
461             return False, File
462
463     # Replace the R8 macros
464     if OverrideDir != '' and OverrideDir != None:
465         if OverrideDir.find('$(EFI_SOURCE)') > -1:
466             OverrideDir = OverrideDir.replace('$(EFI_SOURCE)', EfiSource)
467         if OverrideDir.find('$(EDK_SOURCE)') > -1:
468             OverrideDir = OverrideDir.replace('$(EDK_SOURCE)', EdkSource)
469
470     # Replace the default dir to current dir
471     if Dir == '.':
472         Dir = os.getcwd()
473         Dir = Dir[len(Workspace)+1:]
474
475     # First check if File has R8 definition itself
476     if File.find('$(EFI_SOURCE)') > -1 or File.find('$(EDK_SOURCE)') > -1:
477         NewFile = File.replace('$(EFI_SOURCE)', EfiSource)
478         NewFile = NewFile.replace('$(EDK_SOURCE)', EdkSource)
479         NewFile = AllFiles[os.path.normpath(NewFile)]
480         if NewFile != None:
481             return True, NewFile
482
483     # Second check the path with override value
484     if OverrideDir != '' and OverrideDir != None:
485         NewFile = AllFiles[os.path.normpath(os.path.join(OverrideDir, File))]
486         if NewFile != None:
487             return True, NewFile
488
489     # Last check the path with normal definitions
490     File = os.path.join(Dir, File)
491     NewFile = AllFiles[os.path.normpath(File)]
492     if NewFile != None:
493         return True, NewFile
494
495     return False, File
496
497 ## Check if gvien file exists or not
498 #
499 #
500 def ValidFile3(AllFiles, File, Workspace='', EfiSource='', EdkSource='', Dir='.', OverrideDir=''):
501     # Replace the R8 macros
502     if OverrideDir != '' and OverrideDir != None:
503         if OverrideDir.find('$(EFI_SOURCE)') > -1:
504             OverrideDir = OverrideDir.replace('$(EFI_SOURCE)', EfiSource)
505         if OverrideDir.find('$(EDK_SOURCE)') > -1:
506             OverrideDir = OverrideDir.replace('$(EDK_SOURCE)', EdkSource)
507
508     # Replace the default dir to current dir
509     # Dir is current module dir related to workspace
510     if Dir == '.':
511         Dir = os.getcwd()
512         Dir = Dir[len(Workspace)+1:]
513
514     NewFile = File
515     RelaPath = AllFiles[os.path.normpath(Dir)]
516     NewRelaPath = RelaPath
517
518     while(True):
519         # First check if File has R8 definition itself
520         if File.find('$(EFI_SOURCE)') > -1 or File.find('$(EDK_SOURCE)') > -1:
521             File = File.replace('$(EFI_SOURCE)', EfiSource)
522             File = File.replace('$(EDK_SOURCE)', EdkSource)
523             NewFile = AllFiles[os.path.normpath(File)]
524             if NewFile != None:
525                 NewRelaPath = os.path.dirname(NewFile)
526                 File = os.path.basename(NewFile)
527                 #NewRelaPath = NewFile[:len(NewFile) - len(File.replace("..\\", '').replace("../", '')) - 1]
528                 break
529
530         # Second check the path with override value
531         if OverrideDir != '' and OverrideDir != None:
532             NewFile = AllFiles[os.path.normpath(os.path.join(OverrideDir, File))]
533             if NewFile != None:
534                 #NewRelaPath = os.path.dirname(NewFile)
535                 NewRelaPath = NewFile[:len(NewFile) - len(File.replace("..\\", '').replace("../", '')) - 1]
536                 break
537
538         # Last check the path with normal definitions
539         NewFile = AllFiles[os.path.normpath(os.path.join(Dir, File))]
540         if NewFile != None:
541             break
542
543         # No file found
544         break
545
546     return NewRelaPath, RelaPath, File
547
548
549 def GetRelPath(Path1, Path2):
550     FileName = os.path.basename(Path2)
551     L1 = os.path.normpath(Path1).split(os.path.normpath('/'))
552     L2 = os.path.normpath(Path2).split(os.path.normpath('/'))
553     for Index in range(0, len(L1)):
554         if L1[Index] != L2[Index]:
555             FileName = '../' * (len(L1) - Index)
556             for Index2 in range(Index, len(L2)):
557                 FileName = os.path.join(FileName, L2[Index2])
558             break
559     return os.path.normpath(FileName)
560
561
562 ## Get GUID value from given packages
563 #
564 #   @param      CName           The CName of the GUID
565 #   @param      PackageList     List of packages looking-up in
566 #
567 #   @retval     GuidValue   if the CName is found in any given package
568 #   @retval     None        if the CName is not found in all given packages
569 #
570 def GuidValue(CName, PackageList):
571     for P in PackageList:
572         if CName in P.Guids:
573             return P.Guids[CName]
574     return None
575
576 ## Get Protocol value from given packages
577 #
578 #   @param      CName           The CName of the GUID
579 #   @param      PackageList     List of packages looking-up in
580 #
581 #   @retval     GuidValue   if the CName is found in any given package
582 #   @retval     None        if the CName is not found in all given packages
583 #
584 def ProtocolValue(CName, PackageList):
585     for P in PackageList:
586         if CName in P.Protocols:
587             return P.Protocols[CName]
588     return None
589
590 ## Get PPI value from given packages
591 #
592 #   @param      CName           The CName of the GUID
593 #   @param      PackageList     List of packages looking-up in
594 #
595 #   @retval     GuidValue   if the CName is found in any given package
596 #   @retval     None        if the CName is not found in all given packages
597 #
598 def PpiValue(CName, PackageList):
599     for P in PackageList:
600         if CName in P.Ppis:
601             return P.Ppis[CName]
602     return None
603
604 ## A string template class
605 #
606 #  This class implements a template for string replacement. A string template
607 #  looks like following
608 #
609 #       ${BEGIN} other_string ${placeholder_name} other_string ${END}
610 #
611 #  The string between ${BEGIN} and ${END} will be repeated as many times as the
612 #  length of "placeholder_name", which is a list passed through a dict. The
613 #  "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can
614 #  be not used and, in this case, the "placeholder_name" must not a list and it
615 #  will just be replaced once.
616 #
617 class TemplateString(object):
618     _REPEAT_START_FLAG = "BEGIN"
619     _REPEAT_END_FLAG = "END"
620
621     class Section(object):
622         _LIST_TYPES = [type([]), type(set()), type((0,))]
623
624         def __init__(self, TemplateSection, PlaceHolderList):
625             self._Template = TemplateSection
626             self._PlaceHolderList = []
627
628             # Split the section into sub-sections according to the position of placeholders
629             if PlaceHolderList:
630                 self._SubSectionList = []
631                 SubSectionStart = 0
632                 #
633                 # The placeholders passed in must be in the format of
634                 #
635                 #   PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint
636                 #
637                 for PlaceHolder,Start,End in PlaceHolderList:
638                     self._SubSectionList.append(TemplateSection[SubSectionStart:Start])
639                     self._SubSectionList.append(TemplateSection[Start:End])
640                     self._PlaceHolderList.append(PlaceHolder)
641                     SubSectionStart = End
642                 if SubSectionStart < len(TemplateSection):
643                     self._SubSectionList.append(TemplateSection[SubSectionStart:])
644             else:
645                 self._SubSectionList = [TemplateSection]
646
647         def __str__(self):
648             return self._Template + " : " + str(self._PlaceHolderList)
649
650         def Instantiate(self, PlaceHolderValues):
651             RepeatTime = -1
652             RepeatPlaceHolders = {}
653             NonRepeatPlaceHolders = {}
654
655             for PlaceHolder in self._PlaceHolderList:
656                 if PlaceHolder not in PlaceHolderValues:
657                     continue
658                 Value = PlaceHolderValues[PlaceHolder]
659                 if type(Value) in self._LIST_TYPES:
660                     if RepeatTime < 0:
661                         RepeatTime = len(Value)
662                     elif RepeatTime != len(Value):
663                         EdkLogger.error(
664                                     "TemplateString",
665                                     PARAMETER_INVALID,
666                                     "${%s} has different repeat time from others!" % PlaceHolder,
667                                     ExtraData=str(self._Template)
668                                     )
669                     RepeatPlaceHolders["${%s}" % PlaceHolder] = Value
670                 else:
671                     NonRepeatPlaceHolders["${%s}" % PlaceHolder] = Value
672
673             if NonRepeatPlaceHolders:
674                 StringList = []
675                 for S in self._SubSectionList:
676                     if S not in NonRepeatPlaceHolders:
677                         StringList.append(S)
678                     else:
679                         StringList.append(str(NonRepeatPlaceHolders[S]))
680             else:
681                 StringList = self._SubSectionList
682
683             if RepeatPlaceHolders:
684                 TempStringList = []
685                 for Index in range(RepeatTime):
686                     for S in StringList:
687                         if S not in RepeatPlaceHolders:
688                             TempStringList.append(S)
689                         else:
690                             TempStringList.append(str(RepeatPlaceHolders[S][Index]))
691                 StringList = TempStringList
692
693             return "".join(StringList)
694
695     ## Constructor
696     def __init__(self, Template=None):
697         self.String = ''
698         self.IsBinary = False
699         self._Template = Template
700         self._TemplateSectionList = self._Parse(Template)
701
702     ## str() operator
703     #
704     #   @retval     string  The string replaced
705     #
706     def __str__(self):
707         return self.String
708
709     ## Split the template string into fragments per the ${BEGIN} and ${END} flags
710     #
711     #   @retval     list    A list of TemplateString.Section objects
712     #
713     def _Parse(self, Template):
714         SectionStart = 0
715         SearchFrom = 0
716         MatchEnd = 0
717         PlaceHolderList = []
718         TemplateSectionList = []
719         while Template:
720             MatchObj = gPlaceholderPattern.search(Template, SearchFrom)
721             if not MatchObj:
722                 if MatchEnd < len(Template):
723                     TemplateSection = TemplateString.Section(Template[SectionStart:], PlaceHolderList)
724                     TemplateSectionList.append(TemplateSection)
725                 break
726
727             MatchString = MatchObj.group(1)
728             MatchStart = MatchObj.start()
729             MatchEnd = MatchObj.end()
730
731             if MatchString == self._REPEAT_START_FLAG:
732                 if MatchStart > SectionStart:
733                     TemplateSection = TemplateString.Section(Template[SectionStart:MatchStart], PlaceHolderList)
734                     TemplateSectionList.append(TemplateSection)
735                 SectionStart = MatchEnd
736                 PlaceHolderList = []
737             elif MatchString == self._REPEAT_END_FLAG:
738                 TemplateSection = TemplateString.Section(Template[SectionStart:MatchStart], PlaceHolderList)
739                 TemplateSectionList.append(TemplateSection)
740                 SectionStart = MatchEnd
741                 PlaceHolderList = []
742             else:
743                 PlaceHolderList.append((MatchString, MatchStart - SectionStart, MatchEnd - SectionStart))
744             SearchFrom = MatchEnd
745         return TemplateSectionList
746
747     ## Replace the string template with dictionary of placeholders and append it to previous one
748     #
749     #   @param      AppendString    The string template to append
750     #   @param      Dictionary      The placeholder dictionaries
751     #
752     def Append(self, AppendString, Dictionary=None):
753         if Dictionary:
754             SectionList = self._Parse(AppendString)
755             self.String += "".join([S.Instantiate(Dictionary) for S in SectionList])
756         else:
757             self.String += AppendString
758
759     ## Replace the string template with dictionary of placeholders
760     #
761     #   @param      Dictionary      The placeholder dictionaries
762     #
763     #   @retval     str             The string replaced with placeholder values
764     #
765     def Replace(self, Dictionary=None):
766         return "".join([S.Instantiate(Dictionary) for S in self._TemplateSectionList])
767
768 ## Progress indicator class
769 #
770 #  This class makes use of thread to print progress on console.
771 #
772 class Progressor:
773     # for avoiding deadloop
774     _StopFlag = None
775     _ProgressThread = None
776     _CheckInterval = 0.25
777
778     ## Constructor
779     #
780     #   @param      OpenMessage     The string printed before progress charaters
781     #   @param      CloseMessage    The string printed after progress charaters
782     #   @param      ProgressChar    The charater used to indicate the progress
783     #   @param      Interval        The interval in seconds between two progress charaters
784     #
785     def __init__(self, OpenMessage="", CloseMessage="", ProgressChar='.', Interval=1.0):
786         self.PromptMessage = OpenMessage
787         self.CodaMessage = CloseMessage
788         self.ProgressChar = ProgressChar
789         self.Interval = Interval
790         if Progressor._StopFlag == None:
791             Progressor._StopFlag = threading.Event()
792
793     ## Start to print progress charater
794     #
795     #   @param      OpenMessage     The string printed before progress charaters
796     #
797     def Start(self, OpenMessage=None):
798         if OpenMessage != None:
799             self.PromptMessage = OpenMessage
800         Progressor._StopFlag.clear()
801         if Progressor._ProgressThread == None:
802             Progressor._ProgressThread = threading.Thread(target=self._ProgressThreadEntry)
803             Progressor._ProgressThread.setDaemon(False)
804             Progressor._ProgressThread.start()
805
806     ## Stop printing progress charater
807     #
808     #   @param      CloseMessage    The string printed after progress charaters
809     #
810     def Stop(self, CloseMessage=None):
811         OriginalCodaMessage = self.CodaMessage
812         if CloseMessage != None:
813             self.CodaMessage = CloseMessage
814         self.Abort()
815         self.CodaMessage = OriginalCodaMessage
816
817     ## Thread entry method
818     def _ProgressThreadEntry(self):
819         sys.stdout.write(self.PromptMessage + " ")
820         sys.stdout.flush()
821         TimeUp = 0.0
822         while not Progressor._StopFlag.isSet():
823             if TimeUp <= 0.0:
824                 sys.stdout.write(self.ProgressChar)
825                 sys.stdout.flush()
826                 TimeUp = self.Interval
827             time.sleep(self._CheckInterval)
828             TimeUp -= self._CheckInterval
829         sys.stdout.write(" " + self.CodaMessage + "\n")
830         sys.stdout.flush()
831
832     ## Abort the progress display
833     @staticmethod
834     def Abort():
835         if Progressor._StopFlag != None:
836             Progressor._StopFlag.set()
837         if Progressor._ProgressThread != None:
838             Progressor._ProgressThread.join()
839             Progressor._ProgressThread = None
840
841 ## A dict which can access its keys and/or values orderly
842 #
843 #  The class implements a new kind of dict which its keys or values can be
844 #  accessed in the order they are added into the dict. It guarantees the order
845 #  by making use of an internal list to keep a copy of keys.
846 #
847 class sdict(IterableUserDict):
848     ## Constructor
849     def __init__(self):
850         IterableUserDict.__init__(self)
851         self._key_list = []
852
853     ## [] operator
854     def __setitem__(self, key, value):
855         if key not in self._key_list:
856             self._key_list.append(key)
857         IterableUserDict.__setitem__(self, key, value)
858
859     ## del operator
860     def __delitem__(self, key):
861         self._key_list.remove(key)
862         IterableUserDict.__delitem__(self, key)
863
864     ## used in "for k in dict" loop to ensure the correct order
865     def __iter__(self):
866         return self.iterkeys()
867
868     ## len() support
869     def __len__(self):
870         return len(self._key_list)
871
872     ## "in" test support
873     def __contains__(self, key):
874         return key in self._key_list
875
876     ## indexof support
877     def index(self, key):
878         return self._key_list.index(key)
879
880     ## insert support
881     def insert(self, key, newkey, newvalue, order):
882         index = self._key_list.index(key)
883         if order == 'BEFORE':
884             self._key_list.insert(index, newkey)
885             IterableUserDict.__setitem__(self, newkey, newvalue)
886         elif order == 'AFTER':
887             self._key_list.insert(index + 1, newkey)
888             IterableUserDict.__setitem__(self, newkey, newvalue)
889
890     ## append support
891     def append(self, sdict):
892         for key in sdict:
893             if key not in self._key_list:
894                 self._key_list.append(key)
895             IterableUserDict.__setitem__(self, key, sdict[key])
896
897     def has_key(self, key):
898         return key in self._key_list
899
900     ## Empty the dict
901     def clear(self):
902         self._key_list = []
903         IterableUserDict.clear(self)
904
905     ## Return a copy of keys
906     def keys(self):
907         keys = []
908         for key in self._key_list:
909             keys.append(key)
910         return keys
911
912     ## Return a copy of values
913     def values(self):
914         values = []
915         for key in self._key_list:
916             values.append(self[key])
917         return values
918
919     ## Return a copy of (key, value) list
920     def items(self):
921         items = []
922         for key in self._key_list:
923             items.append((key, self[key]))
924         return items
925
926     ## Iteration support
927     def iteritems(self):
928         return iter(self.items())
929
930     ## Keys interation support
931     def iterkeys(self):
932         return iter(self.keys())
933
934     ## Values interation support
935     def itervalues(self):
936         return iter(self.values())
937
938     ## Return value related to a key, and remove the (key, value) from the dict
939     def pop(self, key, *dv):
940         value = None
941         if key in self._key_list:
942             value = self[key]
943             self.__delitem__(key)
944         elif len(dv) != 0 :
945             value = kv[0]
946         return value
947
948     ## Return (key, value) pair, and remove the (key, value) from the dict
949     def popitem(self):
950         key = self._key_list[-1]
951         value = self[key]
952         self.__delitem__(key)
953         return key, value
954
955     def update(self, dict=None, **kwargs):
956         if dict != None:
957             for k, v in dict.items():
958                 self[k] = v
959         if len(kwargs):
960             for k, v in kwargs.items():
961                 self[k] = v
962
963 ## Dictionary with restricted keys
964 #
965 class rdict(dict):
966     ## Constructor
967     def __init__(self, KeyList):
968         for Key in KeyList:
969             dict.__setitem__(self, Key, "")
970
971     ## []= operator
972     def __setitem__(self, key, value):
973         if key not in self:
974             EdkLogger.error("RestrictedDict", ATTRIBUTE_SET_FAILURE, "Key [%s] is not allowed" % key,
975                             ExtraData=", ".join(dict.keys(self)))
976         dict.__setitem__(self, key, value)
977
978     ## =[] operator
979     def __getitem__(self, key):
980         if key not in self:
981             return ""
982         return dict.__getitem__(self, key)
983
984     ## del operator
985     def __delitem__(self, key):
986         EdkLogger.error("RestrictedDict", ATTRIBUTE_ACCESS_DENIED, ExtraData="del")
987
988     ## Empty the dict
989     def clear(self):
990         for Key in self:
991             self.__setitem__(Key, "")
992
993     ## Return value related to a key, and remove the (key, value) from the dict
994     def pop(self, key, *dv):
995         EdkLogger.error("RestrictedDict", ATTRIBUTE_ACCESS_DENIED, ExtraData="pop")
996
997     ## Return (key, value) pair, and remove the (key, value) from the dict
998     def popitem(self):
999         EdkLogger.error("RestrictedDict", ATTRIBUTE_ACCESS_DENIED, ExtraData="popitem")
1000
1001 ## Dictionary using prioritized list as key
1002 #
1003 class tdict:
1004     _ListType = type([])
1005     _TupleType = type(())
1006     _Wildcard = 'COMMON'
1007     _ValidWildcardList = ['COMMON', 'DEFAULT', 'ALL', '*', 'PLATFORM']
1008
1009     def __init__(self, _Single_=False, _Level_=2):
1010         self._Level_ = _Level_
1011         self.data = {}
1012         self._Single_ = _Single_
1013
1014     # =[] operator
1015     def __getitem__(self, key):
1016         KeyType = type(key)
1017         RestKeys = None
1018         if KeyType == self._ListType or KeyType == self._TupleType:
1019             FirstKey = key[0]
1020             if len(key) > 1:
1021                 RestKeys = key[1:]
1022             elif self._Level_ > 1:
1023                 RestKeys = [self._Wildcard for i in range(0, self._Level_-1)]
1024         else:
1025             FirstKey = key
1026             if self._Level_ > 1:
1027                 RestKeys = [self._Wildcard for i in range(0, self._Level_-1)]
1028
1029         if FirstKey == None or str(FirstKey).upper() in self._ValidWildcardList:
1030             FirstKey = self._Wildcard
1031
1032         if self._Single_:
1033             return self._GetSingleValue(FirstKey, RestKeys)
1034         else:
1035             return self._GetAllValues(FirstKey, RestKeys)
1036
1037     def _GetSingleValue(self, FirstKey, RestKeys):
1038         Value = None
1039         #print "%s-%s" % (FirstKey, self._Level_) ,
1040         if self._Level_ > 1:
1041             if FirstKey == self._Wildcard:
1042                 if FirstKey in self.data:
1043                     Value = self.data[FirstKey][RestKeys]
1044                 if Value == None:
1045                     for Key in self.data:
1046                         Value = self.data[Key][RestKeys]
1047                         if Value != None: break
1048             else:
1049                 if FirstKey in self.data:
1050                     Value = self.data[FirstKey][RestKeys]
1051                 if Value == None and self._Wildcard in self.data:
1052                     #print "Value=None"
1053                     Value = self.data[self._Wildcard][RestKeys]
1054         else:
1055             if FirstKey == self._Wildcard:
1056                 if FirstKey in self.data:
1057                     Value = self.data[FirstKey]
1058                 if Value == None:
1059                     for Key in self.data:
1060                         Value = self.data[Key]
1061                         if Value != None: break
1062             else:
1063                 if FirstKey in self.data:
1064                     Value = self.data[FirstKey]
1065                 elif self._Wildcard in self.data:
1066                     Value = self.data[self._Wildcard]
1067         return Value
1068
1069     def _GetAllValues(self, FirstKey, RestKeys):
1070         Value = []
1071         if self._Level_ > 1:
1072             if FirstKey == self._Wildcard:
1073                 for Key in self.data:
1074                     Value += self.data[Key][RestKeys]
1075             else:
1076                 if FirstKey in self.data:
1077                     Value += self.data[FirstKey][RestKeys]
1078                 if self._Wildcard in self.data:
1079                     Value += self.data[self._Wildcard][RestKeys]
1080         else:
1081             if FirstKey == self._Wildcard:
1082                 for Key in self.data:
1083                     Value.append(self.data[Key])
1084             else:
1085                 if FirstKey in self.data:
1086                     Value.append(self.data[FirstKey])
1087                 if self._Wildcard in self.data:
1088                     Value.append(self.data[self._Wildcard])
1089         return Value
1090
1091     ## []= operator
1092     def __setitem__(self, key, value):
1093         KeyType = type(key)
1094         RestKeys = None
1095         if KeyType == self._ListType or KeyType == self._TupleType:
1096             FirstKey = key[0]
1097             if len(key) > 1:
1098                 RestKeys = key[1:]
1099             else:
1100                 RestKeys = [self._Wildcard for i in range(0, self._Level_-1)]
1101         else:
1102             FirstKey = key
1103             if self._Level_ > 1:
1104                 RestKeys = [self._Wildcard for i in range(0, self._Level_-1)]
1105
1106         if FirstKey in self._ValidWildcardList:
1107             FirstKey = self._Wildcard
1108
1109         if FirstKey not in self.data and self._Level_ > 0:
1110             self.data[FirstKey] = tdict(self._Single_, self._Level_ - 1)
1111
1112         if self._Level_ > 1:
1113             self.data[FirstKey][RestKeys] = value
1114         else:
1115             self.data[FirstKey] = value
1116
1117     def SetGreedyMode(self):
1118         self._Single_ = False
1119         if self._Level_ > 1:
1120             for Key in self.data:
1121                 self.data[Key].SetGreedyMode()
1122
1123     def SetSingleMode(self):
1124         self._Single_ = True
1125         if self._Level_ > 1:
1126             for Key in self.data:
1127                 self.data[Key].SetSingleMode()
1128
1129     def GetKeys(self, KeyIndex=0):
1130         assert KeyIndex >= 0
1131         if KeyIndex == 0:
1132             return set(self.data.keys())
1133         else:
1134             keys = set()
1135             for Key in self.data:
1136                 keys |= self.data[Key].GetKeys(KeyIndex - 1)
1137             return keys
1138
1139 ## Boolean chain list
1140 #
1141 class Blist(UserList):
1142     def __init__(self, initlist=None):
1143         UserList.__init__(self, initlist)
1144     def __setitem__(self, i, item):
1145         if item not in [True, False]:
1146             if item == 0:
1147                 item = False
1148             else:
1149                 item = True
1150         self.data[i] = item
1151     def _GetResult(self):
1152         Value = True
1153         for item in self.data:
1154             Value &= item
1155         return Value
1156     Result = property(_GetResult)
1157
1158 def ParseConsoleLog(Filename):
1159     Opr = open(os.path.normpath(Filename), 'r')
1160     Opw = open(os.path.normpath(Filename + '.New'), 'w+')
1161     for Line in Opr.readlines():
1162         if Line.find('.efi') > -1:
1163             Line = Line[Line.rfind(' ') : Line.rfind('.efi')].strip()
1164             Opw.write('%s\n' % Line)
1165
1166     Opr.close()
1167     Opw.close()
1168
1169 ## check format of PCD value against its the datum type
1170 #
1171 # For PCD value setting
1172 #
1173 def CheckPcdDatum(Type, Value):
1174     if Type == "VOID*":
1175         if not ((Value.startswith('L"') or Value.startswith('"') and Value.endswith('"'))
1176                 or (Value.startswith('{') and Value.endswith('}'))
1177                ):
1178             return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
1179                           ", or \"...\" for string, or L\"...\" for unicode string" % (Value, Type)
1180     elif Type == 'BOOLEAN':
1181         if Value not in ['TRUE', 'FALSE']:
1182             return False, "Invalid value [%s] of type [%s]; must be TRUE or FALSE" % (Value, Type)
1183     elif type(Value) == type(""):
1184         try:
1185             Value = long(Value, 0)
1186         except:
1187             return False, "Invalid value [%s] of type [%s];"\
1188                           " must be a hexadecimal, decimal or octal in C language format."\
1189                             % (Value, Type)
1190
1191     return True, ""
1192
1193 ## Split command line option string to list
1194 #
1195 # subprocess.Popen needs the args to be a sequence. Otherwise there's problem
1196 # in non-windows platform to launch command
1197 #
1198 def SplitOption(OptionString):
1199     OptionList = []
1200     LastChar = " "
1201     OptionStart = 0
1202     QuotationMark = ""
1203     for Index in range(0, len(OptionString)):
1204         CurrentChar = OptionString[Index]
1205         if CurrentChar in ['"', "'"]:
1206             if QuotationMark == CurrentChar:
1207                 QuotationMark = ""
1208             elif QuotationMark == "":
1209                 QuotationMark = CurrentChar
1210             continue
1211         elif QuotationMark:
1212             continue
1213
1214         if CurrentChar in ["/", "-"] and LastChar in [" ", "\t", "\r", "\n"]:
1215             if Index > OptionStart:
1216                 OptionList.append(OptionString[OptionStart:Index-1])
1217             OptionStart = Index
1218         LastChar = CurrentChar
1219     OptionList.append(OptionString[OptionStart:])
1220     return OptionList
1221
1222 def CommonPath(PathList):
1223     P1 = min(PathList).split(os.path.sep)
1224     P2 = max(PathList).split(os.path.sep)
1225     for Index in xrange(min(len(P1), len(P2))):
1226         if P1[Index] != P2[Index]:
1227             return os.path.sep.join(P1[:Index])
1228     return os.path.sep.join(P1)
1229
1230 class PathClass(object):
1231     def __init__(self, File='', Root='', AlterRoot='', Type='', IsBinary=False,
1232                  Arch='COMMON', ToolChainFamily='', Target='', TagName='', ToolCode=''):
1233         self.Arch = Arch
1234         self.File = str(File)
1235         if os.path.isabs(self.File):
1236             self.Root = ''
1237             self.AlterRoot = ''
1238         else:
1239             self.Root = str(Root)
1240             self.AlterRoot = str(AlterRoot)
1241
1242         # Remove any '.' and '..' in path
1243         if self.Root:
1244             self.Path = os.path.normpath(os.path.join(self.Root, self.File))
1245             self.Root = os.path.normpath(CommonPath([self.Root, self.Path]))
1246             # eliminate the side-effect of 'C:'
1247             if self.Root[-1] == ':':
1248                 self.Root += os.path.sep
1249             # file path should not start with path separator
1250             if self.Root[-1] == os.path.sep:
1251                 self.File = self.Path[len(self.Root):]
1252             else:
1253                 self.File = self.Path[len(self.Root)+1:]
1254         else:
1255             self.Path = os.path.normpath(self.File)
1256
1257         self.SubDir, self.Name = os.path.split(self.File)
1258         self.BaseName, self.Ext = os.path.splitext(self.Name)
1259
1260         if self.Root:
1261             if self.SubDir:
1262                 self.Dir = os.path.join(self.Root, self.SubDir)
1263             else:
1264                 self.Dir = self.Root
1265         else:
1266             self.Dir = self.SubDir
1267
1268         if IsBinary:
1269             self.Type = Type
1270         else:
1271             self.Type = self.Ext.lower()
1272
1273         self.IsBinary = IsBinary
1274         self.Target = Target
1275         self.TagName = TagName
1276         self.ToolCode = ToolCode
1277         self.ToolChainFamily = ToolChainFamily
1278
1279         self._Key = None
1280
1281     ## Convert the object of this class to a string
1282     #
1283     #  Convert member Path of the class to a string
1284     #
1285     #  @retval string Formatted String
1286     #
1287     def __str__(self):
1288         return self.Path
1289
1290     ## Override __eq__ function
1291     #
1292     # Check whether PathClass are the same
1293     #
1294     # @retval False The two PathClass are different
1295     # @retval True  The two PathClass are the same
1296     #
1297     def __eq__(self, Other):
1298         if type(Other) == type(self):
1299             return self.Path == Other.Path
1300         else:
1301             return self.Path == str(Other)
1302
1303     ## Override __hash__ function
1304     #
1305     # Use Path as key in hash table
1306     #
1307     # @retval string Key for hash table
1308     #
1309     def __hash__(self):
1310         return hash(self.Path)
1311
1312     def _GetFileKey(self):
1313         if self._Key == None:
1314             self._Key = self.Path.upper()   # + self.ToolChainFamily + self.TagName + self.ToolCode + self.Target
1315         return self._Key
1316
1317     def Validate(self, Type='', CaseSensitive=True):
1318         if GlobalData.gCaseInsensitive:
1319             CaseSensitive = False
1320         if Type and Type.lower() != self.Type:
1321             return FILE_TYPE_MISMATCH, '%s (expect %s but got %s)' % (self.File, Type, self.Type)
1322
1323         RealFile, RealRoot = RealPath2(self.File, self.Root, self.AlterRoot)
1324         if not RealRoot and not RealFile:
1325             return FILE_NOT_FOUND, self.File
1326
1327         ErrorCode = 0
1328         ErrorInfo = ''
1329         if RealRoot != self.Root or RealFile != self.File:
1330             if CaseSensitive and (RealFile != self.File or (RealRoot != self.Root and RealRoot != self.AlterRoot)):
1331                 ErrorCode = FILE_CASE_MISMATCH
1332                 ErrorInfo = self.File + '\n\t' + RealFile + " [in file system]"
1333
1334             self.SubDir, self.Name = os.path.split(RealFile)
1335             self.BaseName, self.Ext = os.path.splitext(self.Name)
1336             if self.SubDir:
1337                 self.Dir = os.path.join(RealRoot, self.SubDir)
1338             else:
1339                 self.Dir = RealRoot
1340             self.File = RealFile
1341             self.Root = RealRoot
1342             self.Path = os.path.join(RealRoot, RealFile)
1343         return ErrorCode, ErrorInfo
1344
1345     Key = property(_GetFileKey)
1346
1347 ## Parse PE image to get the required PE informaion.
1348 #
1349 class PeImageClass():
1350     ## Constructor
1351     #
1352     #   @param  File FilePath of PeImage
1353     #
1354     def __init__(self, PeFile):
1355         self.FileName   = PeFile
1356         self.IsValid    = False
1357         self.Size       = 0
1358         self.EntryPoint = 0
1359         self.SectionAlignment  = 0
1360         self.SectionHeaderList = []
1361         self.ErrorInfo = ''
1362         try:
1363              PeObject = open(PeFile, 'rb')
1364         except:
1365             self.ErrorInfo = self.FileName + ' can not be found\n'
1366             return
1367         # Read DOS header
1368         ByteArray = array.array('B')
1369         ByteArray.fromfile(PeObject, 0x3E)
1370         ByteList = ByteArray.tolist()
1371         # DOS signature should be 'MZ'
1372         if self._ByteListToStr (ByteList[0x0:0x2]) != 'MZ':
1373             self.ErrorInfo = self.FileName + ' has no valid DOS signature MZ'
1374             return
1375
1376         # Read 4 byte PE Signature
1377         PeOffset = self._ByteListToInt(ByteList[0x3C:0x3E])
1378         PeObject.seek(PeOffset)
1379         ByteArray = array.array('B')
1380         ByteArray.fromfile(PeObject, 4)
1381         # PE signature should be 'PE\0\0'
1382         if ByteArray.tostring() != 'PE\0\0':
1383             self.ErrorInfo = self.FileName + ' has no valid PE signature PE00'
1384             return
1385
1386         # Read PE file header
1387         ByteArray = array.array('B')
1388         ByteArray.fromfile(PeObject, 0x14)
1389         ByteList = ByteArray.tolist()
1390         SecNumber = self._ByteListToInt(ByteList[0x2:0x4])
1391         if SecNumber == 0:
1392             self.ErrorInfo = self.FileName + ' has no section header'
1393             return
1394
1395         # Read PE optional header
1396         OptionalHeaderSize = self._ByteListToInt(ByteArray[0x10:0x12])
1397         ByteArray = array.array('B')
1398         ByteArray.fromfile(PeObject, OptionalHeaderSize)
1399         ByteList = ByteArray.tolist()
1400         self.EntryPoint       = self._ByteListToInt(ByteList[0x10:0x14])
1401         self.SectionAlignment = self._ByteListToInt(ByteList[0x20:0x24])
1402         self.Size             = self._ByteListToInt(ByteList[0x38:0x3C])
1403
1404         # Read each Section Header
1405         for Index in range(SecNumber):
1406             ByteArray = array.array('B')
1407             ByteArray.fromfile(PeObject, 0x28)
1408             ByteList = ByteArray.tolist()
1409             SecName  = self._ByteListToStr(ByteList[0:8])
1410             SecVirtualSize = self._ByteListToInt(ByteList[8:12])
1411             SecRawAddress  = self._ByteListToInt(ByteList[20:24])
1412             SecVirtualAddress = self._ByteListToInt(ByteList[12:16])
1413             self.SectionHeaderList.append((SecName, SecVirtualAddress, SecRawAddress, SecVirtualSize))
1414         self.IsValid = True
1415         PeObject.close()
1416
1417     def _ByteListToStr(self, ByteList):
1418         String = ''
1419         for index in range(len(ByteList)):
1420             if ByteList[index] == 0: 
1421                 break
1422             String += chr(ByteList[index])
1423         return String
1424
1425     def _ByteListToInt(self, ByteList):
1426         Value = 0
1427         for index in range(len(ByteList) - 1, -1, -1):
1428             Value = (Value << 8) | int(ByteList[index])
1429         return Value
1430         
1431 ##
1432 #
1433 # This acts like the main() function for the script, unless it is 'import'ed into another
1434 # script.
1435 #
1436 if __name__ == '__main__':
1437     pass
1438