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
 |