## @file # # Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
# # 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 '
\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