## @file
# This file is used to be the c coding style checking of ECC tool
#
# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution.  The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php
#
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#

from __future__ import print_function
from __future__ import absolute_import
import sys
import Common.LongFilePathOs as os
import re
import string
from . import CodeFragmentCollector
from . import FileProfile
from CommonDataClass import DataClass
from . import Database
from Common import EdkLogger
from .EccToolError import *
from . import EccGlobalData
from . import MetaDataParser

IncludeFileListDict = {}
AllIncludeFileListDict = {}
IncludePathListDict = {}
ComplexTypeDict = {}
SUDict = {}
IgnoredKeywordList = ['EFI_ERROR']

def GetIgnoredDirListPattern():
    skipList = list(EccGlobalData.gConfig.SkipDirList) + ['.svn']
    DirString = string.join(skipList, '|')
    p = re.compile(r'.*[\\/](?:%s)[\\/]?.*' % DirString)
    return p

def GetFuncDeclPattern():
    p = re.compile(r'(?:EFIAPI|EFI_BOOT_SERVICE|EFI_RUNTIME_SERVICE)?\s*[_\w]+\s*\(.*\)$', re.DOTALL)
    return p

def GetArrayPattern():
    p = re.compile(r'[_\w]*\s*[\[.*\]]+')
    return p

def GetTypedefFuncPointerPattern():
    p = re.compile('[_\w\s]*\([\w\s]*\*+\s*[_\w]+\s*\)\s*\(.*\)', re.DOTALL)
    return p

def GetDB():
    return EccGlobalData.gDb

def GetConfig():
    return EccGlobalData.gConfig

def PrintErrorMsg(ErrorType, Msg, TableName, ItemId):
    Msg = Msg.replace('\n', '').replace('\r', '')
    MsgPartList = Msg.split()
    Msg = ''
    for Part in MsgPartList:
        Msg += Part
        Msg += ' '
    GetDB().TblReport.Insert(ErrorType, OtherMsg=Msg, BelongsToTable=TableName, BelongsToItem=ItemId)

def GetIdType(Str):
    Type = DataClass.MODEL_UNKNOWN
    Str = Str.replace('#', '# ')
    List = Str.split()
    if List[1] == 'include':
        Type = DataClass.MODEL_IDENTIFIER_INCLUDE
    elif List[1] == 'define':
        Type = DataClass.MODEL_IDENTIFIER_MACRO_DEFINE
    elif List[1] == 'ifdef':
        Type = DataClass.MODEL_IDENTIFIER_MACRO_IFDEF
    elif List[1] == 'ifndef':
        Type = DataClass.MODEL_IDENTIFIER_MACRO_IFNDEF
    elif List[1] == 'endif':
        Type = DataClass.MODEL_IDENTIFIER_MACRO_ENDIF
    elif List[1] == 'pragma':
        Type = DataClass.MODEL_IDENTIFIER_MACRO_PROGMA
    else:
        Type = DataClass.MODEL_UNKNOWN
    return Type

def SuOccurInTypedef (Su, TdList):
    for Td in TdList:
        if Su.StartPos[0] == Td.StartPos[0] and Su.EndPos[0] == Td.EndPos[0]:
            return True
    return False

def GetIdentifierList():
    IdList = []
    for comment in FileProfile.CommentList:
        IdComment = DataClass.IdentifierClass(-1, '', '', '', comment.Content, DataClass.MODEL_IDENTIFIER_COMMENT, -1, -1, comment.StartPos[0], comment.StartPos[1], comment.EndPos[0], comment.EndPos[1])
        IdList.append(IdComment)

    for pp in FileProfile.PPDirectiveList:
        Type = GetIdType(pp.Content)
        IdPP = DataClass.IdentifierClass(-1, '', '', '', pp.Content, Type, -1, -1, pp.StartPos[0], pp.StartPos[1], pp.EndPos[0], pp.EndPos[1])
        IdList.append(IdPP)

    for pe in FileProfile.PredicateExpressionList:
        IdPE = DataClass.IdentifierClass(-1, '', '', '', pe.Content, DataClass.MODEL_IDENTIFIER_PREDICATE_EXPRESSION, -1, -1, pe.StartPos[0], pe.StartPos[1], pe.EndPos[0], pe.EndPos[1])
        IdList.append(IdPE)

    FuncDeclPattern = GetFuncDeclPattern()
    ArrayPattern = GetArrayPattern()
    for var in FileProfile.VariableDeclarationList:
        DeclText = var.Declarator.lstrip()
        FuncPointerPattern = GetTypedefFuncPointerPattern()
        if FuncPointerPattern.match(DeclText):
            continue
        VarNameStartLine = var.NameStartPos[0]
        VarNameStartColumn = var.NameStartPos[1]
        FirstChar = DeclText[0]
        while not FirstChar.isalpha() and FirstChar != '_':
            if FirstChar == '*':
                var.Modifier += '*'
                VarNameStartColumn += 1
                DeclText = DeclText.lstrip('*')
            elif FirstChar == '\r':
                DeclText = DeclText.lstrip('\r\n').lstrip('\r')
                VarNameStartLine += 1
                VarNameStartColumn = 0
            elif FirstChar == '\n':
                DeclText = DeclText.lstrip('\n')
                VarNameStartLine += 1
                VarNameStartColumn = 0
            elif FirstChar == ' ':
                DeclText = DeclText.lstrip(' ')
                VarNameStartColumn += 1
            elif FirstChar == '\t':
                DeclText = DeclText.lstrip('\t')
                VarNameStartColumn += 8
            else:
                DeclText = DeclText[1:]
                VarNameStartColumn += 1
            FirstChar = DeclText[0]

        var.Declarator = DeclText
        if FuncDeclPattern.match(var.Declarator):
            DeclSplitList = var.Declarator.split('(')
            FuncName = DeclSplitList[0].strip()
            FuncNamePartList = FuncName.split()
            if len(FuncNamePartList) > 1:
                FuncName = FuncNamePartList[-1].strip()
                NameStart = DeclSplitList[0].rfind(FuncName)
                var.Declarator = var.Declarator[NameStart:]
                if NameStart > 0:
                    var.Modifier += ' ' + DeclSplitList[0][0:NameStart]
                    Index = 0
                    PreChar = ''
                    while Index < NameStart:
                        FirstChar = DeclSplitList[0][Index]
                        if DeclSplitList[0][Index:].startswith('EFIAPI'):
                            Index += 6
                            VarNameStartColumn += 6
                            PreChar = ''
                            continue
                        elif FirstChar == '\r':
                            Index += 1
                            VarNameStartLine += 1
                            VarNameStartColumn = 0
                        elif FirstChar == '\n':
                            Index += 1
                            if PreChar != '\r':
                                VarNameStartLine += 1
                                VarNameStartColumn = 0
                        elif FirstChar == ' ':
                            Index += 1
                            VarNameStartColumn += 1
                        elif FirstChar == '\t':
                            Index += 1
                            VarNameStartColumn += 8
                        else:
                            Index += 1
                            VarNameStartColumn += 1
                        PreChar = FirstChar
            IdVar = DataClass.IdentifierClass(-1, var.Modifier, '', var.Declarator, FuncName, DataClass.MODEL_IDENTIFIER_FUNCTION_DECLARATION, -1, -1, var.StartPos[0], var.StartPos[1], VarNameStartLine, VarNameStartColumn)
            IdList.append(IdVar)
            continue

        if var.Declarator.find('{') == -1:
            for decl in var.Declarator.split(','):
                DeclList = decl.split('=')
                Name = DeclList[0].strip()
                if ArrayPattern.match(Name):
                    LSBPos = var.Declarator.find('[')
                    var.Modifier += ' ' + Name[LSBPos:]
                    Name = Name[0:LSBPos]

                IdVar = DataClass.IdentifierClass(-1, var.Modifier, '', Name, (len(DeclList) > 1 and [DeclList[1]]or [''])[0], DataClass.MODEL_IDENTIFIER_VARIABLE, -1, -1, var.StartPos[0], var.StartPos[1], VarNameStartLine, VarNameStartColumn)
                IdList.append(IdVar)
        else:
            DeclList = var.Declarator.split('=')
            Name = DeclList[0].strip()
            if ArrayPattern.match(Name):
                LSBPos = var.Declarator.find('[')
                var.Modifier += ' ' + Name[LSBPos:]
                Name = Name[0:LSBPos]
            IdVar = DataClass.IdentifierClass(-1, var.Modifier, '', Name, (len(DeclList) > 1 and [DeclList[1]]or [''])[0], DataClass.MODEL_IDENTIFIER_VARIABLE, -1, -1, var.StartPos[0], var.StartPos[1], VarNameStartLine, VarNameStartColumn)
            IdList.append(IdVar)

    for enum in FileProfile.EnumerationDefinitionList:
        LBPos = enum.Content.find('{')
        RBPos = enum.Content.find('}')
        Name = enum.Content[4:LBPos].strip()
        Value = enum.Content[LBPos + 1:RBPos]
        IdEnum = DataClass.IdentifierClass(-1, '', '', Name, Value, DataClass.MODEL_IDENTIFIER_ENUMERATE, -1, -1, enum.StartPos[0], enum.StartPos[1], enum.EndPos[0], enum.EndPos[1])
        IdList.append(IdEnum)

    for su in FileProfile.StructUnionDefinitionList:
        if SuOccurInTypedef(su, FileProfile.TypedefDefinitionList):
            continue
        Type = DataClass.MODEL_IDENTIFIER_STRUCTURE
        SkipLen = 6
        if su.Content.startswith('union'):
            Type = DataClass.MODEL_IDENTIFIER_UNION
            SkipLen = 5
        LBPos = su.Content.find('{')
        RBPos = su.Content.find('}')
        if LBPos == -1 or RBPos == -1:
            Name = su.Content[SkipLen:].strip()
            Value = ''
        else:
            Name = su.Content[SkipLen:LBPos].strip()
            Value = su.Content[LBPos:RBPos + 1]
        IdPE = DataClass.IdentifierClass(-1, '', '', Name, Value, Type, -1, -1, su.StartPos[0], su.StartPos[1], su.EndPos[0], su.EndPos[1])
        IdList.append(IdPE)

    TdFuncPointerPattern = GetTypedefFuncPointerPattern()
    for td in FileProfile.TypedefDefinitionList:
        Modifier = ''
        Name = td.ToType
        Value = td.FromType
        if TdFuncPointerPattern.match(td.ToType):
            Modifier = td.FromType
            LBPos = td.ToType.find('(')
            TmpStr = td.ToType[LBPos + 1:].strip()
            StarPos = TmpStr.find('*')
            if StarPos != -1:
                Modifier += ' ' + TmpStr[0:StarPos]
            while TmpStr[StarPos] == '*':
#                Modifier += ' ' + '*'
                StarPos += 1
            TmpStr = TmpStr[StarPos:].strip()
            RBPos = TmpStr.find(')')
            Name = TmpStr[0:RBPos]
            Value = 'FP' + TmpStr[RBPos + 1:]
        else:
            while Name.startswith('*'):
                Value += ' ' + '*'
                Name = Name.lstrip('*').strip()

        if Name.find('[') != -1:
            LBPos = Name.find('[')
            RBPos = Name.rfind(']')
            Value += Name[LBPos : RBPos + 1]
            Name = Name[0 : LBPos]

        IdTd = DataClass.IdentifierClass(-1, Modifier, '', Name, Value, DataClass.MODEL_IDENTIFIER_TYPEDEF, -1, -1, td.StartPos[0], td.StartPos[1], td.EndPos[0], td.EndPos[1])
        IdList.append(IdTd)

    for funcCall in FileProfile.FunctionCallingList:
        IdFC = DataClass.IdentifierClass(-1, '', '', funcCall.FuncName, funcCall.ParamList, DataClass.MODEL_IDENTIFIER_FUNCTION_CALLING, -1, -1, funcCall.StartPos[0], funcCall.StartPos[1], funcCall.EndPos[0], funcCall.EndPos[1])
        IdList.append(IdFC)
    return IdList

def StripNonAlnumChars(Str):
    StrippedStr = ''
    for Char in Str:
        if Char.isalnum() or Char == '_':
            StrippedStr += Char
    return StrippedStr

def GetParamList(FuncDeclarator, FuncNameLine=0, FuncNameOffset=0):
    FuncDeclarator = StripComments(FuncDeclarator)
    ParamIdList = []
    #DeclSplitList = FuncDeclarator.split('(')
    LBPos = FuncDeclarator.find('(')
    #if len(DeclSplitList) < 2:
    if LBPos == -1:
        return ParamIdList
    #FuncName = DeclSplitList[0]
    FuncName = FuncDeclarator[0:LBPos]
    #ParamStr = DeclSplitList[1].rstrip(')')
    ParamStr = FuncDeclarator[LBPos + 1:].rstrip(')')
    LineSkipped = 0
    OffsetSkipped = 0
    TailChar = FuncName[-1]
    while not TailChar.isalpha() and TailChar != '_':

        if TailChar == '\n':
            FuncName = FuncName.rstrip('\r\n').rstrip('\n')
            LineSkipped += 1
            OffsetSkipped = 0
        elif TailChar == '\r':
            FuncName = FuncName.rstrip('\r')
            LineSkipped += 1
            OffsetSkipped = 0
        elif TailChar == ' ':
            FuncName = FuncName.rstrip(' ')
            OffsetSkipped += 1
        elif TailChar == '\t':
            FuncName = FuncName.rstrip('\t')
            OffsetSkipped += 8
        else:
            FuncName = FuncName[:-1]
        TailChar = FuncName[-1]

    OffsetSkipped += 1 #skip '('

    for p in ParamStr.split(','):
        ListP = p.split()
        if len(ListP) == 0:
            continue
        ParamName = ListP[-1]
        DeclText = ParamName.strip()
        RightSpacePos = p.rfind(ParamName)
        ParamModifier = p[0:RightSpacePos]
        if ParamName == 'OPTIONAL':
            if ParamModifier == '':
                ParamModifier += ' ' + 'OPTIONAL'
                DeclText = ''
            else:
                ParamName = ListP[-2]
                DeclText = ParamName.strip()
                RightSpacePos = p.rfind(ParamName)
                ParamModifier = p[0:RightSpacePos]
                ParamModifier += 'OPTIONAL'
        while DeclText.startswith('*'):
            ParamModifier += ' ' + '*'
            DeclText = DeclText.lstrip('*').strip()
        ParamName = DeclText
        # ignore array length if exists.
        LBIndex = ParamName.find('[')
        if LBIndex != -1:
            ParamName = ParamName[0:LBIndex]

        Start = RightSpacePos
        Index = 0
        PreChar = ''
        while Index < Start:
            FirstChar = p[Index]

            if FirstChar == '\r':
                Index += 1
                LineSkipped += 1
                OffsetSkipped = 0
            elif FirstChar == '\n':
                Index += 1
                if PreChar != '\r':
                    LineSkipped += 1
                    OffsetSkipped = 0
            elif FirstChar == ' ':
                Index += 1
                OffsetSkipped += 1
            elif FirstChar == '\t':
                Index += 1
                OffsetSkipped += 8
            else:
                Index += 1
                OffsetSkipped += 1
            PreChar = FirstChar

        ParamBeginLine = FuncNameLine + LineSkipped
        ParamBeginOffset = FuncNameOffset + OffsetSkipped

        Index = Start + len(ParamName)
        PreChar = ''
        while Index < len(p):
            FirstChar = p[Index]

            if FirstChar == '\r':
                Index += 1
                LineSkipped += 1
                OffsetSkipped = 0
            elif FirstChar == '\n':
                Index += 1
                if PreChar != '\r':
                    LineSkipped += 1
                    OffsetSkipped = 0
            elif FirstChar == ' ':
                Index += 1
                OffsetSkipped += 1
            elif FirstChar == '\t':
                Index += 1
                OffsetSkipped += 8
            else:
                Index += 1
                OffsetSkipped += 1
            PreChar = FirstChar

        ParamEndLine = FuncNameLine + LineSkipped
        ParamEndOffset = FuncNameOffset + OffsetSkipped
        if ParamName != '...':
            ParamName = StripNonAlnumChars(ParamName)
        IdParam = DataClass.IdentifierClass(-1, ParamModifier, '', ParamName, '', DataClass.MODEL_IDENTIFIER_PARAMETER, -1, -1, ParamBeginLine, ParamBeginOffset, ParamEndLine, ParamEndOffset)
        ParamIdList.append(IdParam)

        OffsetSkipped += 1 #skip ','

    return ParamIdList

def GetFunctionList():
    FuncObjList = []
    for FuncDef in FileProfile.FunctionDefinitionList:
        ParamIdList = []
        DeclText = FuncDef.Declarator.lstrip()
        FuncNameStartLine = FuncDef.NamePos[0]
        FuncNameStartColumn = FuncDef.NamePos[1]
        FirstChar = DeclText[0]
        while not FirstChar.isalpha() and FirstChar != '_':
            if FirstChar == '*':
                FuncDef.Modifier += '*'
                FuncNameStartColumn += 1
                DeclText = DeclText.lstrip('*')
            elif FirstChar == '\r':
                DeclText = DeclText.lstrip('\r\n').lstrip('\r')
                FuncNameStartLine += 1
                FuncNameStartColumn = 0
            elif FirstChar == '\n':
                DeclText = DeclText.lstrip('\n')
                FuncNameStartLine += 1
                FuncNameStartColumn = 0
            elif FirstChar == ' ':
                DeclText = DeclText.lstrip(' ')
                FuncNameStartColumn += 1
            elif FirstChar == '\t':
                DeclText = DeclText.lstrip('\t')
                FuncNameStartColumn += 8
            else:
                DeclText = DeclText[1:]
                FuncNameStartColumn += 1
            FirstChar = DeclText[0]

        FuncDef.Declarator = DeclText
        DeclSplitList = FuncDef.Declarator.split('(')
        if len(DeclSplitList) < 2:
            continue

        FuncName = DeclSplitList[0]
        FuncNamePartList = FuncName.split()
        if len(FuncNamePartList) > 1:
            FuncName = FuncNamePartList[-1]
            NameStart = DeclSplitList[0].rfind(FuncName)
            if NameStart > 0:
                FuncDef.Modifier += ' ' + DeclSplitList[0][0:NameStart]
                Index = 0
                PreChar = ''
                while Index < NameStart:
                    FirstChar = DeclSplitList[0][Index]
                    if DeclSplitList[0][Index:].startswith('EFIAPI'):
                        Index += 6
                        FuncNameStartColumn += 6
                        PreChar = ''
                        continue
                    elif FirstChar == '\r':
                        Index += 1
                        FuncNameStartLine += 1
                        FuncNameStartColumn = 0
                    elif FirstChar == '\n':
                        Index += 1
                        if PreChar != '\r':
                            FuncNameStartLine += 1
                            FuncNameStartColumn = 0
                    elif FirstChar == ' ':
                        Index += 1
                        FuncNameStartColumn += 1
                    elif FirstChar == '\t':
                        Index += 1
                        FuncNameStartColumn += 8
                    else:
                        Index += 1
                        FuncNameStartColumn += 1
                    PreChar = FirstChar

        FuncObj = DataClass.FunctionClass(-1, FuncDef.Declarator, FuncDef.Modifier, FuncName.strip(), '', FuncDef.StartPos[0], FuncDef.StartPos[1], FuncDef.EndPos[0], FuncDef.EndPos[1], FuncDef.LeftBracePos[0], FuncDef.LeftBracePos[1], -1, ParamIdList, [], FuncNameStartLine, FuncNameStartColumn)
        FuncObjList.append(FuncObj)

    return FuncObjList

def GetFileModificationTimeFromDB(FullFileName):
    TimeValue = 0.0
    Db = GetDB()
    SqlStatement = """ select TimeStamp
                       from File
                       where FullPath = \'%s\'
                   """ % (FullFileName)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    for Result in ResultSet:
        TimeValue = Result[0]
    return TimeValue

def CollectSourceCodeDataIntoDB(RootDir):
    FileObjList = []
    tuple = os.walk(RootDir)
    IgnoredPattern = GetIgnoredDirListPattern()
    ParseErrorFileList = []

    for dirpath, dirnames, filenames in tuple:
        if IgnoredPattern.match(dirpath.upper()):
            continue

        for Dir in dirnames:
            Dirname = os.path.join(dirpath, Dir)
            if os.path.islink(Dirname):
                Dirname = os.path.realpath(Dirname)
                if os.path.isdir(Dirname):
                    # symlinks to directories are treated as directories
                    dirnames.remove(Dir)
                    dirnames.append(Dirname)

        for f in filenames:
            if f.lower() in EccGlobalData.gConfig.SkipFileList:
                continue
            collector = None
            FullName = os.path.normpath(os.path.join(dirpath, f))
            model = DataClass.MODEL_FILE_OTHERS
            if os.path.splitext(f)[1] in ('.h', '.c'):
                EdkLogger.info("Parsing " + FullName)
                model = f.endswith('c') and DataClass.MODEL_FILE_C or DataClass.MODEL_FILE_H
                collector = CodeFragmentCollector.CodeFragmentCollector(FullName)
                try:
                    collector.ParseFile()
                except UnicodeError:
                    ParseErrorFileList.append(FullName)
                    collector.CleanFileProfileBuffer()
                    collector.ParseFileWithClearedPPDirective()
#                collector.PrintFragments()
            BaseName = os.path.basename(f)
            DirName = os.path.dirname(FullName)
            Ext = os.path.splitext(f)[1].lstrip('.')
            ModifiedTime = os.path.getmtime(FullName)
            FileObj = DataClass.FileClass(-1, BaseName, Ext, DirName, FullName, model, ModifiedTime, GetFunctionList(), GetIdentifierList(), [])
            FileObjList.append(FileObj)
            if collector:
                collector.CleanFileProfileBuffer()

    if len(ParseErrorFileList) > 0:
        EdkLogger.info("Found unrecoverable error during parsing:\n\t%s\n" % "\n\t".join(ParseErrorFileList))

    Db = GetDB()
    for file in FileObjList:
        if file.ExtName.upper() not in ['INF', 'DEC', 'DSC', 'FDF']:
            Db.InsertOneFile(file)

    Db.UpdateIdentifierBelongsToFunction()

def GetTableID(FullFileName, ErrorMsgList=None):
    if ErrorMsgList is None:
        ErrorMsgList = []

    Db = GetDB()
    SqlStatement = """ select ID
                       from File
                       where FullPath like '%s'
                   """ % FullFileName
    ResultSet = Db.TblFile.Exec(SqlStatement)

    FileID = -1
    for Result in ResultSet:
        if FileID != -1:
            ErrorMsgList.append('Duplicate file ID found in DB for file %s' % FullFileName)
            return - 2
        FileID = Result[0]
    if FileID == -1:
        ErrorMsgList.append('NO file ID found in DB for file %s' % FullFileName)
        return - 1
    return FileID

def GetIncludeFileList(FullFileName):
    if os.path.splitext(FullFileName)[1].upper() not in ('.H'):
        return []
    IFList = IncludeFileListDict.get(FullFileName)
    if IFList is not None:
        return IFList

    FileID = GetTableID(FullFileName)
    if FileID < 0:
        return []

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select Value
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_INCLUDE)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    IncludeFileListDict[FullFileName] = ResultSet
    return ResultSet

def GetFullPathOfIncludeFile(Str, IncludePathList):
    for IncludePath in IncludePathList:
        FullPath = os.path.join(IncludePath, Str)
        FullPath = os.path.normpath(FullPath)
        if os.path.exists(FullPath):
            return FullPath
    return None

def GetAllIncludeFiles(FullFileName):
    if AllIncludeFileListDict.get(FullFileName) is not None:
        return AllIncludeFileListDict.get(FullFileName)

    FileDirName = os.path.dirname(FullFileName)
    IncludePathList = IncludePathListDict.get(FileDirName)
    if IncludePathList is None:
        IncludePathList = MetaDataParser.GetIncludeListOfFile(EccGlobalData.gWorkspace, FullFileName, GetDB())
        if FileDirName not in IncludePathList:
            IncludePathList.insert(0, FileDirName)
        IncludePathListDict[FileDirName] = IncludePathList
    IncludeFileQueue = []
    for IncludeFile in GetIncludeFileList(FullFileName):
        FileName = IncludeFile[0].lstrip('#').strip()
        FileName = FileName.lstrip('include').strip()
        FileName = FileName.strip('\"')
        FileName = FileName.lstrip('<').rstrip('>').strip()
        FullPath = GetFullPathOfIncludeFile(FileName, IncludePathList)
        if FullPath is not None:
            IncludeFileQueue.append(FullPath)

    i = 0
    while i < len(IncludeFileQueue):
        for IncludeFile in GetIncludeFileList(IncludeFileQueue[i]):
            FileName = IncludeFile[0].lstrip('#').strip()
            FileName = FileName.lstrip('include').strip()
            FileName = FileName.strip('\"')
            FileName = FileName.lstrip('<').rstrip('>').strip()
            FullPath = GetFullPathOfIncludeFile(FileName, IncludePathList)
            if FullPath is not None and FullPath not in IncludeFileQueue:
                IncludeFileQueue.insert(i + 1, FullPath)
        i += 1

    AllIncludeFileListDict[FullFileName] = IncludeFileQueue
    return IncludeFileQueue

def GetPredicateListFromPredicateExpStr(PES):

    PredicateList = []
    i = 0
    PredicateBegin = 0
    #PredicateEnd = 0
    LogicOpPos = -1
    p = GetFuncDeclPattern()
    while i < len(PES) - 1:
        if (PES[i].isalnum() or PES[i] == '_' or PES[i] == '*') and LogicOpPos > PredicateBegin:
            PredicateBegin = i
        if (PES[i] == '&' and PES[i + 1] == '&') or (PES[i] == '|' and PES[i + 1] == '|'):
            LogicOpPos = i
            Exp = PES[PredicateBegin:i].strip()
            # Exp may contain '.' or '->'
            TmpExp = Exp.replace('.', '').replace('->', '')
            if p.match(TmpExp):
                PredicateList.append(Exp)
            else:
                PredicateList.append(Exp.rstrip(';').rstrip(')').strip())
        i += 1

    if PredicateBegin > LogicOpPos:
        while PredicateBegin < len(PES):
            if PES[PredicateBegin].isalnum() or PES[PredicateBegin] == '_' or PES[PredicateBegin] == '*':
                break
            PredicateBegin += 1
        Exp = PES[PredicateBegin:len(PES)].strip()
        # Exp may contain '.' or '->'
        TmpExp = Exp.replace('.', '').replace('->', '')
        if p.match(TmpExp):
            PredicateList.append(Exp)
        else:
            PredicateList.append(Exp.rstrip(';').rstrip(')').strip())
    return PredicateList

def GetCNameList(Lvalue, StarList=[]):
    Lvalue += ' '
    i = 0
    SearchBegin = 0
    VarStart = -1
    VarEnd = -1
    VarList = []

    while SearchBegin < len(Lvalue):
        while i < len(Lvalue):
            if Lvalue[i].isalnum() or Lvalue[i] == '_':
                if VarStart == -1:
                    VarStart = i
                VarEnd = i
                i += 1
            elif VarEnd != -1:
                VarList.append(Lvalue[VarStart:VarEnd + 1])
                i += 1
                break
            else:
                if VarStart == -1 and Lvalue[i] == '*':
                    StarList.append('*')
                i += 1
        if VarEnd == -1:
            break


        DotIndex = Lvalue[VarEnd:].find('.')
        ArrowIndex = Lvalue[VarEnd:].find('->')
        if DotIndex == -1 and ArrowIndex == -1:
            break
        elif DotIndex == -1 and ArrowIndex != -1:
            SearchBegin = VarEnd + ArrowIndex
        elif ArrowIndex == -1 and DotIndex != -1:
            SearchBegin = VarEnd + DotIndex
        else:
            SearchBegin = VarEnd + ((DotIndex < ArrowIndex) and DotIndex or ArrowIndex)

        i = SearchBegin
        VarStart = -1
        VarEnd = -1

    return VarList

def SplitPredicateByOp(Str, Op, IsFuncCalling=False):

    Name = Str.strip()
    Value = None

    if IsFuncCalling:
        Index = 0
        LBFound = False
        UnmatchedLBCount = 0
        while Index < len(Str):
            while not LBFound and Str[Index] != '_' and not Str[Index].isalnum():
                Index += 1

            while not LBFound and (Str[Index].isalnum() or Str[Index] == '_'):
                Index += 1
            # maybe type-cast at the begining, skip it.
            RemainingStr = Str[Index:].lstrip()
            if RemainingStr.startswith(')') and not LBFound:
                Index += 1
                continue

            if RemainingStr.startswith('(') and not LBFound:
                LBFound = True

            if Str[Index] == '(':
                UnmatchedLBCount += 1
                Index += 1
                continue

            if Str[Index] == ')':
                UnmatchedLBCount -= 1
                Index += 1
                if UnmatchedLBCount == 0:
                    break
                continue

            Index += 1

        if UnmatchedLBCount > 0:
            return [Name]

        IndexInRemainingStr = Str[Index:].find(Op)
        if IndexInRemainingStr == -1:
            return [Name]

        Name = Str[0:Index + IndexInRemainingStr].strip()
        Value = Str[Index + IndexInRemainingStr + len(Op):].strip().strip(')')
        return [Name, Value]

    TmpStr = Str.rstrip(';').rstrip(')')
    while True:
        Index = TmpStr.rfind(Op)
        if Index == -1:
            return [Name]

        if Str[Index - 1].isalnum() or Str[Index - 1].isspace() or Str[Index - 1] == ')' or Str[Index - 1] == ']':
            Name = Str[0:Index].strip()
            Value = Str[Index + len(Op):].strip()
            return [Name, Value]

        TmpStr = Str[0:Index - 1]

def SplitPredicateStr(Str):

    Str = Str.lstrip('(')
    IsFuncCalling = False
    p = GetFuncDeclPattern()
    TmpStr = Str.replace('.', '').replace('->', '')
    if p.match(TmpStr):
        IsFuncCalling = True

    PredPartList = SplitPredicateByOp(Str, '==', IsFuncCalling)
    if len(PredPartList) > 1:
        return [PredPartList, '==']

    PredPartList = SplitPredicateByOp(Str, '!=', IsFuncCalling)
    if len(PredPartList) > 1:
        return [PredPartList, '!=']

    PredPartList = SplitPredicateByOp(Str, '>=', IsFuncCalling)
    if len(PredPartList) > 1:
        return [PredPartList, '>=']

    PredPartList = SplitPredicateByOp(Str, '<=', IsFuncCalling)
    if len(PredPartList) > 1:
        return [PredPartList, '<=']

    PredPartList = SplitPredicateByOp(Str, '>', IsFuncCalling)
    if len(PredPartList) > 1:
        return [PredPartList, '>']

    PredPartList = SplitPredicateByOp(Str, '<', IsFuncCalling)
    if len(PredPartList) > 1:
        return [PredPartList, '<']

    return [[Str, None], None]

def GetFuncContainsPE(ExpLine, ResultSet):
    for Result in ResultSet:
        if Result[0] < ExpLine and Result[1] > ExpLine:
            return Result
    return None

def PatternInModifier(Modifier, SubStr):
    PartList = Modifier.split()
    for Part in PartList:
        if Part == SubStr:
            return True
    return False

def GetDataTypeFromModifier(ModifierStr):
    MList = ModifierStr.split()
    ReturnType = ''
    for M in MList:
        if M in EccGlobalData.gConfig.ModifierSet:
            continue
        # remove array sufix
        if M.startswith('[') or M.endswith(']'):
            continue
        ReturnType += M + ' '

    ReturnType = ReturnType.strip()
    if len(ReturnType) == 0:
        ReturnType = 'VOID'
    return ReturnType

def DiffModifier(Str1, Str2):
    PartList1 = Str1.split()
    PartList2 = Str2.split()
    if PartList1 == PartList2:
        return False
    else:
        return True

def GetTypedefDict(FullFileName):

    Dict = ComplexTypeDict.get(FullFileName)
    if Dict is not None:
        return Dict

    FileID = GetTableID(FullFileName)
    FileTable = 'Identifier' + str(FileID)
    Db = GetDB()
    SqlStatement = """ select Modifier, Name, Value, ID
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_TYPEDEF)
    ResultSet = Db.TblFile.Exec(SqlStatement)

    Dict = {}
    for Result in ResultSet:
        if len(Result[0]) == 0:
            Dict[Result[1]] = Result[2]

    IncludeFileList = GetAllIncludeFiles(FullFileName)
    for F in IncludeFileList:
        FileID = GetTableID(F)
        if FileID < 0:
            continue

        FileTable = 'Identifier' + str(FileID)
        SqlStatement = """ select Modifier, Name, Value, ID
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_TYPEDEF)
        ResultSet = Db.TblFile.Exec(SqlStatement)

        for Result in ResultSet:
            if not Result[2].startswith('FP ('):
                Dict[Result[1]] = Result[2]
            else:
                if len(Result[0]) == 0:
                    Dict[Result[1]] = 'VOID'
                else:
                    Dict[Result[1]] = GetDataTypeFromModifier(Result[0])

    ComplexTypeDict[FullFileName] = Dict
    return Dict

def GetSUDict(FullFileName):

    Dict = SUDict.get(FullFileName)
    if Dict is not None:
        return Dict

    FileID = GetTableID(FullFileName)
    FileTable = 'Identifier' + str(FileID)
    Db = GetDB()
    SqlStatement = """ select Name, Value, ID
                       from %s
                       where Model = %d or Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_STRUCTURE, DataClass.MODEL_IDENTIFIER_UNION)
    ResultSet = Db.TblFile.Exec(SqlStatement)

    Dict = {}
    for Result in ResultSet:
        if len(Result[1]) > 0:
            Dict[Result[0]] = Result[1]

    IncludeFileList = GetAllIncludeFiles(FullFileName)
    for F in IncludeFileList:
        FileID = GetTableID(F)
        if FileID < 0:
            continue

        FileTable = 'Identifier' + str(FileID)
        SqlStatement = """ select Name, Value, ID
                       from %s
                       where Model = %d or Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_STRUCTURE, DataClass.MODEL_IDENTIFIER_UNION)
        ResultSet = Db.TblFile.Exec(SqlStatement)

        for Result in ResultSet:
            if len(Result[1]) > 0:
                Dict[Result[0]] = Result[1]

    SUDict[FullFileName] = Dict
    return Dict

def StripComments(Str):
    Str += '   '
    ListFromStr = list(Str)

    InComment = False
    DoubleSlashComment = False
    Index = 0
    while Index < len(ListFromStr):
        # meet new line, then no longer in a comment for //
        if ListFromStr[Index] == '\n':
            if InComment and DoubleSlashComment:
                InComment = False
                DoubleSlashComment = False
            Index += 1
        # check for */ comment end
        elif InComment and not DoubleSlashComment and ListFromStr[Index] == '*' and ListFromStr[Index + 1] == '/':
            ListFromStr[Index] = ' '
            Index += 1
            ListFromStr[Index] = ' '
            Index += 1
            InComment = False
        # set comments to spaces
        elif InComment:
            ListFromStr[Index] = ' '
            Index += 1
        # check for // comment
        elif ListFromStr[Index] == '/' and ListFromStr[Index + 1] == '/' and ListFromStr[Index + 2] != '\n':
            InComment = True
            DoubleSlashComment = True

        # check for /* comment start
        elif ListFromStr[Index] == '/' and ListFromStr[Index + 1] == '*':
            ListFromStr[Index] = ' '
            Index += 1
            ListFromStr[Index] = ' '
            Index += 1
            InComment = True
        else:
            Index += 1

    # restore from List to String
    Str = "".join(ListFromStr)
    Str = Str.rstrip(' ')

    return Str

def GetFinalTypeValue(Type, FieldName, TypedefDict, SUDict):
    Value = TypedefDict.get(Type)
    if Value is None:
        Value = SUDict.get(Type)
    if Value is None:
        return None

    LBPos = Value.find('{')
    while LBPos == -1:
        FTList = Value.split()
        for FT in FTList:
            if FT not in ('struct', 'union'):
                Value = TypedefDict.get(FT)
                if Value is None:
                    Value = SUDict.get(FT)
                break

        if Value is None:
            return None

        LBPos = Value.find('{')

#    RBPos = Value.find('}')
    Fields = Value[LBPos + 1:]
    Fields = StripComments(Fields)
    FieldsList = Fields.split(';')
    for Field in FieldsList:
        Field = Field.strip()
        Index = Field.rfind(FieldName)
        if Index < 1:
            continue
        if not Field[Index - 1].isalnum():
            if Index + len(FieldName) == len(Field):
                Type = GetDataTypeFromModifier(Field[0:Index])
                return Type.strip()
            else:
            # For the condition that the field in struct is an array with [] sufixes...
                if not Field[Index + len(FieldName)].isalnum():
                    Type = GetDataTypeFromModifier(Field[0:Index])
                    return Type.strip()

    return None

def GetRealType(Type, TypedefDict, TargetType=None):
    if TargetType is not None and Type == TargetType:
            return Type
    while TypedefDict.get(Type):
        Type = TypedefDict.get(Type)
        if TargetType is not None and Type == TargetType:
            return Type
    return Type

def GetTypeInfo(RefList, Modifier, FullFileName, TargetType=None):
    TypedefDict = GetTypedefDict(FullFileName)
    SUDict = GetSUDict(FullFileName)
    Type = GetDataTypeFromModifier(Modifier).replace('*', '').strip()

    Type = Type.split()[-1]
    Index = 0
    while Index < len(RefList):
        FieldName = RefList[Index]
        FromType = GetFinalTypeValue(Type, FieldName, TypedefDict, SUDict)
        if FromType is None:
            return None
        # we want to determine the exact type.
        if TargetType is not None:
            Type = FromType.split()[0]
        # we only want to check if it is a pointer
        else:
            Type = FromType
            if Type.find('*') != -1 and Index == len(RefList) - 1:
                return Type
            Type = FromType.split()[0]

        Index += 1

    Type = GetRealType(Type, TypedefDict, TargetType)

    return Type

def GetVarInfo(PredVarList, FuncRecord, FullFileName, IsFuncCall=False, TargetType=None, StarList=None):

    PredVar = PredVarList[0]
    FileID = GetTableID(FullFileName)

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    # search variable in include files

    # it is a function call, search function declarations and definitions
    if IsFuncCall:
        SqlStatement = """ select Modifier, ID
                       from %s
                       where Model = %d and Value = \'%s\'
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_FUNCTION_DECLARATION, PredVar)
        ResultSet = Db.TblFile.Exec(SqlStatement)

        for Result in ResultSet:
            Type = GetDataTypeFromModifier(Result[0]).split()[-1]
            TypedefDict = GetTypedefDict(FullFileName)
            Type = GetRealType(Type, TypedefDict, TargetType)
            return Type

        IncludeFileList = GetAllIncludeFiles(FullFileName)
        for F in IncludeFileList:
            FileID = GetTableID(F)
            if FileID < 0:
                continue

            FileTable = 'Identifier' + str(FileID)
            SqlStatement = """ select Modifier, ID
                           from %s
                           where Model = %d and Value = \'%s\'
                       """ % (FileTable, DataClass.MODEL_IDENTIFIER_FUNCTION_DECLARATION, PredVar)
            ResultSet = Db.TblFile.Exec(SqlStatement)

            for Result in ResultSet:
                Type = GetDataTypeFromModifier(Result[0]).split()[-1]
                TypedefDict = GetTypedefDict(FullFileName)
                Type = GetRealType(Type, TypedefDict, TargetType)
                return Type

        FileID = GetTableID(FullFileName)
        SqlStatement = """ select Modifier, ID
                       from Function
                       where BelongsToFile = %d and Name = \'%s\'
                   """ % (FileID, PredVar)
        ResultSet = Db.TblFile.Exec(SqlStatement)

        for Result in ResultSet:
            Type = GetDataTypeFromModifier(Result[0]).split()[-1]
            TypedefDict = GetTypedefDict(FullFileName)
            Type = GetRealType(Type, TypedefDict, TargetType)
            return Type

        for F in IncludeFileList:
            FileID = GetTableID(F)
            if FileID < 0:
                continue

            FileTable = 'Identifier' + str(FileID)
            SqlStatement = """ select Modifier, ID
                           from Function
                           where BelongsToFile = %d and Name = \'%s\'
                       """ % (FileID, PredVar)
            ResultSet = Db.TblFile.Exec(SqlStatement)

            for Result in ResultSet:
                Type = GetDataTypeFromModifier(Result[0]).split()[-1]
                TypedefDict = GetTypedefDict(FullFileName)
                Type = GetRealType(Type, TypedefDict, TargetType)
                return Type

        return None

    # really variable, search local variable first
    SqlStatement = """ select Modifier, ID
                       from %s
                       where Model = %d and Name = \'%s\' and StartLine >= %d and StartLine <= %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_VARIABLE, PredVar, FuncRecord[0], FuncRecord[1])
    ResultSet = Db.TblFile.Exec(SqlStatement)
    VarFound = False
    for Result in ResultSet:
        if len(PredVarList) > 1:
            Type = GetTypeInfo(PredVarList[1:], Result[0], FullFileName, TargetType)
            return Type
        else:
#            Type = GetDataTypeFromModifier(Result[0]).split()[-1]
            TypeList = GetDataTypeFromModifier(Result[0]).split()
            Type = TypeList[-1]
            if len(TypeList) > 1 and StarList is not None:
                for Star in StarList:
                    Type = Type.strip()
                    Type = Type.rstrip(Star)
                # Get real type after de-reference pointers.
                if len(Type.strip()) == 0:
                    Type = TypeList[-2]
            TypedefDict = GetTypedefDict(FullFileName)
            Type = GetRealType(Type, TypedefDict, TargetType)
            return Type

    # search function parameters second
    ParamList = GetParamList(FuncRecord[2])
    for Param in ParamList:
        if Param.Name.strip() == PredVar:
            if len(PredVarList) > 1:
                Type = GetTypeInfo(PredVarList[1:], Param.Modifier, FullFileName, TargetType)
                return Type
            else:
                TypeList = GetDataTypeFromModifier(Param.Modifier).split()
                Type = TypeList[-1]
                if Type == '*' and len(TypeList) >= 2:
                    Type = TypeList[-2]
                if len(TypeList) > 1 and StarList is not None:
                    for Star in StarList:
                        Type = Type.strip()
                        Type = Type.rstrip(Star)
                    # Get real type after de-reference pointers.
                    if len(Type.strip()) == 0:
                        Type = TypeList[-2]
                TypedefDict = GetTypedefDict(FullFileName)
                Type = GetRealType(Type, TypedefDict, TargetType)
                return Type

    # search global variable next
    SqlStatement = """ select Modifier, ID
           from %s
           where Model = %d and Name = \'%s\' and BelongsToFunction = -1
       """ % (FileTable, DataClass.MODEL_IDENTIFIER_VARIABLE, PredVar)
    ResultSet = Db.TblFile.Exec(SqlStatement)

    for Result in ResultSet:
        if len(PredVarList) > 1:
            Type = GetTypeInfo(PredVarList[1:], Result[0], FullFileName, TargetType)
            return Type
        else:
            TypeList = GetDataTypeFromModifier(Result[0]).split()
            Type = TypeList[-1]
            if len(TypeList) > 1 and StarList is not None:
                for Star in StarList:
                    Type = Type.strip()
                    Type = Type.rstrip(Star)
                # Get real type after de-reference pointers.
                if len(Type.strip()) == 0:
                    Type = TypeList[-2]
            TypedefDict = GetTypedefDict(FullFileName)
            Type = GetRealType(Type, TypedefDict, TargetType)
            return Type

    IncludeFileList = GetAllIncludeFiles(FullFileName)
    for F in IncludeFileList:
        FileID = GetTableID(F)
        if FileID < 0:
            continue

        FileTable = 'Identifier' + str(FileID)
        SqlStatement = """ select Modifier, ID
                       from %s
                       where Model = %d and BelongsToFunction = -1 and Name = \'%s\'
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_VARIABLE, PredVar)
        ResultSet = Db.TblFile.Exec(SqlStatement)

        for Result in ResultSet:
            if len(PredVarList) > 1:
                Type = GetTypeInfo(PredVarList[1:], Result[0], FullFileName, TargetType)
                return Type
            else:
                TypeList = GetDataTypeFromModifier(Result[0]).split()
                Type = TypeList[-1]
                if len(TypeList) > 1 and StarList is not None:
                    for Star in StarList:
                        Type = Type.strip()
                        Type = Type.rstrip(Star)
                    # Get real type after de-reference pointers.
                    if len(Type.strip()) == 0:
                        Type = TypeList[-2]
                TypedefDict = GetTypedefDict(FullFileName)
                Type = GetRealType(Type, TypedefDict, TargetType)
                return Type

def GetTypeFromArray(Type, Var):
    Count = Var.count('[')

    while Count > 0:
        Type = Type.strip()
        Type = Type.rstrip('*')
        Count = Count - 1

    return Type

def CheckFuncLayoutReturnType(FullFileName):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select Modifier, ID, StartLine, StartColumn, EndLine, Value
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_FUNCTION_DECLARATION)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    for Result in ResultSet:
        ReturnType = GetDataTypeFromModifier(Result[0])
        TypeStart = ReturnType.split()[0]
        FuncName = Result[5]
        if EccGlobalData.gException.IsException(ERROR_C_FUNCTION_LAYOUT_CHECK_RETURN_TYPE, FuncName):
            continue
        Result0 = Result[0]
        if Result0.upper().startswith('STATIC'):
            Result0 = Result0[6:].strip()
        Index = Result0.find(TypeStart)
        if Index != 0 or Result[3] != 0:
            PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_RETURN_TYPE, '[%s] Return Type should appear at the start of line' % FuncName, FileTable, Result[1])

        if Result[2] == Result[4]:
            PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_RETURN_TYPE, '[%s] Return Type should appear on its own line' % FuncName, FileTable, Result[1])

    SqlStatement = """ select Modifier, ID, StartLine, StartColumn, FunNameStartLine, Name
                       from Function
                       where BelongsToFile = %d
                   """ % (FileID)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    for Result in ResultSet:
        ReturnType = GetDataTypeFromModifier(Result[0])
        TypeStart = ReturnType.split()[0]
        FuncName = Result[5]
        if EccGlobalData.gException.IsException(ERROR_C_FUNCTION_LAYOUT_CHECK_RETURN_TYPE, FuncName):
            continue
        Result0 = Result[0]
        if Result0.upper().startswith('STATIC'):
            Result0 = Result0[6:].strip()
        Index = Result0.find(ReturnType)
        if Index != 0 or Result[3] != 0:
            PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_RETURN_TYPE, '[%s] Return Type should appear at the start of line' % FuncName, 'Function', Result[1])

def CheckFuncLayoutModifier(FullFileName):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select Modifier, ID
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_FUNCTION_DECLARATION)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    for Result in ResultSet:
        ReturnType = GetDataTypeFromModifier(Result[0])
        TypeStart = ReturnType.split()[0]
        Result0 = Result[0]
        if Result0.upper().startswith('STATIC'):
            Result0 = Result0[6:].strip()
        Index = Result0.find(TypeStart)
        if Index != 0:
            PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_OPTIONAL_FUNCTIONAL_MODIFIER, '', FileTable, Result[1])

    SqlStatement = """ select Modifier, ID
                       from Function
                       where BelongsToFile = %d
                   """ % (FileID)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    for Result in ResultSet:
        ReturnType = GetDataTypeFromModifier(Result[0])
        TypeStart = ReturnType.split()[0]
        Result0 = Result[0]
        if Result0.upper().startswith('STATIC'):
            Result0 = Result0[6:].strip()
        Index = Result0.find(TypeStart)
        if Index != 0:
            PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_OPTIONAL_FUNCTIONAL_MODIFIER, '', 'Function', Result[1])

def CheckFuncLayoutName(FullFileName):
    ErrorMsgList = []
    # Parameter variable format pattern.
    Pattern = re.compile(r'^[A-Z]+\S*[a-z]\S*$')
    ParamIgnoreList = ('VOID', '...')
    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select Name, ID, EndColumn, Value
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_FUNCTION_DECLARATION)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    for Result in ResultSet:
        FuncName = Result[3]
        if EccGlobalData.gException.IsException(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_NAME, FuncName):
            continue
        if Result[2] != 0:
            PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_NAME, 'Function name [%s] should appear at the start of a line' % FuncName, FileTable, Result[1])
        ParamList = GetParamList(Result[0])
        if len(ParamList) == 0:
            continue
        StartLine = 0
        for Param in ParamList:
            if Param.StartLine <= StartLine:
                PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_NAME, 'Parameter %s should be in its own line.' % Param.Name, FileTable, Result[1])
            if Param.StartLine - StartLine > 1:
                PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_NAME, 'Empty line appears before Parameter %s.' % Param.Name, FileTable, Result[1])
            if not Pattern.match(Param.Name) and not Param.Name in ParamIgnoreList and not EccGlobalData.gException.IsException(ERROR_NAMING_CONVENTION_CHECK_VARIABLE_NAME, Param.Name):
                PrintErrorMsg(ERROR_NAMING_CONVENTION_CHECK_VARIABLE_NAME, 'Parameter [%s] NOT follow naming convention.' % Param.Name, FileTable, Result[1])
            StartLine = Param.StartLine

        if not Result[0].endswith('\n  )') and not Result[0].endswith('\r  )'):
            PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_NAME, '\')\' should be on a new line and indented two spaces', FileTable, Result[1])

    SqlStatement = """ select Modifier, ID, FunNameStartColumn, Name
                       from Function
                       where BelongsToFile = %d
                   """ % (FileID)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    for Result in ResultSet:
        FuncName = Result[3]
        if EccGlobalData.gException.IsException(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_NAME, FuncName):
            continue
        if Result[2] != 0:
            PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_NAME, 'Function name [%s] should appear at the start of a line' % FuncName, 'Function', Result[1])
        ParamList = GetParamList(Result[0])
        if len(ParamList) == 0:
            continue
        StartLine = 0
        for Param in ParamList:
            if Param.StartLine <= StartLine:
                PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_NAME, 'Parameter %s should be in its own line.' % Param.Name, 'Function', Result[1])
            if Param.StartLine - StartLine > 1:
                PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_NAME, 'Empty line appears before Parameter %s.' % Param.Name, 'Function', Result[1])
            if not Pattern.match(Param.Name) and not Param.Name in ParamIgnoreList and not EccGlobalData.gException.IsException(ERROR_NAMING_CONVENTION_CHECK_VARIABLE_NAME, Param.Name):
                PrintErrorMsg(ERROR_NAMING_CONVENTION_CHECK_VARIABLE_NAME, 'Parameter [%s] NOT follow naming convention.' % Param.Name, FileTable, Result[1])
            StartLine = Param.StartLine
        if not Result[0].endswith('\n  )') and not Result[0].endswith('\r  )'):
            PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_NAME, '\')\' should be on a new line and indented two spaces', 'Function', Result[1])

def CheckFuncLayoutPrototype(FullFileName):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    FileTable = 'Identifier' + str(FileID)
    Db = GetDB()
    SqlStatement = """ select Modifier, Header, Name, ID
                       from Function
                       where BelongsToFile = %d
                   """ % (FileID)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    if len(ResultSet) == 0:
        return ErrorMsgList

    FuncDefList = []
    for Result in ResultSet:
        FuncDefList.append(Result)

    SqlStatement = """ select Modifier, Name, ID
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_FUNCTION_DECLARATION)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    FuncDeclList = []
    for Result in ResultSet:
        FuncDeclList.append(Result)

    UndeclFuncList = []
    for FuncDef in FuncDefList:
        FuncName = FuncDef[2].strip()
        FuncModifier = FuncDef[0]
        FuncDefHeader = FuncDef[1]
        for FuncDecl in FuncDeclList:
            LBPos = FuncDecl[1].find('(')
            DeclName = FuncDecl[1][0:LBPos].strip()
            DeclModifier = FuncDecl[0]
            if DeclName == FuncName:
                if DiffModifier(FuncModifier, DeclModifier) and not EccGlobalData.gException.IsException(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_PROTO_TYPE, FuncName):
                    PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_PROTO_TYPE, 'Function [%s] modifier different with prototype.' % FuncName, 'Function', FuncDef[3])
                ParamListOfDef = GetParamList(FuncDefHeader)
                ParamListOfDecl = GetParamList(FuncDecl[1])
                if len(ParamListOfDef) != len(ParamListOfDecl) and not EccGlobalData.gException.IsException(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_PROTO_TYPE_2, FuncName):
                    PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_PROTO_TYPE_2, 'Parameter number different in function [%s].' % FuncName, 'Function', FuncDef[3])
                    break

                Index = 0
                while Index < len(ParamListOfDef):
                    if DiffModifier(ParamListOfDef[Index].Modifier, ParamListOfDecl[Index].Modifier) and not EccGlobalData.gException.IsException(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_PROTO_TYPE_3, FuncName):
                        PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_PROTO_TYPE_3, 'Parameter %s has different modifier with prototype in function [%s].' % (ParamListOfDef[Index].Name, FuncName), 'Function', FuncDef[3])
                    Index += 1
                break
        else:
            UndeclFuncList.append(FuncDef)

    IncludeFileList = GetAllIncludeFiles(FullFileName)
    FuncDeclList = []
    for F in IncludeFileList:
        FileID = GetTableID(F, ErrorMsgList)
        if FileID < 0:
            continue

        FileTable = 'Identifier' + str(FileID)
        SqlStatement = """ select Modifier, Name, ID
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_FUNCTION_DECLARATION)
        ResultSet = Db.TblFile.Exec(SqlStatement)

        for Result in ResultSet:
            FuncDeclList.append(Result)

    for FuncDef in UndeclFuncList:
        FuncName = FuncDef[2].strip()
        FuncModifier = FuncDef[0]
        FuncDefHeader = FuncDef[1]
        for FuncDecl in FuncDeclList:
            LBPos = FuncDecl[1].find('(')
            DeclName = FuncDecl[1][0:LBPos].strip()
            DeclModifier = FuncDecl[0]
            if DeclName == FuncName:
                if DiffModifier(FuncModifier, DeclModifier) and not EccGlobalData.gException.IsException(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_PROTO_TYPE, FuncName):
                    PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_PROTO_TYPE, 'Function [%s] modifier different with prototype.' % FuncName, 'Function', FuncDef[3])
                ParamListOfDef = GetParamList(FuncDefHeader)
                ParamListOfDecl = GetParamList(FuncDecl[1])
                if len(ParamListOfDef) != len(ParamListOfDecl) and not EccGlobalData.gException.IsException(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_PROTO_TYPE_2, FuncName):
                    PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_PROTO_TYPE_2, 'Parameter number different in function [%s].' % FuncName, 'Function', FuncDef[3])
                    break

                Index = 0
                while Index < len(ParamListOfDef):
                    if DiffModifier(ParamListOfDef[Index].Modifier, ParamListOfDecl[Index].Modifier) and not EccGlobalData.gException.IsException(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_PROTO_TYPE_3, FuncName):
                        PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_PROTO_TYPE_3, 'Parameter %s has different modifier with prototype in function [%s].' % (ParamListOfDef[Index].Name, FuncName), 'Function', FuncDef[3])
                    Index += 1
                break

def CheckFuncLayoutBody(FullFileName):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    FileTable = 'Identifier' + str(FileID)
    Db = GetDB()
    SqlStatement = """ select BodyStartColumn, EndColumn, ID
                       from Function
                       where BelongsToFile = %d
                   """ % (FileID)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    if len(ResultSet) == 0:
        return ErrorMsgList
    for Result in ResultSet:
        if Result[0] != 0:
            PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_BODY, 'open brace should be at the very beginning of a line.', 'Function', Result[2])
        if Result[1] != 0:
            PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_BODY, 'close brace should be at the very beginning of a line.', 'Function', Result[2])

def CheckFuncLayoutLocalVariable(FullFileName):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select ID
                       from Function
                       where BelongsToFile = %d
                   """ % (FileID)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    if len(ResultSet) == 0:
        return ErrorMsgList
    FL = []
    for Result in ResultSet:
        FL.append(Result)

    for F in FL:
        SqlStatement = """ select Name, Value, ID, Modifier
                       from %s
                       where Model = %d and BelongsToFunction = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_VARIABLE, F[0])
        ResultSet = Db.TblFile.Exec(SqlStatement)
        if len(ResultSet) == 0:
            continue

        for Result in ResultSet:
            if len(Result[1]) > 0 and 'CONST' not in Result[3]:
                PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_NO_INIT_OF_VARIABLE, 'Variable Name: %s' % Result[0], FileTable, Result[2])

def CheckMemberVariableFormat(Name, Value, FileTable, TdId, ModelId):
    ErrMsgList = []
    # Member variable format pattern.
    Pattern = re.compile(r'^[A-Z]+\S*[a-z]\S*$')

    LBPos = Value.find('{')
    RBPos = Value.rfind('}')
    if LBPos == -1 or RBPos == -1:
        return ErrMsgList

    Fields = Value[LBPos + 1 : RBPos]
    Fields = StripComments(Fields).strip()
    NestPos = Fields.find ('struct')
    if NestPos != -1 and (NestPos + len('struct') < len(Fields)) and ModelId != DataClass.MODEL_IDENTIFIER_UNION:
        if not Fields[NestPos + len('struct') + 1].isalnum():
            if not EccGlobalData.gException.IsException(ERROR_DECLARATION_DATA_TYPE_CHECK_NESTED_STRUCTURE, Name):
                PrintErrorMsg(ERROR_DECLARATION_DATA_TYPE_CHECK_NESTED_STRUCTURE, 'Nested struct in [%s].' % (Name), FileTable, TdId)
            return ErrMsgList
    NestPos = Fields.find ('union')
    if NestPos != -1 and (NestPos + len('union') < len(Fields)):
        if not Fields[NestPos + len('union') + 1].isalnum():
            if not EccGlobalData.gException.IsException(ERROR_DECLARATION_DATA_TYPE_CHECK_NESTED_STRUCTURE, Name):
                PrintErrorMsg(ERROR_DECLARATION_DATA_TYPE_CHECK_NESTED_STRUCTURE, 'Nested union in [%s].' % (Name), FileTable, TdId)
            return ErrMsgList
    NestPos = Fields.find ('enum')
    if NestPos != -1 and (NestPos + len('enum') < len(Fields)):
        if not Fields[NestPos + len('enum') + 1].isalnum():
            if not EccGlobalData.gException.IsException(ERROR_DECLARATION_DATA_TYPE_CHECK_NESTED_STRUCTURE, Name):
                PrintErrorMsg(ERROR_DECLARATION_DATA_TYPE_CHECK_NESTED_STRUCTURE, 'Nested enum in [%s].' % (Name), FileTable, TdId)
            return ErrMsgList

    if ModelId == DataClass.MODEL_IDENTIFIER_ENUMERATE:
        FieldsList = Fields.split(',')
        # deal with enum is pre-assigned a value by function call ( , , , ...)
        QuoteCount = 0
        Index = 0
        RemoveCurrentElement = False
        while Index < len(FieldsList):
            Field = FieldsList[Index]

            if Field.find('(') != -1:
                QuoteCount += 1
                RemoveCurrentElement = True
                Index += 1
                continue

            if Field.find(')') != -1 and QuoteCount > 0:
                QuoteCount -= 1

            if RemoveCurrentElement:
                FieldsList.remove(Field)
                if QuoteCount == 0:
                    RemoveCurrentElement = False
                continue

            if QuoteCount == 0:
                RemoveCurrentElement = False

            Index += 1
    else:
        FieldsList = Fields.split(';')

    for Field in FieldsList:
        Field = Field.strip()
        if Field == '':
            continue
        # For the condition that the field in struct is an array with [] sufixes...
        if Field[-1] == ']':
            LBPos = Field.find('[')
            Field = Field[0:LBPos]
        # For the condition that bit field ": Number"
        if Field.find(':') != -1:
            ColonPos = Field.find(':')
            Field = Field[0:ColonPos]

        Field = Field.strip()
        if Field == '':
            continue
        if Field.startswith("#"):
            continue
        # Enum could directly assign value to variable
        Field = Field.split('=')[0].strip()
        TokenList = Field.split()
        # Remove pointers before variable
        Token = TokenList[-1]
        if Token in ['OPTIONAL']:
            Token = TokenList[-2]
        if not Pattern.match(Token.lstrip('*')):
            ErrMsgList.append(Token.lstrip('*'))

    return ErrMsgList

def CheckDeclTypedefFormat(FullFileName, ModelId):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select Name, StartLine, EndLine, ID, Value
                       from %s
                       where Model = %d
                   """ % (FileTable, ModelId)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    ResultList = []
    for Result in ResultSet:
        ResultList.append(Result)

    ErrorType = ERROR_DECLARATION_DATA_TYPE_CHECK_ALL
    if ModelId == DataClass.MODEL_IDENTIFIER_STRUCTURE:
        ErrorType = ERROR_DECLARATION_DATA_TYPE_CHECK_STRUCTURE_DECLARATION
    elif ModelId == DataClass.MODEL_IDENTIFIER_ENUMERATE:
        ErrorType = ERROR_DECLARATION_DATA_TYPE_CHECK_ENUMERATED_TYPE
    elif ModelId == DataClass.MODEL_IDENTIFIER_UNION:
        ErrorType = ERROR_DECLARATION_DATA_TYPE_CHECK_UNION_TYPE

    SqlStatement = """ select Modifier, Name, Value, StartLine, EndLine, ID
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_TYPEDEF)
    TdSet = Db.TblFile.Exec(SqlStatement)
    TdList = []
    for Td in TdSet:
        TdList.append(Td)
    # Check member variable name format that from typedefs of ONLY this file.
    for Td in TdList:
        Name = Td[1].strip()
        Value = Td[2].strip()
        if Value.startswith('enum'):
            ValueModelId = DataClass.MODEL_IDENTIFIER_ENUMERATE
        elif Value.startswith('struct'):
            ValueModelId = DataClass.MODEL_IDENTIFIER_STRUCTURE
        elif Value.startswith('union'):
            ValueModelId = DataClass.MODEL_IDENTIFIER_UNION
        else:
            continue

        if ValueModelId != ModelId:
            continue
        # Check member variable format.
        ErrMsgList = CheckMemberVariableFormat(Name, Value, FileTable, Td[5], ModelId)
        for ErrMsg in ErrMsgList:
            if EccGlobalData.gException.IsException(ERROR_NAMING_CONVENTION_CHECK_VARIABLE_NAME, Name + '.' + ErrMsg):
                continue
            PrintErrorMsg(ERROR_NAMING_CONVENTION_CHECK_VARIABLE_NAME, 'Member variable [%s] NOT follow naming convention.' % (Name + '.' + ErrMsg), FileTable, Td[5])

    # First check in current file to see whether struct/union/enum is typedef-ed.
    UntypedefedList = []
    for Result in ResultList:
        # Check member variable format.
        Name = Result[0].strip()
        Value = Result[4].strip()
        if Value.startswith('enum'):
            ValueModelId = DataClass.MODEL_IDENTIFIER_ENUMERATE
        elif Value.startswith('struct'):
            ValueModelId = DataClass.MODEL_IDENTIFIER_STRUCTURE
        elif Value.startswith('union'):
            ValueModelId = DataClass.MODEL_IDENTIFIER_UNION
        else:
            continue

        if ValueModelId != ModelId:
            continue
        ErrMsgList = CheckMemberVariableFormat(Name, Value, FileTable, Result[3], ModelId)
        for ErrMsg in ErrMsgList:
            if EccGlobalData.gException.IsException(ERROR_NAMING_CONVENTION_CHECK_VARIABLE_NAME, Result[0] + '.' + ErrMsg):
                continue
            PrintErrorMsg(ERROR_NAMING_CONVENTION_CHECK_VARIABLE_NAME, 'Member variable [%s] NOT follow naming convention.' % (Result[0] + '.' + ErrMsg), FileTable, Result[3])
        # Check whether it is typedefed.
        Found = False
        for Td in TdList:
            # skip function pointer
            if len(Td[0]) > 0:
                continue
            if Result[1] >= Td[3] and Td[4] >= Result[2]:
                Found = True
                if not Td[1].isupper():
                    PrintErrorMsg(ErrorType, 'Typedef should be UPPER case', FileTable, Td[5])
            if Result[0] in Td[2].split():
                Found = True
                if not Td[1].isupper():
                    PrintErrorMsg(ErrorType, 'Typedef should be UPPER case', FileTable, Td[5])
            if Found:
                break

        if not Found:
            UntypedefedList.append(Result)
            continue

    if len(UntypedefedList) == 0:
        return

    IncludeFileList = GetAllIncludeFiles(FullFileName)
    TdList = []
    for F in IncludeFileList:
        FileID = GetTableID(F, ErrorMsgList)
        if FileID < 0:
            continue

        IncludeFileTable = 'Identifier' + str(FileID)
        SqlStatement = """ select Modifier, Name, Value, StartLine, EndLine, ID
                       from %s
                       where Model = %d
                   """ % (IncludeFileTable, DataClass.MODEL_IDENTIFIER_TYPEDEF)
        ResultSet = Db.TblFile.Exec(SqlStatement)
        TdList.extend(ResultSet)

    for Result in UntypedefedList:

        # Check whether it is typedefed.
        Found = False
        for Td in TdList:

            if len(Td[0]) > 0:
                continue
            if Result[1] >= Td[3] and Td[4] >= Result[2]:
                Found = True
                if not Td[1].isupper():
                    PrintErrorMsg(ErrorType, 'Typedef should be UPPER case', FileTable, Td[5])
            if Result[0] in Td[2].split():
                Found = True
                if not Td[1].isupper():
                    PrintErrorMsg(ErrorType, 'Typedef should be UPPER case', FileTable, Td[5])
            if Found:
                break

        if not Found:
            PrintErrorMsg(ErrorType, 'No Typedef for %s' % Result[0], FileTable, Result[3])
            continue

def CheckDeclStructTypedef(FullFileName):
    CheckDeclTypedefFormat(FullFileName, DataClass.MODEL_IDENTIFIER_STRUCTURE)

def CheckDeclEnumTypedef(FullFileName):
    CheckDeclTypedefFormat(FullFileName, DataClass.MODEL_IDENTIFIER_ENUMERATE)

def CheckDeclUnionTypedef(FullFileName):
    CheckDeclTypedefFormat(FullFileName, DataClass.MODEL_IDENTIFIER_UNION)

def CheckDeclArgModifier(FullFileName):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select Modifier, Name, ID
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_VARIABLE)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    ModifierTuple = ('IN', 'OUT', 'OPTIONAL', 'UNALIGNED')
    MAX_MODIFIER_LENGTH = 100
    for Result in ResultSet:
        for Modifier in ModifierTuple:
            if PatternInModifier(Result[0], Modifier) and len(Result[0]) < MAX_MODIFIER_LENGTH:
                PrintErrorMsg(ERROR_DECLARATION_DATA_TYPE_CHECK_IN_OUT_MODIFIER, 'Variable Modifier %s' % Result[0], FileTable, Result[2])
                break

    SqlStatement = """ select Modifier, Name, ID
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_FUNCTION_DECLARATION)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    for Result in ResultSet:
        for Modifier in ModifierTuple:
            if PatternInModifier(Result[0], Modifier):
                PrintErrorMsg(ERROR_DECLARATION_DATA_TYPE_CHECK_IN_OUT_MODIFIER, 'Return Type Modifier %s' % Result[0], FileTable, Result[2])
                break

    SqlStatement = """ select Modifier, Header, ID
                       from Function
                       where BelongsToFile = %d
                   """ % (FileID)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    for Result in ResultSet:
        for Modifier in ModifierTuple:
            if PatternInModifier(Result[0], Modifier):
                PrintErrorMsg(ERROR_DECLARATION_DATA_TYPE_CHECK_IN_OUT_MODIFIER, 'Return Type Modifier %s' % Result[0], FileTable, Result[2])
                break

def CheckDeclNoUseCType(FullFileName):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select Modifier, Name, ID
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_VARIABLE)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    CTypeTuple = ('int', 'unsigned', 'char', 'void', 'static', 'long')
    for Result in ResultSet:
        for Type in CTypeTuple:
            if PatternInModifier(Result[0], Type):
                if EccGlobalData.gException.IsException(ERROR_DECLARATION_DATA_TYPE_CHECK_NO_USE_C_TYPE,
                                                        Result[0] + ' ' + Result[1]):
                    continue
                PrintErrorMsg(ERROR_DECLARATION_DATA_TYPE_CHECK_NO_USE_C_TYPE,
                              'Invalid variable type (%s) in definition [%s]' % (Type, Result[0] + ' ' + Result[1]),
                              FileTable,
                              Result[2])
                break

    SqlStatement = """ select Modifier, Name, ID, Value
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_FUNCTION_DECLARATION)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    for Result in ResultSet:
        ParamList = GetParamList(Result[1])
        FuncName = Result[3]
        if EccGlobalData.gException.IsException(ERROR_DECLARATION_DATA_TYPE_CHECK_NO_USE_C_TYPE, FuncName):
            continue
        for Type in CTypeTuple:
            if PatternInModifier(Result[0], Type):
                PrintErrorMsg(ERROR_DECLARATION_DATA_TYPE_CHECK_NO_USE_C_TYPE, '%s Return type %s' % (FuncName, Result[0]), FileTable, Result[2])

            for Param in ParamList:
                if PatternInModifier(Param.Modifier, Type):
                    PrintErrorMsg(ERROR_DECLARATION_DATA_TYPE_CHECK_NO_USE_C_TYPE, 'Parameter %s' % Param.Name, FileTable, Result[2])

    SqlStatement = """ select Modifier, Header, ID, Name
                       from Function
                       where BelongsToFile = %d
                   """ % (FileID)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    for Result in ResultSet:
        ParamList = GetParamList(Result[1])
        FuncName = Result[3]
        if EccGlobalData.gException.IsException(ERROR_DECLARATION_DATA_TYPE_CHECK_NO_USE_C_TYPE, FuncName):
            continue
        for Type in CTypeTuple:
            if PatternInModifier(Result[0], Type):
                PrintErrorMsg(ERROR_DECLARATION_DATA_TYPE_CHECK_NO_USE_C_TYPE, '[%s] Return type %s' % (FuncName, Result[0]), FileTable, Result[2])

            for Param in ParamList:
                if PatternInModifier(Param.Modifier, Type):
                    PrintErrorMsg(ERROR_DECLARATION_DATA_TYPE_CHECK_NO_USE_C_TYPE, 'Parameter %s' % Param.Name, FileTable, Result[2])


def CheckPointerNullComparison(FullFileName):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    # cache the found function return type to accelerate later checking in this file.
    FuncReturnTypeDict = {}

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select Value, StartLine, ID
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_PREDICATE_EXPRESSION)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    if len(ResultSet) == 0:
        return
    PSL = []
    for Result in ResultSet:
        PSL.append([Result[0], Result[1], Result[2]])

    SqlStatement = """ select BodyStartLine, EndLine, Header, Modifier, ID
                       from Function
                       where BelongsToFile = %d
                   """ % (FileID)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    FL = []
    for Result in ResultSet:
        FL.append([Result[0], Result[1], Result[2], Result[3], Result[4]])

    p = GetFuncDeclPattern()
    for Str in PSL:
        FuncRecord = GetFuncContainsPE(Str[1], FL)
        if FuncRecord is None:
            continue

        for Exp in GetPredicateListFromPredicateExpStr(Str[0]):
            PredInfo = SplitPredicateStr(Exp)
            if PredInfo[1] is None:
                PredVarStr = PredInfo[0][0].strip()
                IsFuncCall = False
                SearchInCache = False
                # PredVarStr may contain '.' or '->'
                TmpStr = PredVarStr.replace('.', '').replace('->', '')
                if p.match(TmpStr):
                    PredVarStr = PredVarStr[0:PredVarStr.find('(')]
                    SearchInCache = True
                    # Only direct function call using IsFuncCall branch. Multi-level ref. function call is considered a variable.
                    if TmpStr.startswith(PredVarStr):
                        IsFuncCall = True

                if PredVarStr.strip() in IgnoredKeywordList:
                    continue
                StarList = []
                PredVarList = GetCNameList(PredVarStr, StarList)
                # No variable found, maybe value first? like (0 == VarName)
                if len(PredVarList) == 0:
                    continue
                if SearchInCache:
                    Type = FuncReturnTypeDict.get(PredVarStr)
                    if Type is not None:
                        if Type.find('*') != -1 and Type != 'BOOLEAN*':
                            PrintErrorMsg(ERROR_PREDICATE_EXPRESSION_CHECK_COMPARISON_NULL_TYPE, 'Predicate Expression: %s' % Exp, FileTable, Str[2])
                        continue

                    if PredVarStr in FuncReturnTypeDict:
                        continue

                Type = GetVarInfo(PredVarList, FuncRecord, FullFileName, IsFuncCall, None, StarList)
                if SearchInCache:
                    FuncReturnTypeDict[PredVarStr] = Type
                if Type is None:
                    continue
                Type = GetTypeFromArray(Type, PredVarStr)
                if Type.find('*') != -1 and Type != 'BOOLEAN*':
                    PrintErrorMsg(ERROR_PREDICATE_EXPRESSION_CHECK_COMPARISON_NULL_TYPE, 'Predicate Expression: %s' % Exp, FileTable, Str[2])

def CheckNonBooleanValueComparison(FullFileName):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    # cache the found function return type to accelerate later checking in this file.
    FuncReturnTypeDict = {}

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select Value, StartLine, ID
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_PREDICATE_EXPRESSION)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    if len(ResultSet) == 0:
        return
    PSL = []
    for Result in ResultSet:
        PSL.append([Result[0], Result[1], Result[2]])

    SqlStatement = """ select BodyStartLine, EndLine, Header, Modifier, ID
                       from Function
                       where BelongsToFile = %d
                   """ % (FileID)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    FL = []
    for Result in ResultSet:
        FL.append([Result[0], Result[1], Result[2], Result[3], Result[4]])

    p = GetFuncDeclPattern()
    for Str in PSL:
        FuncRecord = GetFuncContainsPE(Str[1], FL)
        if FuncRecord is None:
            continue

        for Exp in GetPredicateListFromPredicateExpStr(Str[0]):
            PredInfo = SplitPredicateStr(Exp)
            if PredInfo[1] is None:
                PredVarStr = PredInfo[0][0].strip()
                IsFuncCall = False
                SearchInCache = False
                # PredVarStr may contain '.' or '->'
                TmpStr = PredVarStr.replace('.', '').replace('->', '')
                if p.match(TmpStr):
                    PredVarStr = PredVarStr[0:PredVarStr.find('(')]
                    SearchInCache = True
                    # Only direct function call using IsFuncCall branch. Multi-level ref. function call is considered a variable.
                    if TmpStr.startswith(PredVarStr):
                        IsFuncCall = True

                if PredVarStr.strip() in IgnoredKeywordList:
                    continue
                StarList = []
                PredVarList = GetCNameList(PredVarStr, StarList)
                # No variable found, maybe value first? like (0 == VarName)
                if len(PredVarList) == 0:
                    continue

                if SearchInCache:
                    Type = FuncReturnTypeDict.get(PredVarStr)
                    if Type is not None:
                        if Type.find('BOOLEAN') == -1:
                            PrintErrorMsg(ERROR_PREDICATE_EXPRESSION_CHECK_NO_BOOLEAN_OPERATOR, 'Predicate Expression: %s' % Exp, FileTable, Str[2])
                        continue

                    if PredVarStr in FuncReturnTypeDict:
                        continue
                Type = GetVarInfo(PredVarList, FuncRecord, FullFileName, IsFuncCall, 'BOOLEAN', StarList)
                if SearchInCache:
                    FuncReturnTypeDict[PredVarStr] = Type
                if Type is None:
                    continue
                if Type.find('BOOLEAN') == -1:
                    PrintErrorMsg(ERROR_PREDICATE_EXPRESSION_CHECK_NO_BOOLEAN_OPERATOR, 'Predicate Expression: %s' % Exp, FileTable, Str[2])


def CheckBooleanValueComparison(FullFileName):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    # cache the found function return type to accelerate later checking in this file.
    FuncReturnTypeDict = {}

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select Value, StartLine, ID
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_PREDICATE_EXPRESSION)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    if len(ResultSet) == 0:
        return
    PSL = []
    for Result in ResultSet:
        PSL.append([Result[0], Result[1], Result[2]])

    SqlStatement = """ select BodyStartLine, EndLine, Header, Modifier, ID
                       from Function
                       where BelongsToFile = %d
                   """ % (FileID)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    FL = []
    for Result in ResultSet:
        FL.append([Result[0], Result[1], Result[2], Result[3], Result[4]])

    p = GetFuncDeclPattern()
    for Str in PSL:
        FuncRecord = GetFuncContainsPE(Str[1], FL)
        if FuncRecord is None:
            continue

        for Exp in GetPredicateListFromPredicateExpStr(Str[0]):
            PredInfo = SplitPredicateStr(Exp)
            if PredInfo[1] in ('==', '!=') and PredInfo[0][1] in ('TRUE', 'FALSE'):
                PredVarStr = PredInfo[0][0].strip()
                IsFuncCall = False
                SearchInCache = False
                # PredVarStr may contain '.' or '->'
                TmpStr = PredVarStr.replace('.', '').replace('->', '')
                if p.match(TmpStr):
                    PredVarStr = PredVarStr[0:PredVarStr.find('(')]
                    SearchInCache = True
                    # Only direct function call using IsFuncCall branch. Multi-level ref. function call is considered a variable.
                    if TmpStr.startswith(PredVarStr):
                        IsFuncCall = True

                if PredVarStr.strip() in IgnoredKeywordList:
                    continue
                StarList = []
                PredVarList = GetCNameList(PredVarStr, StarList)
                # No variable found, maybe value first? like (0 == VarName)
                if len(PredVarList) == 0:
                    continue

                if SearchInCache:
                    Type = FuncReturnTypeDict.get(PredVarStr)
                    if Type is not None:
                        if Type.find('BOOLEAN') != -1:
                            PrintErrorMsg(ERROR_PREDICATE_EXPRESSION_CHECK_BOOLEAN_VALUE, 'Predicate Expression: %s' % Exp, FileTable, Str[2])
                        continue

                    if PredVarStr in FuncReturnTypeDict:
                        continue

                Type = GetVarInfo(PredVarList, FuncRecord, FullFileName, IsFuncCall, 'BOOLEAN', StarList)
                if SearchInCache:
                    FuncReturnTypeDict[PredVarStr] = Type
                if Type is None:
                    continue
                if Type.find('BOOLEAN') != -1:
                    PrintErrorMsg(ERROR_PREDICATE_EXPRESSION_CHECK_BOOLEAN_VALUE, 'Predicate Expression: %s' % Exp, FileTable, Str[2])


def CheckHeaderFileData(FullFileName):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select ID, Modifier
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_VARIABLE)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    for Result in ResultSet:
        if not Result[1].startswith('extern'):
            PrintErrorMsg(ERROR_INCLUDE_FILE_CHECK_DATA, 'Variable definition appears in header file', FileTable, Result[0])

    SqlStatement = """ select ID
                       from Function
                       where BelongsToFile = %d
                   """ % FileID
    ResultSet = Db.TblFile.Exec(SqlStatement)
    for Result in ResultSet:
        PrintErrorMsg(ERROR_INCLUDE_FILE_CHECK_DATA, 'Function definition appears in header file', 'Function', Result[0])

    return ErrorMsgList

def CheckHeaderFileIfndef(FullFileName):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select Value, StartLine
                       from %s
                       where Model = %d order by StartLine
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_MACRO_IFNDEF)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    if len(ResultSet) == 0:
        PrintErrorMsg(ERROR_INCLUDE_FILE_CHECK_IFNDEF_STATEMENT_1, '', 'File', FileID)
        return ErrorMsgList
    for Result in ResultSet:
        SqlStatement = """ select Value, EndLine
                       from %s
                       where EndLine < %d
                   """ % (FileTable, Result[1])
        ResultSet = Db.TblFile.Exec(SqlStatement)
        for Result in ResultSet:
            if not Result[0].startswith('/*') and not Result[0].startswith('//'):
                PrintErrorMsg(ERROR_INCLUDE_FILE_CHECK_IFNDEF_STATEMENT_2, '', 'File', FileID)
        break

    SqlStatement = """ select Value
                       from %s
                       where StartLine > (select max(EndLine) from %s where Model = %d)
                   """ % (FileTable, FileTable, DataClass.MODEL_IDENTIFIER_MACRO_ENDIF)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    for Result in ResultSet:
        if not Result[0].startswith('/*') and not Result[0].startswith('//'):
            PrintErrorMsg(ERROR_INCLUDE_FILE_CHECK_IFNDEF_STATEMENT_3, '', 'File', FileID)
    return ErrorMsgList

def CheckDoxygenCommand(FullFileName):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select Value, ID
                       from %s
                       where Model = %d or Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_COMMENT, DataClass.MODEL_IDENTIFIER_FUNCTION_HEADER)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    DoxygenCommandList = ['bug', 'todo', 'example', 'file', 'attention', 'param', 'post', 'pre', 'retval',
                          'return', 'sa', 'since', 'test', 'note', 'par', 'endcode', 'code']
    for Result in ResultSet:
        CommentStr = Result[0]
        CommentPartList = CommentStr.split()
        for Part in CommentPartList:
            if Part.upper() == 'BUGBUG':
                PrintErrorMsg(ERROR_DOXYGEN_CHECK_COMMAND, 'Bug should be marked with doxygen tag @bug', FileTable, Result[1])
            if Part.upper() == 'TODO':
                PrintErrorMsg(ERROR_DOXYGEN_CHECK_COMMAND, 'ToDo should be marked with doxygen tag @todo', FileTable, Result[1])
            if Part.startswith('@'):
                if EccGlobalData.gException.IsException(ERROR_DOXYGEN_CHECK_COMMAND, Part):
                    continue
                if not Part.replace('@', '').strip():
                    continue
                if Part.lstrip('@') in ['{', '}']:
                    continue
                if Part.lstrip('@').isalpha():
                    if Part.lstrip('@') not in DoxygenCommandList:
                        PrintErrorMsg(ERROR_DOXYGEN_CHECK_COMMAND, 'Unknown doxygen command %s' % Part, FileTable, Result[1])
                else:
                    Index = Part.find('[')
                    if Index == -1:
                        PrintErrorMsg(ERROR_DOXYGEN_CHECK_COMMAND, 'Unknown doxygen command %s' % Part, FileTable, Result[1])
                    RealCmd = Part[1:Index]
                    if RealCmd not in DoxygenCommandList:
                        PrintErrorMsg(ERROR_DOXYGEN_CHECK_COMMAND, 'Unknown doxygen command %s' % Part, FileTable, Result[1])


def CheckDoxygenTripleForwardSlash(FullFileName):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    Db = GetDB()

    SqlStatement = """ select ID, BodyStartLine, BodyStartColumn, EndLine, EndColumn
                       from Function
                       where BelongsToFile = %d
                   """ % (FileID)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    if len(ResultSet) == 0:
        return

    FuncDefSet = []
    for Result in ResultSet:
        FuncDefSet.append(Result)


    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select Value, ID, StartLine, StartColumn, EndLine, EndColumn
                       from %s
                       where Model = %d

                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_COMMENT)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    CommentSet = []
    try:
        for Result in ResultSet:
            CommentSet.append(Result)
    except:
        print('Unrecognized chars in comment of file %s', FullFileName)


    for Result in CommentSet:
        CommentStr = Result[0]
        StartLine = Result[2]
        StartColumn = Result[3]
        EndLine = Result[4]
        EndColumn = Result[5]
        if not CommentStr.startswith('///<'):
            continue

        Found = False
        for FuncDef in FuncDefSet:
            if StartLine == FuncDef[1] and StartColumn > FuncDef[2] and EndLine == FuncDef[3] and EndColumn < FuncDef[4]:
                Found = True
                break
            if StartLine > FuncDef[1] and EndLine < FuncDef[3]:
                Found = True
                break
            if StartLine == FuncDef[1] and StartColumn > FuncDef[2] and EndLine < FuncDef[3]:
                Found = True
                break
            if StartLine > FuncDef[1] and EndLine == FuncDef[3] and EndColumn < FuncDef[4]:
                Found = True
                break
        if Found:
            PrintErrorMsg(ERROR_DOXYGEN_CHECK_COMMENT_FORMAT, '', FileTable, Result[1])


def CheckFileHeaderDoxygenComments(FullFileName):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select Value, ID
                       from %s
                       where Model = %d and (StartLine = 1 or StartLine = 7 or StartLine = 8) and StartColumn = 0
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_COMMENT)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    if len(ResultSet) == 0:
        PrintErrorMsg(ERROR_HEADER_CHECK_FILE, 'No File License header appear at the very beginning of file.', 'File', FileID)
        return ErrorMsgList

    NoHeaderCommentStartFlag = True
    NoHeaderCommentEndFlag = True
    NoHeaderCommentPeriodFlag = True
    NoCopyrightFlag = True
    NoLicenseFlag = True
    NoRevReferFlag = True
    NextLineIndex = 0
    for Result in ResultSet:
        FileStartFlag = False
        CommentStrList = []
        CommentStr = Result[0].strip()
        CommentStrListTemp = CommentStr.split('\n')
        if (len(CommentStrListTemp) <= 1):
            # For Mac
            CommentStrListTemp = CommentStr.split('\r')
        # Skip the content before the file  header
        for CommentLine in CommentStrListTemp:
            if CommentLine.strip().startswith('/** @file'):
                FileStartFlag = True
            if FileStartFlag ==  True:
                CommentStrList.append(CommentLine)

        ID = Result[1]
        Index = 0
        if CommentStrList and CommentStrList[0].strip().startswith('/** @file'):
            NoHeaderCommentStartFlag = False
        else:
            continue
        if CommentStrList and CommentStrList[-1].strip().endswith('**/'):
            NoHeaderCommentEndFlag = False
        else:
            continue

        for CommentLine in CommentStrList:
            Index = Index + 1
            NextLineIndex = Index
            if CommentLine.startswith('/** @file'):
                continue
            if CommentLine.startswith('**/'):
                break
            # Check whether C File header Comment content start with two spaces.
            if EccGlobalData.gConfig.HeaderCheckCFileCommentStartSpacesNum == '1' or EccGlobalData.gConfig.HeaderCheckAll == '1' or EccGlobalData.gConfig.CheckAll == '1':
                if CommentLine.startswith('/** @file') == False and CommentLine.startswith('**/') == False and CommentLine.strip() and CommentLine.startswith('  ') == False:
                    PrintErrorMsg(ERROR_HEADER_CHECK_FILE, 'File header comment content should start with two spaces at each line', FileTable, ID)

            CommentLine = CommentLine.strip()
            if CommentLine.startswith('Copyright'):
                NoCopyrightFlag = False
                if CommentLine.find('All rights reserved') == -1:
                    for Copyright in EccGlobalData.gConfig.Copyright:
                        if CommentLine.find(Copyright) > -1:
                            PrintErrorMsg(ERROR_HEADER_CHECK_FILE, '""All rights reserved"" announcement should be following the ""Copyright"" at the same line', FileTable, ID)
                            break
                if CommentLine.endswith('<BR>') == -1:
                    PrintErrorMsg(ERROR_HEADER_CHECK_FILE, 'The ""<BR>"" at the end of the Copyright line is required', FileTable, ID)
                if NextLineIndex < len(CommentStrList) and CommentStrList[NextLineIndex].strip().startswith('Copyright') == False and CommentStrList[NextLineIndex].strip():
                    NoLicenseFlag = False
            if CommentLine.startswith('@par Revision Reference:'):
                NoRevReferFlag = False
                RefListFlag = False
                for RefLine in CommentStrList[NextLineIndex:]:
                    if RefLine.strip() and (NextLineIndex + 1) < len(CommentStrList) and CommentStrList[NextLineIndex+1].strip() and CommentStrList[NextLineIndex+1].strip().startswith('**/') == False:
                        RefListFlag = True
                    if RefLine.strip() == False or RefLine.strip().startswith('**/'):
                        RefListFlag = False
                        break
                    # Check whether C File header Comment's each reference at list should begin with a bullet character.
                    if EccGlobalData.gConfig.HeaderCheckCFileCommentReferenceFormat == '1' or EccGlobalData.gConfig.HeaderCheckAll == '1' or EccGlobalData.gConfig.CheckAll == '1':
                        if RefListFlag == True:
                            if RefLine.strip() and RefLine.strip().startswith('**/') == False and RefLine.startswith('  -') == False:
                                PrintErrorMsg(ERROR_HEADER_CHECK_FILE, 'Each reference on a separate line should begin with a bullet character ""-"" ', FileTable, ID)

    if NoHeaderCommentStartFlag:
        PrintErrorMsg(ERROR_DOXYGEN_CHECK_FILE_HEADER, 'File header comment should begin with ""/** @file""', FileTable, ID)
        return
    if NoHeaderCommentEndFlag:
        PrintErrorMsg(ERROR_HEADER_CHECK_FILE, 'File header comment should end with ""**/""', FileTable, ID)
        return
    if NoCopyrightFlag:
        PrintErrorMsg(ERROR_HEADER_CHECK_FILE, 'File header comment missing the ""Copyright""', FileTable, ID)
    #Check whether C File header Comment have the License immediately after the ""Copyright"" line.
    if EccGlobalData.gConfig.HeaderCheckCFileCommentLicenseFormat == '1' or EccGlobalData.gConfig.HeaderCheckAll == '1' or EccGlobalData.gConfig.CheckAll == '1':
        if NoLicenseFlag:
            PrintErrorMsg(ERROR_HEADER_CHECK_FILE, 'File header comment should have the License immediately after the ""Copyright"" line', FileTable, ID)

def CheckFuncHeaderDoxygenComments(FullFileName):
    ErrorMsgList = []

    FileID = GetTableID(FullFileName, ErrorMsgList)
    if FileID < 0:
        return ErrorMsgList

    Db = GetDB()
    FileTable = 'Identifier' + str(FileID)
    SqlStatement = """ select Value, StartLine, EndLine, ID
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_COMMENT)

    ResultSet = Db.TblFile.Exec(SqlStatement)
    CommentSet = []
    try:
        for Result in ResultSet:
            CommentSet.append(Result)
    except:
        print('Unrecognized chars in comment of file %s', FullFileName)

    # Func Decl check
    SqlStatement = """ select Modifier, Name, StartLine, ID, Value
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_FUNCTION_DECLARATION)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    for Result in ResultSet:
        FuncName = Result[4]
        FunctionHeaderComment = CheckCommentImmediatelyPrecedeFunctionHeader(Result[1], Result[2], CommentSet)
        if FunctionHeaderComment:
            CheckFunctionHeaderConsistentWithDoxygenComment(Result[0], Result[1], Result[2], FunctionHeaderComment[0], FunctionHeaderComment[1], ErrorMsgList, FunctionHeaderComment[3], FileTable)
        else:
            if EccGlobalData.gException.IsException(ERROR_HEADER_CHECK_FUNCTION, FuncName):
                continue
            ErrorMsgList.append('Line %d :Function %s has NO comment immediately preceding it.' % (Result[2], Result[1]))
            PrintErrorMsg(ERROR_HEADER_CHECK_FUNCTION, 'Function [%s] has NO comment immediately preceding it.' % (FuncName), FileTable, Result[3])

    # Func Def check
    SqlStatement = """ select Value, StartLine, EndLine, ID
                       from %s
                       where Model = %d
                   """ % (FileTable, DataClass.MODEL_IDENTIFIER_FUNCTION_HEADER)

    ResultSet = Db.TblFile.Exec(SqlStatement)
    CommentSet = []
    try:
        for Result in ResultSet:
            CommentSet.append(Result)
    except:
        print('Unrecognized chars in comment of file %s', FullFileName)

    SqlStatement = """ select Modifier, Header, StartLine, ID, Name
                       from Function
                       where BelongsToFile = %d
                   """ % (FileID)
    ResultSet = Db.TblFile.Exec(SqlStatement)
    for Result in ResultSet:
        FuncName = Result[4]
        FunctionHeaderComment = CheckCommentImmediatelyPrecedeFunctionHeader(Result[1], Result[2], CommentSet)
        if FunctionHeaderComment:
            CheckFunctionHeaderConsistentWithDoxygenComment(Result[0], Result[1], Result[2], FunctionHeaderComment[0], FunctionHeaderComment[1], ErrorMsgList, FunctionHeaderComment[3], FileTable)
        else:
            if EccGlobalData.gException.IsException(ERROR_HEADER_CHECK_FUNCTION, FuncName):
                continue
            ErrorMsgList.append('Line %d :Function [%s] has NO comment immediately preceding it.' % (Result[2], Result[1]))
            PrintErrorMsg(ERROR_HEADER_CHECK_FUNCTION, 'Function [%s] has NO comment immediately preceding it.' % (FuncName), 'Function', Result[3])
    return ErrorMsgList

def CheckCommentImmediatelyPrecedeFunctionHeader(FuncName, FuncStartLine, CommentSet):

    for Comment in CommentSet:
        if Comment[2] == FuncStartLine - 1:
            return Comment
    return None

def GetDoxygenStrFromComment(Str):
    DoxygenStrList = []
    ParamTagList = Str.split('@param')
    if len(ParamTagList) > 1:
        i = 1
        while i < len(ParamTagList):
            DoxygenStrList.append('@param' + ParamTagList[i])
            i += 1

    Str = ParamTagList[0]

    RetvalTagList = ParamTagList[-1].split('@retval')
    if len(RetvalTagList) > 1:
        if len(ParamTagList) > 1:
            DoxygenStrList[-1] = '@param' + RetvalTagList[0]
        i = 1
        while i < len(RetvalTagList):
            DoxygenStrList.append('@retval' + RetvalTagList[i])
            i += 1

    ReturnTagList = RetvalTagList[-1].split('@return')
    if len(ReturnTagList) > 1:
        if len(RetvalTagList) > 1:
            DoxygenStrList[-1] = '@retval' + ReturnTagList[0]
        elif len(ParamTagList) > 1:
            DoxygenStrList[-1] = '@param' + ReturnTagList[0]
        i = 1
        while i < len(ReturnTagList):
            DoxygenStrList.append('@return' + ReturnTagList[i])
            i += 1

    if len(DoxygenStrList) > 0:
        DoxygenStrList[-1] = DoxygenStrList[-1].rstrip('--*/')

    return DoxygenStrList

def CheckGeneralDoxygenCommentLayout(Str, StartLine, ErrorMsgList, CommentId= -1, TableName=''):
    #/** --*/ @retval after @param
    if not Str.startswith('/**'):
        ErrorMsgList.append('Line %d : Comment does NOT have prefix /** ' % StartLine)
        PrintErrorMsg(ERROR_DOXYGEN_CHECK_FUNCTION_HEADER, 'Comment does NOT have prefix /** ', TableName, CommentId)
    if not Str.endswith('**/'):
        ErrorMsgList.append('Line %d : Comment does NOT have tail **/ ' % StartLine)
        PrintErrorMsg(ERROR_DOXYGEN_CHECK_FUNCTION_HEADER, 'Comment does NOT have tail **/ ', TableName, CommentId)
    FirstRetvalIndex = Str.find('@retval')
    LastParamIndex = Str.rfind('@param')
    if (FirstRetvalIndex > 0) and (LastParamIndex > 0) and (FirstRetvalIndex < LastParamIndex):
        ErrorMsgList.append('Line %d : @retval appear before @param ' % StartLine)
        PrintErrorMsg(ERROR_DOXYGEN_CHECK_FUNCTION_HEADER, 'in Comment, @retval appear before @param  ', TableName, CommentId)

def CheckFunctionHeaderConsistentWithDoxygenComment(FuncModifier, FuncHeader, FuncStartLine, CommentStr, CommentStartLine, ErrorMsgList, CommentId= -1, TableName=''):

    ParamList = GetParamList(FuncHeader)
    CheckGeneralDoxygenCommentLayout(CommentStr, CommentStartLine, ErrorMsgList, CommentId, TableName)
    DescriptionStr = CommentStr
    DoxygenStrList = GetDoxygenStrFromComment(DescriptionStr)
    if DescriptionStr.find('.') == -1:
        PrintErrorMsg(ERROR_DOXYGEN_CHECK_COMMENT_DESCRIPTION, 'Comment description should end with period \'.\'', TableName, CommentId)
    DoxygenTagNumber = len(DoxygenStrList)
    ParamNumber = len(ParamList)
    for Param in ParamList:
        if Param.Name.upper() == 'VOID' and ParamNumber == 1:
            ParamNumber -= 1
    Index = 0
    if ParamNumber > 0 and DoxygenTagNumber > 0:
        while Index < ParamNumber and Index < DoxygenTagNumber:
            ParamModifier = ParamList[Index].Modifier
            ParamName = ParamList[Index].Name.strip()
            Tag = DoxygenStrList[Index].strip(' ')
            if (not Tag[-1] == ('\n')) and (not Tag[-1] == ('\r')):
                ErrorMsgList.append('Line %d : in Comment, <%s> does NOT end with new line ' % (CommentStartLine, Tag.replace('\n', '').replace('\r', '')))
                PrintErrorMsg(ERROR_HEADER_CHECK_FUNCTION, 'in Comment, <%s> does NOT end with new line ' % (Tag.replace('\n', '').replace('\r', '')), TableName, CommentId)
            TagPartList = Tag.split()
            if len(TagPartList) < 2:
                ErrorMsgList.append('Line %d : in Comment, <%s> does NOT contain doxygen contents ' % (CommentStartLine, Tag.replace('\n', '').replace('\r', '')))
                PrintErrorMsg(ERROR_DOXYGEN_CHECK_FUNCTION_HEADER, 'in Comment, <%s> does NOT contain doxygen contents ' % (Tag.replace('\n', '').replace('\r', '')), TableName, CommentId)
                Index += 1
                continue
            LBPos = Tag.find('[')
            RBPos = Tag.find(']')
            ParamToLBContent = Tag[len('@param'):LBPos].strip()
            if LBPos > 0 and len(ParamToLBContent) == 0 and RBPos > LBPos:
                InOutStr = ''
                ModifierPartList = ParamModifier.split()
                for Part in ModifierPartList:
                    if Part.strip() == 'IN':
                        InOutStr += 'in'
                    if Part.strip() == 'OUT':
                        if InOutStr != '':
                            InOutStr += ', out'
                        else:
                            InOutStr = 'out'

                if InOutStr != '':
                    if Tag.find('[' + InOutStr + ']') == -1:
                        if InOutStr != 'in, out':
                            ErrorMsgList.append('Line %d : in Comment, <%s> does NOT have %s ' % (CommentStartLine, (TagPartList[0] + ' ' + TagPartList[1]).replace('\n', '').replace('\r', ''), '[' + InOutStr + ']'))
                            PrintErrorMsg(ERROR_DOXYGEN_CHECK_FUNCTION_HEADER, 'in Comment, <%s> does NOT have %s ' % ((TagPartList[0] + ' ' + TagPartList[1]).replace('\n', '').replace('\r', ''), '[' + InOutStr + ']'), TableName, CommentId)
                        else:
                            if Tag.find('[in,out]') == -1:
                                ErrorMsgList.append('Line %d : in Comment, <%s> does NOT have %s ' % (CommentStartLine, (TagPartList[0] + ' ' + TagPartList[1]).replace('\n', '').replace('\r', ''), '[' + InOutStr + ']'))
                                PrintErrorMsg(ERROR_DOXYGEN_CHECK_FUNCTION_HEADER, 'in Comment, <%s> does NOT have %s ' % ((TagPartList[0] + ' ' + TagPartList[1]).replace('\n', '').replace('\r', ''), '[' + InOutStr + ']'), TableName, CommentId)


            if Tag.find(ParamName) == -1 and ParamName != 'VOID' and ParamName != 'void':
                ErrorMsgList.append('Line %d : in Comment, <%s> does NOT consistent with parameter name %s ' % (CommentStartLine, (TagPartList[0] + ' ' + TagPartList[1]).replace('\n', '').replace('\r', ''), ParamName))
                PrintErrorMsg(ERROR_DOXYGEN_CHECK_FUNCTION_HEADER, 'in Comment, <%s> does NOT consistent with parameter name %s ' % ((TagPartList[0] + ' ' + TagPartList[1]).replace('\n', '').replace('\r', ''), ParamName), TableName, CommentId)
            Index += 1

        if Index < ParamNumber:
            ErrorMsgList.append('Line %d : Number of doxygen tags in comment less than number of function parameters' % CommentStartLine)
            PrintErrorMsg(ERROR_DOXYGEN_CHECK_FUNCTION_HEADER, 'Number of doxygen tags in comment less than number of function parameters ', TableName, CommentId)
        # VOID return type, NOT VOID*. VOID* should be matched with a doxygen tag.
        if (FuncModifier.find('VOID') != -1 or FuncModifier.find('void') != -1) and FuncModifier.find('*') == -1:

            # assume we allow a return description tag for void func. return. that's why 'DoxygenTagNumber - 1' is used instead of 'DoxygenTagNumber'
            if Index < DoxygenTagNumber - 1 or (Index < DoxygenTagNumber and DoxygenStrList[Index].startswith('@retval')):
                ErrorMsgList.append('Line %d : VOID return type need NO doxygen tags in comment' % CommentStartLine)
                PrintErrorMsg(ERROR_DOXYGEN_CHECK_FUNCTION_HEADER, 'VOID return type need no doxygen tags in comment ', TableName, CommentId)
        else:
            if Index < DoxygenTagNumber and not DoxygenStrList[Index].startswith('@retval') and not DoxygenStrList[Index].startswith('@return'):
                ErrorMsgList.append('Line %d : Number of @param doxygen tags in comment does NOT match number of function parameters' % CommentStartLine)
                PrintErrorMsg(ERROR_DOXYGEN_CHECK_FUNCTION_HEADER, 'Number of @param doxygen tags in comment does NOT match number of function parameters ', TableName, CommentId)
    else:
        if ParamNumber == 0 and DoxygenTagNumber != 0 and ((FuncModifier.find('VOID') != -1 or FuncModifier.find('void') != -1) and FuncModifier.find('*') == -1):
            ErrorMsgList.append('Line %d : VOID return type need NO doxygen tags in comment' % CommentStartLine)
            PrintErrorMsg(ERROR_DOXYGEN_CHECK_FUNCTION_HEADER, 'VOID return type need NO doxygen tags in comment ', TableName, CommentId)
        if ParamNumber != 0 and DoxygenTagNumber == 0:
            ErrorMsgList.append('Line %d : No doxygen tags in comment' % CommentStartLine)
            PrintErrorMsg(ERROR_DOXYGEN_CHECK_FUNCTION_HEADER, 'No doxygen tags in comment ', TableName, CommentId)

if __name__ == '__main__':

#    EdkLogger.Initialize()
#    EdkLogger.SetLevel(EdkLogger.QUIET)
#    CollectSourceCodeDataIntoDB(sys.argv[1])
    try:
        test_file = sys.argv[1]
    except IndexError as v:
        print("Usage: %s filename" % sys.argv[0])
        sys.exit(1)
    MsgList = CheckFuncHeaderDoxygenComments(test_file)
    for Msg in MsgList:
        print(Msg)
    print('Done!')
