## @file
# This file is used to be the c coding style checking of ECC tool
#
# Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#

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

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

def GetIgnoredDirListPattern():
    skipList = list(EccGlobalData.gConfig.SkipDirList) + ['.svn']
    DirString = '|'.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 = []
    TokenReleaceList = EccGlobalData.gConfig.TokenReleaceList
    TokenReleaceList.extend(['L",\\\""'])

    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)
                collector.TokenReleaceList = TokenReleaceList
                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 beginning, 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 suffix
        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] == '/':
            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 [] suffixes...
                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(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, '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, Name
                       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:
            if not EccGlobalData.gException.IsException(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_BODY, Result[3]):
                PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_BODY,
                              'The open brace should be at the very beginning of a line for the function [%s].' % Result[3],
                              'Function', Result[2])
        if Result[1] != 0:
            if not EccGlobalData.gException.IsException(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_BODY, Result[3]):
                PrintErrorMsg(ERROR_C_FUNCTION_LAYOUT_CHECK_FUNCTION_BODY,
                              'The close brace should be at the very beginning of a line for the function [%s].' % Result[3],
                              '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 [] suffixes...
        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', '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, AllTypedefFun=[]):
    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'):
            for Item in AllTypedefFun:
                if '(%s)' % Result[1] in Item:
                    break
            else:
                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') or ('Copyright' in CommentLine and CommentLine.lower().startswith('(c)')):
                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!')