https://bugzilla.tianocore.org/show_bug.cgi?id=1373 Replace BSD 2-Clause License with BSD+Patent License. This change is based on the following emails: https://lists.01.org/pipermail/edk2-devel/2019-February/036260.html https://lists.01.org/pipermail/edk2-devel/2018-October/030385.html RFCs with detailed process for the license change: V3: https://lists.01.org/pipermail/edk2-devel/2019-March/038116.html V2: https://lists.01.org/pipermail/edk2-devel/2019-March/037669.html V1: https://lists.01.org/pipermail/edk2-devel/2019-March/037500.html Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Bob Feng <bob.c.feng@intel.com>
		
			
				
	
	
		
			476 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			476 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| ## @file
 | |
| #
 | |
| # Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
 | |
| #
 | |
| # SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| #
 | |
| 
 | |
| from __future__ import absolute_import
 | |
| from .message import *
 | |
| import re
 | |
| import os
 | |
| 
 | |
| section_re = re.compile(r'^\[([\w., "]+)\]')
 | |
| 
 | |
| class BaseINIFile(object):
 | |
|     _objs = {}
 | |
|     def __new__(cls, *args, **kwargs):
 | |
|         """Maintain only a single instance of this object
 | |
|         @return: instance of this class
 | |
| 
 | |
|         """
 | |
|         if len(args) == 0: return object.__new__(cls)
 | |
|         filename = args[0]
 | |
|         parent   = None
 | |
|         if len(args) > 1:
 | |
|             parent = args[1]
 | |
| 
 | |
|         key = os.path.normpath(filename)
 | |
|         if key not in cls._objs.keys():
 | |
|             cls._objs[key] = object.__new__(cls)
 | |
| 
 | |
|         if parent is not None:
 | |
|             cls._objs[key].AddParent(parent)
 | |
| 
 | |
|         return cls._objs[key]
 | |
| 
 | |
|     def __init__(self, filename=None, parent=None):
 | |
|         self._lines    = []
 | |
|         self._sections = {}
 | |
|         self._filename = filename
 | |
|         self._globals  = []
 | |
|         self._isModify = True
 | |
| 
 | |
|     def AddParent(self, parent):
 | |
|         if parent is None: return
 | |
|         if not hasattr(self, "_parents"):
 | |
|             self._parents = []
 | |
| 
 | |
|         if parent in self._parents:
 | |
|             ErrorMsg("Duplicate parent is found for INI file %s" % self._filename)
 | |
|             return
 | |
|         self._parents.append(parent)
 | |
| 
 | |
|     def GetFilename(self):
 | |
|         return os.path.normpath(self._filename)
 | |
| 
 | |
|     def IsModified(self):
 | |
|         return self._isModify
 | |
| 
 | |
|     def Modify(self, modify=True, obj=None):
 | |
|         if modify == self._isModify: return
 | |
|         self._isModify = modify
 | |
|         if modify:
 | |
|             for parent in self._parents:
 | |
|                 parent.Modify(True, self)
 | |
| 
 | |
|     def _ReadLines(self, filename):
 | |
|         #
 | |
|         # try to open file
 | |
|         #
 | |
|         if not os.path.exists(filename):
 | |
|             return False
 | |
| 
 | |
|         try:
 | |
|             handle = open(filename, 'r')
 | |
|             self._lines  = handle.readlines()
 | |
|             handle.close()
 | |
|         except:
 | |
|             raise EdkException("Fail to open file %s" % filename)
 | |
| 
 | |
|         return True
 | |
| 
 | |
|     def GetSectionInstance(self, parent, name, isCombined=False):
 | |
|         return BaseINISection(parent, name, isCombined)
 | |
| 
 | |
|     def GetSectionByName(self, name):
 | |
|         arr = []
 | |
|         for key in self._sections.keys():
 | |
|             if '.private' in key:
 | |
|                 continue
 | |
|             for item in self._sections[key]:
 | |
|                 if item.GetBaseName().lower().find(name.lower()) != -1:
 | |
|                     arr.append(item)
 | |
|         return arr
 | |
| 
 | |
|     def GetSectionObjectsByName(self, name):
 | |
|         arr = []
 | |
|         sects = self.GetSectionByName(name)
 | |
|         for sect in sects:
 | |
|             for obj in sect.GetObjects():
 | |
|                 arr.append(obj)
 | |
|         return arr
 | |
| 
 | |
|     def Parse(self):
 | |
|         if not self._isModify: return True
 | |
|         if not self._ReadLines(self._filename): return False
 | |
| 
 | |
|         sObjs    = []
 | |
|         inGlobal = True
 | |
|         # process line
 | |
|         for index in range(len(self._lines)):
 | |
|             templine = self._lines[index].strip()
 | |
|             # skip comments
 | |
|             if len(templine) == 0: continue
 | |
|             if re.match("^\[=*\]", templine) or re.match("^#", templine) or \
 | |
|                re.match("\*+/", templine):
 | |
|                 continue
 | |
| 
 | |
|             m = section_re.match(templine)
 | |
|             if m is not None: # found a section
 | |
|                 inGlobal = False
 | |
|                 # Finish the latest section first
 | |
|                 if len(sObjs) != 0:
 | |
|                     for sObj in sObjs:
 | |
|                         sObj._end = index - 1
 | |
|                         if not sObj.Parse():
 | |
|                             ErrorMsg("Fail to parse section %s" % sObj.GetBaseName(),
 | |
|                                      self._filename,
 | |
|                                      sObj._start)
 | |
| 
 | |
|                 # start new section
 | |
|                 sname_arr = m.groups()[0].split(',')
 | |
|                 sObjs = []
 | |
|                 for name in sname_arr:
 | |
|                     sObj = self.GetSectionInstance(self, name, (len(sname_arr) > 1))
 | |
|                     sObj._start = index
 | |
|                     sObjs.append(sObj)
 | |
|                     if name.lower() not in self._sections:
 | |
|                         self._sections[name.lower()] = [sObj]
 | |
|                     else:
 | |
|                         self._sections[name.lower()].append(sObj)
 | |
|             elif inGlobal:  # not start any section and find global object
 | |
|                 gObj = BaseINIGlobalObject(self)
 | |
|                 gObj._start = index
 | |
|                 gObj.Parse()
 | |
|                 self._globals.append(gObj)
 | |
| 
 | |
|         # Finish the last section
 | |
|         if len(sObjs) != 0:
 | |
|             for sObj in sObjs:
 | |
|                 sObj._end = index
 | |
|                 if not sObj.Parse():
 | |
|                     ErrorMsg("Fail to parse section %s" % sObj.GetBaseName(),
 | |
|                              self._filename,
 | |
|                              sObj._start)
 | |
| 
 | |
|         self._isModify = False
 | |
|         return True
 | |
| 
 | |
|     def Destroy(self, parent):
 | |
| 
 | |
|         # check referenced parent
 | |
|         if parent is not None:
 | |
|             assert parent in self._parents, "when destory ini object, can not found parent reference!"
 | |
|             self._parents.remove(parent)
 | |
| 
 | |
|         if len(self._parents) != 0: return
 | |
| 
 | |
|         for sects in self._sections.values():
 | |
|             for sect in sects:
 | |
|                 sect.Destroy()
 | |
| 
 | |
|         # dereference from _objs array
 | |
|         assert self.GetFilename() in self._objs.keys(), "When destroy ini object, can not find obj reference!"
 | |
|         assert self in self._objs.values(), "When destroy ini object, can not find obj reference!"
 | |
|         del self._objs[self.GetFilename()]
 | |
| 
 | |
|         # dereference self
 | |
|         self.Clear()
 | |
| 
 | |
|     def GetDefine(self, name):
 | |
|         sects = self.GetSectionByName('Defines')
 | |
|         for sect in sects:
 | |
|             for obj in sect.GetObjects():
 | |
|                 line = obj.GetLineByOffset(obj._start).split('#')[0].strip()
 | |
|                 arr = line.split('=')
 | |
|                 if arr[0].strip().lower() == name.strip().lower():
 | |
|                     return arr[1].strip()
 | |
|         return None
 | |
| 
 | |
|     def Clear(self):
 | |
|         for sects in self._sections.values():
 | |
|             for sect in sects:
 | |
|                 del sect
 | |
|         self._sections.clear()
 | |
|         for gObj in self._globals:
 | |
|             del gObj
 | |
| 
 | |
|         del self._globals[:]
 | |
|         del self._lines[:]
 | |
| 
 | |
|     def Reload(self):
 | |
|         self.Clear()
 | |
|         ret = self.Parse()
 | |
|         if ret:
 | |
|             self._isModify = False
 | |
|         return ret
 | |
| 
 | |
|     def AddNewSection(self, sectName):
 | |
|         if sectName.lower() in self._sections.keys():
 | |
|             ErrorMsg('Section %s can not be created for conflict with existing section')
 | |
|             return None
 | |
| 
 | |
|         sectionObj = self.GetSectionInstance(self, sectName)
 | |
|         sectionObj._start = len(self._lines)
 | |
|         sectionObj._end   = len(self._lines) + 1
 | |
|         self._lines.append('[%s]\n' % sectName)
 | |
|         self._lines.append('\n\n')
 | |
|         self._sections[sectName.lower()] = sectionObj
 | |
|         return sectionObj
 | |
| 
 | |
|     def CopySectionsByName(self, oldDscObj, nameStr):
 | |
|         sects = oldDscObj.GetSectionByName(nameStr)
 | |
|         for sect in sects:
 | |
|             sectObj = self.AddNewSection(sect.GetName())
 | |
|             sectObj.Copy(sect)
 | |
| 
 | |
|     def __str__(self):
 | |
|         return ''.join(self._lines)
 | |
| 
 | |
|     ## Get file header's comment from basic INI file.
 | |
|     #  The file comments has two style:
 | |
|     #  1) #/** @file
 | |
|     #  2) ## @file
 | |
|     #
 | |
|     def GetFileHeader(self):
 | |
|         desc = []
 | |
|         lineArr  = self._lines
 | |
|         inHeader = False
 | |
|         for num in range(len(self._lines)):
 | |
|             line = lineArr[num].strip()
 | |
|             if not inHeader and (line.startswith("#/**") or line.startswith("##")) and \
 | |
|                 line.find("@file") != -1:
 | |
|                 inHeader = True
 | |
|                 continue
 | |
|             if inHeader and (line.startswith("#**/") or line.startswith('##')):
 | |
|                 inHeader = False
 | |
|                 break
 | |
|             if inHeader:
 | |
|                 prefixIndex = line.find('#')
 | |
|                 if prefixIndex == -1:
 | |
|                     desc.append(line)
 | |
|                 else:
 | |
|                     desc.append(line[prefixIndex + 1:])
 | |
|         return '<br>\n'.join(desc)
 | |
| 
 | |
| class BaseINISection(object):
 | |
|     def __init__(self, parent, name, isCombined=False):
 | |
|         self._parent     = parent
 | |
|         self._name       = name
 | |
|         self._isCombined = isCombined
 | |
|         self._start      = 0
 | |
|         self._end        = 0
 | |
|         self._objs       = []
 | |
| 
 | |
|     def __del__(self):
 | |
|         for obj in self._objs:
 | |
|             del obj
 | |
|         del self._objs[:]
 | |
| 
 | |
|     def GetName(self):
 | |
|         return self._name
 | |
| 
 | |
|     def GetObjects(self):
 | |
|         return self._objs
 | |
| 
 | |
|     def GetParent(self):
 | |
|         return self._parent
 | |
| 
 | |
|     def GetStartLinenumber(self):
 | |
|         return self._start
 | |
| 
 | |
|     def GetEndLinenumber(self):
 | |
|         return self._end
 | |
| 
 | |
|     def GetLine(self, linenumber):
 | |
|         return self._parent._lines[linenumber]
 | |
| 
 | |
|     def GetFilename(self):
 | |
|         return self._parent.GetFilename()
 | |
| 
 | |
|     def GetSectionINIObject(self, parent):
 | |
|         return BaseINISectionObject(parent)
 | |
| 
 | |
|     def Parse(self):
 | |
|         # skip first line in section, it is used by section name
 | |
|         visit = self._start + 1
 | |
|         iniObj = None
 | |
|         while (visit <= self._end):
 | |
|             line = self.GetLine(visit).strip()
 | |
|             if re.match("^\[=*\]", line) or re.match("^#", line) or len(line) == 0:
 | |
|                 visit += 1
 | |
|                 continue
 | |
|             line = line.split('#')[0].strip()
 | |
|             if iniObj is not None:
 | |
|                 if line.endswith('}'):
 | |
|                     iniObj._end = visit - self._start
 | |
|                     if not iniObj.Parse():
 | |
|                         ErrorMsg("Fail to parse ini object",
 | |
|                                  self.GetFilename(),
 | |
|                                  iniObj.GetStartLinenumber())
 | |
|                     else:
 | |
|                         self._objs.append(iniObj)
 | |
|                     iniObj = None
 | |
|             else:
 | |
|                 iniObj = self.GetSectionINIObject(self)
 | |
|                 iniObj._start = visit - self._start
 | |
|                 if not line.endswith('{'):
 | |
|                     iniObj._end = visit - self._start
 | |
|                     if not iniObj.Parse():
 | |
|                         ErrorMsg("Fail to parse ini object",
 | |
|                                  self.GetFilename(),
 | |
|                                  iniObj.GetStartLinenumber())
 | |
|                     else:
 | |
|                         self._objs.append(iniObj)
 | |
|                     iniObj = None
 | |
|             visit += 1
 | |
|         return True
 | |
| 
 | |
|     def Destroy(self):
 | |
|         for obj in self._objs:
 | |
|             obj.Destroy()
 | |
| 
 | |
|     def GetBaseName(self):
 | |
|         return self._name
 | |
| 
 | |
|     def AddLine(self, line):
 | |
|         end = self.GetEndLinenumber()
 | |
|         self._parent._lines.insert(end, line)
 | |
|         self._end += 1
 | |
| 
 | |
|     def Copy(self, sectObj):
 | |
|         index = sectObj.GetStartLinenumber() + 1
 | |
|         while index < sectObj.GetEndLinenumber():
 | |
|             line = sectObj.GetLine(index)
 | |
|             if not line.strip().startswith('#'):
 | |
|                 self.AddLine(line)
 | |
|             index += 1
 | |
| 
 | |
|     def AddObject(self, obj):
 | |
|         lines = obj.GenerateLines()
 | |
|         for line in lines:
 | |
|             self.AddLine(line)
 | |
| 
 | |
|     def GetComment(self):
 | |
|         comments = []
 | |
|         start  = self._start - 1
 | |
|         bFound = False
 | |
| 
 | |
|         while (start > 0):
 | |
|             line = self.GetLine(start).strip()
 | |
|             if len(line) == 0:
 | |
|                 start -= 1
 | |
|                 continue
 | |
|             if line.startswith('##'):
 | |
|                 bFound = True
 | |
|                 index = line.rfind('#')
 | |
|                 if (index + 1) < len(line):
 | |
|                     comments.append(line[index + 1:])
 | |
|                 break
 | |
|             if line.startswith('#'):
 | |
|                 start -= 1
 | |
|                 continue
 | |
|             break
 | |
|         if bFound:
 | |
|             end = start + 1
 | |
|             while (end < self._start):
 | |
|                 line = self.GetLine(end).strip()
 | |
|                 if len(line) == 0: break
 | |
|                 if not line.startswith('#'): break
 | |
|                 index = line.rfind('#')
 | |
|                 if (index + 1) < len(line):
 | |
|                     comments.append(line[index + 1:])
 | |
|                 end += 1
 | |
|         return comments
 | |
| 
 | |
| class BaseINIGlobalObject(object):
 | |
|     def __init__(self, parent):
 | |
|         self._start = 0
 | |
|         self._end   = 0
 | |
| 
 | |
|     def Parse(self):
 | |
|         return True
 | |
| 
 | |
|     def __str__(self):
 | |
|         return parent._lines[self._start]
 | |
| 
 | |
|     def __del__(self):
 | |
|         pass
 | |
| 
 | |
| class BaseINISectionObject(object):
 | |
|     def __init__(self, parent):
 | |
|         self._start  = 0
 | |
|         self._end    = 0
 | |
|         self._parent = parent
 | |
| 
 | |
|     def __del__(self):
 | |
|         self._parent = None
 | |
| 
 | |
|     def GetParent(self):
 | |
|         return self._parent
 | |
| 
 | |
|     def GetFilename(self):
 | |
|         return self.GetParent().GetFilename()
 | |
| 
 | |
|     def GetPackageName(self):
 | |
|         return self.GetFilename()
 | |
| 
 | |
|     def GetFileObj(self):
 | |
|         return self.GetParent().GetParent()
 | |
| 
 | |
|     def GetStartLinenumber(self):
 | |
|         return self.GetParent()._start + self._start
 | |
| 
 | |
|     def GetLineByOffset(self, offset):
 | |
|         sect_start = self._parent.GetStartLinenumber()
 | |
|         linenumber = sect_start + offset
 | |
|         return self._parent.GetLine(linenumber)
 | |
| 
 | |
|     def GetLinenumberByOffset(self, offset):
 | |
|         return offset + self._parent.GetStartLinenumber()
 | |
| 
 | |
|     def Parse(self):
 | |
|         return True
 | |
| 
 | |
|     def Destroy(self):
 | |
|         pass
 | |
| 
 | |
|     def __str__(self):
 | |
|         return self.GetLineByOffset(self._start).strip()
 | |
| 
 | |
|     def GenerateLines(self):
 | |
|         return ['default setion object string\n']
 | |
| 
 | |
|     def GetComment(self):
 | |
|         comments = []
 | |
|         start  = self.GetStartLinenumber() - 1
 | |
|         bFound = False
 | |
| 
 | |
|         while (start > 0):
 | |
|             line = self.GetParent().GetLine(start).strip()
 | |
|             if len(line) == 0:
 | |
|                 start -= 1
 | |
|                 continue
 | |
|             if line.startswith('##'):
 | |
|                 bFound = True
 | |
|                 index = line.rfind('#')
 | |
|                 if (index + 1) < len(line):
 | |
|                     comments.append(line[index + 1:])
 | |
|                 break
 | |
|             if line.startswith('#'):
 | |
|                 start -= 1
 | |
|                 continue
 | |
|             break
 | |
|         if bFound:
 | |
|             end = start + 1
 | |
|             while (end <= self.GetStartLinenumber() - 1):
 | |
|                 line = self.GetParent().GetLine(end).strip()
 | |
|                 if len(line) == 0: break
 | |
|                 if not line.startswith('#'): break
 | |
|                 index = line.rfind('#')
 | |
|                 if (index + 1) < len(line):
 | |
|                     comments.append(line[index + 1:])
 | |
|                 end += 1
 | |
|         return comments
 |