srcanamdw/codescanner/scripts/linescanner.py
changeset 1 22878952f6e2
equal deleted inserted replaced
0:509e4801c378 1:22878952f6e2
       
     1 # #################################################################
       
     2 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 # All rights reserved.
       
     4 # 
       
     5 # Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
       
     6 # 
       
     7 # * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
       
     8 # * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
       
     9 # * Neither the name of Nokia Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
       
    10 # 
       
    11 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
       
    12 # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 
       
    13 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
       
    14 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
       
    15 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.#
       
    16 #
       
    17 # linescanner.py - the main body of CodeScanner
       
    18 #
       
    19 # #################################################################
       
    20 
       
    21 import base64
       
    22 import datetime
       
    23 import encodings
       
    24 import getopt
       
    25 import os
       
    26 import os.path
       
    27 import psyco
       
    28 import re
       
    29 import sys
       
    30 import xml.dom.minidom
       
    31 import zlib
       
    32 
       
    33 # Ignore flags
       
    34 KIgnoreNothing = 0
       
    35 KIgnoreComments = 1
       
    36 KIgnoreCommentsAndQuotes = 2
       
    37 KIgnoreQuotes = 3
       
    38 
       
    39 # Severities for the scripts
       
    40 KSeverityHigh = 0
       
    41 KSeverityMedium = 1
       
    42 KSeverityLow = 2
       
    43 
       
    44 # The names used in the XML configuration file for severity element names.
       
    45 KSeverityConfigMap = {
       
    46 			KSeverityHigh		: "high",
       
    47 			KSeverityMedium		: "medium",
       
    48 			KSeverityLow		: "low"}
       
    49 
       
    50 # The names used in the HTML summary file for severity element names.
       
    51 KSeverityHTMLMap = {
       
    52 			KSeverityHigh		: "High",
       
    53 			KSeverityMedium		: "Medium",
       
    54 			KSeverityLow		: "Low"}
       
    55 
       
    56 # Categories for the scripts
       
    57 KCategoryLegal = "Legal Code" 
       
    58 KCategoryDefinitePanic = "Always Panic"
       
    59 KCategoryCanPanic = "Can Panic"
       
    60 KCategoryWrongFunctionality = "Wrong Functionality"
       
    61 KCategoryLocalisation = "Localisation"
       
    62 KCategoryPerformance = "Performance"
       
    63 KCategoryCodingStandards = "Coding Standard"
       
    64 KCategoryDocumentation = "Documentation"
       
    65 KCategoryCodeReviewGuides = "Code Review Guide"
       
    66 KCategoryOther = "Other"
       
    67 
       
    68 KCategoryHtmlDisplayOrder = [KCategoryLegal,
       
    69 	KCategoryDefinitePanic,
       
    70 	KCategoryCanPanic,
       
    71 	KCategoryWrongFunctionality,
       
    72 	KCategoryLocalisation,
       
    73 	KCategoryPerformance,
       
    74 	KCategoryCodingStandards,
       
    75 	KCategoryDocumentation,
       
    76 	KCategoryCodeReviewGuides,
       
    77 	KCategoryOther]
       
    78 
       
    79 # The names used in the XML configuration file for category element names.
       
    80 KCategoryConfigMap = {
       
    81 			KCategoryLegal				:	"legal", 
       
    82 			KCategoryDefinitePanic		:	"panic", 
       
    83 			KCategoryCanPanic			:	"canpanic", 
       
    84 			KCategoryWrongFunctionality	:	"functionality",
       
    85 			KCategoryLocalisation		:	"localisation",
       
    86 			KCategoryPerformance		:	"performance",
       
    87 			KCategoryCodingStandards	:	"codingstandards",
       
    88 			KCategoryDocumentation		:	"documentation",
       
    89 			KCategoryCodeReviewGuides	:	"codereview",
       
    90 			KCategoryOther				:	"other"}
       
    91 
       
    92 #Custom rule keyword types
       
    93 KKeywordBaseClass = "baseclass"
       
    94 KKeywordCall = "call"
       
    95 KKeywordClassName = "class"
       
    96 KKeywordComment = "comment"
       
    97 KKeywordGeneric = "generic"
       
    98 KKeywordLocal = "local"
       
    99 KKeywordMacro = "macro"
       
   100 KKeywordMember = "member"
       
   101 KKeywordMethod = "method"
       
   102 KKeywordParameter = "parameter"
       
   103 KKeywordUnknown = "unknown"
       
   104 
       
   105 #The names used in the XML configuration file for custom rule keyword types.
       
   106 KCustomRuleKeywordMap = {
       
   107 			KKeywordBaseClass           :   "baseclass",
       
   108 			KKeywordCall                :   "call",
       
   109 			KKeywordClassName           :   "class",
       
   110 			KKeywordComment             :   "comment",
       
   111 			KKeywordGeneric             :   "generic",
       
   112 			KKeywordLocal               :   "local",
       
   113 			KKeywordMacro               :   "macro",
       
   114 			KKeywordMember              :   "member",
       
   115 			KKeywordMethod              :   "method",
       
   116 			KKeywordParameter           :   "parameter",
       
   117 			KKeywordUnknown             :   "unknown"}
       
   118 
       
   119 KVersion = "Nokia CodeScanner version 2.1.4"
       
   120 KCopyrightLine1 = "Copyright (c) 2007-2009. Nokia Corporation. All rights reserved."
       
   121 KCopyrightLine1Html = "Copyright © 2007-2009. Nokia Corporation. All rights reserved."
       
   122 KCopyrightLine2 = "For product and support information, visit www.forum.nokia.com."
       
   123 KWww = "www.forum.nokia.com"
       
   124 
       
   125 stringPool = {}
       
   126 #!LOCALISEHERE
       
   127 
       
   128 def Usage(code, msg=""):
       
   129 	print msg
       
   130 	print
       
   131 	print KVersion
       
   132 	print
       
   133 	print "Usage: CodeScanner [options] <source dir> [<output dir>]"	
       
   134 	print "   or: CodeScanner [options] <source file> [<output dir>]"
       
   135 	print 
       
   136 	print "options:"
       
   137 	print "    -h - display command help"
       
   138 	print "    -v - display verbose messages"
       
   139 	print "    -c <config file> - use specified configuration file"
       
   140 	print "    -i <source dir/file> - specify additional directory/file to scan"
       
   141 	print "    -l <log file> - create debug log with specified filename"
       
   142 	print "    -o html|xml|std - specify output format : HTML, XML or StdOut; default is HTML"
       
   143 	print "    -x url to lxr site"
       
   144 	print "    -r lxr version"
       
   145 	print "    -t on/off - create a time-stamped directory for results, default is on"
       
   146 	print
       
   147 	print "<source dir> is the directory containing the source code to scan"
       
   148 	print "<source file> is the single file containing the source code to scan"
       
   149 	print "<output dir> is the directory in which to produce the output"
       
   150 	print
       
   151 	print "Notes:"
       
   152 	print "<source dir> and <output dir> cannot be identical"
       
   153 	print "<output dir> cannot be the root of a drive"
       
   154 	print
       
   155 	print KCopyrightLine1
       
   156 	print KCopyrightLine2
       
   157 	if scanner.iLog <> None:
       
   158 		scanner.iLog.Write("usage(): exiting with code " + str(code))
       
   159 		scanner.iLog.Close()
       
   160 	sys.exit(code)
       
   161 
       
   162 def DefaultCompare(aLines, aCurrentline, aRematch, aFilename):
       
   163 	if aRematch.search(aLines[aCurrentline]):
       
   164 		return 1
       
   165 	else:
       
   166 		return 0
       
   167 
       
   168 def DefaultFuncParamCompare(lines, currentline, rematch, filename):
       
   169 	# distinguish local declaration from function parameter
       
   170     line = lines[currentline]
       
   171     m = rematch.search(line)
       
   172     if m:
       
   173         isFuncParam = (line.find(")") <> -1)
       
   174         isLocal = (line.find(";") <> -1)
       
   175 
       
   176         while (not isFuncParam) and (not isLocal) and (currentline + 1 < len(lines)):
       
   177             currentline += 1
       
   178             line = lines[currentline]
       
   179             isFuncParam = (line.find(")") <> -1)
       
   180             isLocal = (line.find(";") <> -1)
       
   181 
       
   182         if isFuncParam:
       
   183             return 1
       
   184         elif isLocal:
       
   185             return 0
       
   186 
       
   187     return 0
       
   188 
       
   189 def ScanDirOrFile(argument):
       
   190 	if os.path.isdir(argument):
       
   191 		scanner.iComponentManager.SetRoot(argument)
       
   192 		scanner.TraverseDirectory(argument)
       
   193 	elif os.path.isfile(argument):
       
   194 		parentDir = os.path.dirname(argument)
       
   195 		scanner.iComponentManager.SetRoot(parentDir)
       
   196 		scanner.iComponentManager.BeginDirectory(parentDir)
       
   197 		numberOfLinesScanned = 0
       
   198 		numberOfLinesScanned += scanner.ScanFile(argument)
       
   199 		scanner.iComponentManager.EndDirectory(parentDir, numberOfLinesScanned)
       
   200 	else:
       
   201 		print "Unable to open specified source file: " + argument
       
   202 		sys.exit(2)
       
   203 
       
   204 
       
   205 class CScript:
       
   206 
       
   207 	# #######################################################
       
   208 	# CScript - a test script
       
   209 
       
   210 	def __init__(self, aScriptName):
       
   211 		self.iScriptName = aScriptName
       
   212 		self.iCompare = DefaultCompare
       
   213 		self.iReString = ""
       
   214 		self.iReMatch = re.compile("")
       
   215 		self.iTitle = stringPool[aScriptName + "!title"]
       
   216 		self.iIdeTitle = stringPool[aScriptName + "!ideTitle"]
       
   217 		self.iFileExts = []
       
   218 		self.iIgnore = KIgnoreNothing
       
   219 		self.iDescription = stringPool[aScriptName + "!description"]
       
   220 		self.iSeverity = KSeverityMedium
       
   221 		self.iBaseClass = ""
       
   222 
       
   223 	def ScriptConfig(self):
       
   224 		if (scanner.iDomConfig <> None):
       
   225 			for scriptsNode in scanner.iDomConfig.getElementsByTagName("scripts"):
       
   226 				for scriptNode in scriptsNode.getElementsByTagName(self.iScriptName):
       
   227 					return scriptNode
       
   228 		return None
       
   229 	
       
   230 	def DefaultInheritanceCompare(self, lines, currentline, rematch, filename):
       
   231 		m = rematch.search(lines[currentline])
       
   232 		if m:
       
   233 			inheritanceString = m.group(3)
       
   234 			# check for inheritance list spanning multiple lines
       
   235 			i = currentline + 1
       
   236 			while (inheritanceString.find("{") == -1) and i < len(lines):
       
   237 				if (inheritanceString.find(";") <> -1):
       
   238 					return 0
       
   239 				inheritanceString += lines[i]
       
   240 				i += 1
       
   241 
       
   242 			# construct inheritance class list
       
   243 			inheritancelist = inheritanceString.split(",")
       
   244 			reclass = re.compile("[\s:]*(public|protected|private)?\s*([\w:]+)")
       
   245 			classlist = []
       
   246 			for inheritance in inheritancelist:
       
   247 				match = reclass.search(inheritance)
       
   248 				if match:
       
   249 					inheritclass = match.group(2)
       
   250 					colonpos = inheritclass.rfind(":")
       
   251 					if (colonpos <> -1):
       
   252 						inheritclass = inheritclass[colonpos + 1:]
       
   253 					classlist.append(inheritclass)
       
   254 
       
   255 			# search for inheritance class
       
   256 			for classname in classlist:
       
   257 				if classname == self.iBaseClass:
       
   258 					return 1
       
   259 
       
   260 		return 0
       
   261 
       
   262 
       
   263 class CCustomScript(CScript):
       
   264 
       
   265 	# #######################################################
       
   266 	# CScript - a test script based on a custom rule
       
   267 
       
   268 	def __init__(self, aScriptName):
       
   269 		self.iScriptName = aScriptName
       
   270 		self.iCompare = DefaultCompare
       
   271 		self.iReString = ""
       
   272 		self.iTitle = ""
       
   273 		self.iIdeTitle = ""
       
   274 		self.iFileExts = []
       
   275 		self.iIgnore = KIgnoreNothing
       
   276 		self.iDescription = ""
       
   277 
       
   278 
       
   279 class CCategorisedScripts:
       
   280 
       
   281 	# #######################################################
       
   282 	# CCategorisedScripts - a collection of scripts sorted
       
   283 	# by script category (panic, can panic, etc.)
       
   284 
       
   285 	def AddScript(self, aScript):
       
   286 		# do we have a category for this already?
       
   287 		category = aScript.iCategory
       
   288 		if (not self.iScripts.has_key(category)):
       
   289 			# no, create a linear array here
       
   290 			self.iScripts[category] = []
       
   291 
       
   292 		# append to the correct category
       
   293 		self.iScripts[category].append(aScript)
       
   294 
       
   295 		# compile the reg-ex otherwise will get continuous hits
       
   296 		aScript.iReMatch = re.compile(aScript.iReString, re.VERBOSE)
       
   297 
       
   298 	def AllScripts(self):
       
   299 		result = []
       
   300 		for scripts in self.iScripts.values():
       
   301 			result += scripts
       
   302 
       
   303 		return result
       
   304 
       
   305 	def PrintListOfTestScripts(self):
       
   306 		for category in self.iScripts.keys():
       
   307 			print(category + "\n----------------------------------")
       
   308 			for script in self.iScripts[category]:
       
   309 				print("\t" + script.iScriptName)
       
   310 
       
   311 		print("")
       
   312 
       
   313 	# iScripts is a 2D array, 1st level is a hash of categories
       
   314 	#                         2nd level is linear array
       
   315 	iScripts = {}        
       
   316 
       
   317 class CLogger:
       
   318 
       
   319 	# #######################################################
       
   320 	# CLogger
       
   321 	# a simple log file interface
       
   322 
       
   323 	def __init__(self, aFilename):
       
   324 		if aFilename != None and len(aFilename) > 0:
       
   325 			self.iFile = file(aFilename, "w")
       
   326 			self.iFile.write(KVersion + " started at " + datetime.datetime.now().ctime() + "\n")
       
   327 		else:
       
   328 			self.iFile = None
       
   329 
       
   330 	def Write(self, aText):
       
   331 		if self.iFile <> None:
       
   332 			self.iFile.write(str(datetime.datetime.now().time())+":"+aText+"\n")
       
   333 			self.iFile.flush()
       
   334 
       
   335 	def Close(self):
       
   336 		if self.iFile <> None:
       
   337 			self.iFile.write(KVersion + " ended at " + datetime.datetime.now().ctime() + "\n")
       
   338 			self.iFile.close()
       
   339 
       
   340 
       
   341 class CRendererBase:
       
   342 
       
   343 	# #######################################################
       
   344 	# CRendererBase - base class for renderers
       
   345 
       
   346 	def RegisterSelf(self, aName, aDescription, aRendererManager):
       
   347 		self.iName = aName
       
   348 		self.iDescription = aDescription
       
   349 		aRendererManager.AddRenderer(self)
       
   350 	def BeginComponent(self, aComponent):
       
   351 		return
       
   352 	def BeginFile(self, aFilename):
       
   353 		return
       
   354 	def ReportError(self, aLineContext, aScript):
       
   355 		return
       
   356 	def EndFile(self):
       
   357 		return
       
   358 	def EndComponent(self, aComponent):
       
   359 		return
       
   360 
       
   361 
       
   362 class CStdOutRenderer(CRendererBase):
       
   363 
       
   364 	# #######################################################
       
   365 	# CStdOutRenderer - renderer for Standard Console Output
       
   366 	# Output goes to standard output; when run in Carbide, 
       
   367 	# this shows up in the output window. Correctly formatted 
       
   368 	# lines can then be selected, automatically selecting 
       
   369 	# the corresponding line of the associated source file. 
       
   370 	# The format is:
       
   371 	#   <filename>(<line>) : <comment>
       
   372 
       
   373 	def __init__(self, aRendererManager):
       
   374 		self.RegisterSelf("stdout", "StdOut renderer", aRendererManager)
       
   375 		print KVersion
       
   376 
       
   377 	def BeginComponent(self, aComponent):
       
   378 		return
       
   379 
       
   380 	def BeginFile(self, aFilename):
       
   381 		self.iErrorCount = 0
       
   382 		scanner.ReportAction("Scanning file " + aFilename)
       
   383 
       
   384 	def ReportError(self, aLineContext, aScript):
       
   385 		self.iErrorCount += 1
       
   386 		if (aScript.iSeverity == KSeverityLow):
       
   387 			msgType = "info"
       
   388 		elif (aScript.iSeverity == KSeverityMedium):
       
   389 			msgType = "warning"
       
   390 		elif (aScript.iSeverity == KSeverityHigh):
       
   391 			msgType = "error"
       
   392 		print(aLineContext.iFileName + "(" + str(aLineContext.iLineNumber) + ") : " + msgType + ": " + aScript.iScriptName + ": " + KSeverityConfigMap[aScript.iSeverity] + ": " + KCategoryConfigMap[aScript.iCategory] + ": " + aScript.iIdeTitle)
       
   393 		if len(scanner.iRendererManager.iAnnotation)>0:
       
   394 			print scanner.iRendererManager.iAnnotation
       
   395 			scanner.iRendererManager.iAnnotation = ""
       
   396 
       
   397 	def EndFile(self):
       
   398 		scanner.ReportAction("Total problems found in file: " + str(self.iErrorCount))
       
   399 
       
   400 	def EndComponent(self, aComponent):
       
   401 		scanner.iEndTime = datetime.datetime.now().ctime()
       
   402 		return
       
   403 
       
   404 
       
   405 class CXmlComponentSummaryFile:
       
   406 	# #########################################################
       
   407 	# CXmlComponentSummaryFile
       
   408 	# Encapsulates the script (problem) summary for XML output.
       
   409 	# For each script, there is a listing for occurrences
       
   410 	# of that script's problem and location of each occurrence.
       
   411 
       
   412 	def CreateSummary(self, aXmlRenderer):
       
   413 		try:
       
   414 			outputPath = os.path.normpath(os.path.join(aXmlRenderer.iOutputDirectory, "problemIndex.xml"))
       
   415 			outputFile = file(outputPath, "w")
       
   416 		except IOError:
       
   417 			scanner.ReportError("IOError : Unable to create output file " + outputPath)
       
   418 		else:
       
   419 			errors = aXmlRenderer.iErrors
       
   420 			level = 0
       
   421 			indent = "   "
       
   422 			outputFile.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
       
   423 			outputFile.write(level * indent + "<problemIndex>\n")
       
   424 			level += 1
       
   425 			for category in KCategoryHtmlDisplayOrder:
       
   426 				found = False
       
   427 				if scanner.iCategoriedScripts.iScripts.has_key(category):
       
   428 					for script in scanner.iCategoriedScripts.iScripts[category]:
       
   429 						if errors.has_key(script.iScriptName):
       
   430 							found = True
       
   431 							break
       
   432 					if found:
       
   433 						outputFile.write(level * indent + "<category")
       
   434 						outputFile.write(" name=\"" + KCategoryConfigMap[category] + "\">\n")
       
   435 						level += 1
       
   436 						for script in scanner.iCategoriedScripts.iScripts[category]:
       
   437 							if errors.has_key(script.iScriptName):
       
   438 								outputFile.write(level * indent + "<problem")
       
   439 								outputFile.write(" name=\"" + script.iScriptName + "\"")
       
   440 								outputFile.write(" severity=\"" + KSeverityConfigMap[script.iSeverity] + "\">\n")
       
   441 								level += 1
       
   442 								for fileName, lines in errors[script.iScriptName].items():
       
   443 									outputFile.write(level * indent + "<file")
       
   444 									outputFile.write(" path=\"" + fileName + "\">\n")
       
   445 									level += 1
       
   446 									for lineNo in lines:
       
   447 										outputFile.write(level * indent + str(lineNo) + "\n")
       
   448 									level -= 1
       
   449 									outputFile.write(level * indent + "</file>\n")
       
   450 								level -= 1
       
   451 								outputFile.write(level * indent + "</problem>\n")
       
   452 						level -= 1
       
   453 						outputFile.write(level * indent + "</category>\n")
       
   454 			level -= 1
       
   455 			outputFile.write(level * indent + "</problemIndex>\n")
       
   456 			outputFile.close()
       
   457 
       
   458 
       
   459 class CXmlRenderer(CRendererBase):
       
   460 
       
   461 	# ########################################
       
   462 	# CXmlRenderer - a renderer for XML output
       
   463 
       
   464 	def __init__(self, aRendererManager, aOutputDirectory):
       
   465 		self.RegisterSelf("xml", "XML renderer", aRendererManager)
       
   466 		self.iOutputDirectory = aOutputDirectory
       
   467 		if os.path.isdir(self.iOutputDirectory) != True :
       
   468 			os.makedirs(self.iOutputDirectory)
       
   469 		self.iErrors = {} 
       
   470 		print
       
   471 		print KVersion
       
   472 		print KCopyrightLine1
       
   473 		print KCopyrightLine2
       
   474 
       
   475 	def BeginComponent(self, aComponent):
       
   476 		return
       
   477 
       
   478 	def BeginFile(self, aFilename):
       
   479 		self.iFilename = aFilename
       
   480 		scanner.ReportAction("Scanning file " + aFilename)
       
   481 
       
   482 	def ReportError(self, aLineContext, aScript):
       
   483 		scriptName = aScript.iScriptName
       
   484 		fileName = aLineContext.iFileName
       
   485 		lineNumber = aLineContext.iLineNumber
       
   486 		if (not self.iErrors.has_key(scriptName)):
       
   487 			self.iErrors[scriptName] = {}
       
   488 		if (not self.iErrors[scriptName].has_key(fileName)):
       
   489 			self.iErrors[scriptName][fileName] = []
       
   490 		self.iErrors[scriptName][fileName].append(lineNumber)
       
   491 
       
   492 	def EndFile(self):
       
   493 		#tbd
       
   494 		return
       
   495 
       
   496 	def EndComponent(self, aComponent):
       
   497 		relativeComponentName = scanner.iComponentManager.RelativeComponentName(aComponent.iFullPath)
       
   498 		if len(relativeComponentName) < 1:	# root component - final component
       
   499 			scanner.iEndTime = datetime.datetime.now().ctime()
       
   500 			componentSummaryFile = CXmlComponentSummaryFile()
       
   501 			componentSummaryFile.CreateSummary(self)
       
   502 
       
   503 
       
   504 class CHtmlOutputFileBase:
       
   505 
       
   506 	# #######################################################
       
   507 	# CHtmlOutputFileBase - base class for HTML output files
       
   508 
       
   509 	def WriteHeader(self, aOutputFile):
       
   510 		aOutputFile.write("<html><body>")
       
   511 
       
   512 	def Write(self, aOutputFile, aText):
       
   513 		aOutputFile.write(aText)
       
   514 
       
   515 	def WriteLink(self, aOutputFile, aHref, aText):
       
   516 		aHref = self.CleanupLink(aHref)
       
   517 		aOutputFile.write("<a href=\"" + aHref + "\">" + aText + "</a>")
       
   518 
       
   519 	def WriteElement(self, aOutputFile, aElementName, aElementValue):
       
   520 		aOutputFile.write("<"+aElementName+">"+aElementValue+"</"+aElementName+">")
       
   521 
       
   522 	def WriteBreak(self, aOutputFile):
       
   523 		aOutputFile.write("<br>")
       
   524 
       
   525 	def WriteFooter(self, aOutputFile):
       
   526 		aOutputFile.write("<br><hr><center><h5>"+KCopyrightLine1Html+"</h5>")
       
   527 		aOutputFile.write("<h5>")
       
   528 		CHtmlOutputFileBase.WriteLink(self, aOutputFile, "http://"+KWww, KWww)
       
   529 		aOutputFile.write("</h5></center></body></html>")
       
   530 
       
   531 	def CleanupLink(self, aHref):
       
   532 		# Mozilla Firefox does not handle link with the '#' character correctly, 
       
   533 		# so we need to replace it with the equivalent URL encoding "%23"
       
   534 		aHref = aHref.replace("#", "%23")
       
   535 		# Mozilla Firefox sometimes does not handle link with '\' correctly,
       
   536 		# so we need to replace it with '/'
       
   537 		aHref = aHref.replace('\\', '/')
       
   538 		return aHref
       
   539 
       
   540 
       
   541 class CHtmlOutputFile(CHtmlOutputFileBase):
       
   542 
       
   543 	# #######################################################
       
   544 	# CHtmlOutputFile - simplified access to HTML output file
       
   545 
       
   546 	def __init__(self, aOutputPath):
       
   547 		if not os.path.isdir(os.path.dirname(aOutputPath)):
       
   548 			os.makedirs(os.path.dirname(aOutputPath))
       
   549 		self.iOutputFile = file(aOutputPath, "w")
       
   550 		self.WriteHeader(self.iOutputFile)
       
   551 
       
   552 	def Write(self, aText):
       
   553 		CHtmlOutputFileBase.Write(self, self.iOutputFile, aText)
       
   554 
       
   555 	def WriteLink(self, aHref, aText):
       
   556 		CHtmlOutputFileBase.WriteLink(self, self.iOutputFile, aHref, aText)
       
   557 
       
   558 	def WriteElement(self, aElementName, aElementValue):
       
   559 		CHtmlOutputFileBase.WriteElement(self, self.iOutputFile, aElementName, aElementValue)
       
   560 
       
   561 	def WriteBreak(self):
       
   562 		CHtmlOutputFileBase.WriteBreak(self, self.iOutputFile)
       
   563 		
       
   564 	def Close(self):
       
   565 		self.WriteFooter(self.iOutputFile)
       
   566 		self.iOutputFile.close()
       
   567 
       
   568 
       
   569 class CHtmlComponentSummaryFiles:
       
   570 
       
   571 	# #######################################################
       
   572 	# CHtmlComponentSummaryFiles
       
   573 	# Encapsulates the component summary files for HTML output.
       
   574 	# For each component, there is a component report file listing the number 
       
   575 	# of occurrences of each problem type. There is also a single index or 
       
   576 	# summary file with links to each of the component report files.
       
   577 
       
   578 	def CreateSummaries(self, aHtmlRenderer, aOutputDirectory):
       
   579 		totalErrorCount = 0
       
   580 		outputPath = os.path.normpath(os.path.join(aOutputDirectory, "componentIndex.html"))
       
   581 		componentSummaryFile = CHtmlOutputFile(outputPath)
       
   582 		componentSummaryFile.Write("<font face=verdana>")
       
   583 		componentSummaryFile.WriteElement("h2", "Component Summary")
       
   584 		componentSummaryFile.Write("Source: "+scanner.iSource)
       
   585 		componentSummaryFile.WriteBreak()
       
   586 		componentSummaryFile.Write("Scan started at:   " + scanner.iStartTime)
       
   587 		componentSummaryFile.WriteBreak()
       
   588 		componentSummaryFile.Write("Scan completed at: " + scanner.iEndTime)
       
   589 		componentSummaryFile.WriteBreak()
       
   590 		componentSummaryFile.WriteBreak()
       
   591 		componentSummaryFile.WriteLink("problemIndex.html", "View problems by type")
       
   592 		componentSummaryFile.WriteBreak()
       
   593 		componentSummaryFile.Write("<hr>")
       
   594 		componentSummaryFile.WriteBreak()
       
   595 		componentSummaryFile.Write("<table border=\"1\" width=\"100%\">")
       
   596 		componentSummaryFile.Write("<tr bgcolor=\"#0099ff\">")
       
   597 		componentSummaryFile.WriteElement("th width=\"75%\"", "Component")
       
   598 		componentSummaryFile.WriteElement("th", "Items Found")
       
   599 		componentSummaryFile.WriteElement("th", "Lines of Code")
       
   600 		componentSummaryFile.WriteElement("th", "Possible Defects/KLOC")
       
   601 		componentSummaryFile.Write("</tr>")
       
   602 		for component in scanner.iComponentManager.iCompletedComponents:
       
   603 			componentName = scanner.iComponentManager.ComponentName(component.iFullPath)
       
   604 			outputPath = os.path.normpath(os.path.join(aOutputDirectory, "byComponent"))
       
   605 			outputPath = os.path.normpath(os.path.join(outputPath, componentName))
       
   606 			outputPath = os.path.normpath(os.path.join(outputPath, "componentSummary.html"))
       
   607 			errorCount = self.WriteComponentReport(aHtmlRenderer, outputPath, component.iFullPath, componentName)
       
   608 			if (errorCount > 0):
       
   609 				totalErrorCount = totalErrorCount + errorCount
       
   610 				numberOfLinesScanned = component.iNumberOfLinesScanned
       
   611 				if (numberOfLinesScanned > 0):
       
   612 					defectsPerKLOC = int((1000.0 / numberOfLinesScanned) * errorCount)
       
   613 				else:
       
   614 					defectsPerKLOC = 0
       
   615 				componentSummaryFile.Write("<tr>")
       
   616 				componentSummaryFile.Write("<td>")
       
   617 				relOutputPath = os.path.normpath(os.path.join("byComponent", componentName))
       
   618 				relOutputPath = os.path.normpath(os.path.join(relOutputPath, "componentSummary.html"))
       
   619 				componentSummaryFile.WriteLink(relOutputPath, component.iFullPath)
       
   620 				componentSummaryFile.Write("</td>")
       
   621 				componentSummaryFile.Write("<td>")
       
   622 				componentSummaryFile.WriteElement("center",str(errorCount))
       
   623 				componentSummaryFile.Write("</td>")
       
   624 				componentSummaryFile.Write("<td>")
       
   625 				componentSummaryFile.WriteElement("center",str(numberOfLinesScanned))
       
   626 				componentSummaryFile.Write("</td>")
       
   627 				componentSummaryFile.Write("<td>")
       
   628 				componentSummaryFile.WriteElement("center",str(defectsPerKLOC))
       
   629 				componentSummaryFile.Write("</td>")
       
   630 				componentSummaryFile.Write("</tr>")
       
   631 
       
   632 		componentSummaryFile.Write("<tr>")
       
   633 		componentSummaryFile.Write("<td>")
       
   634 		componentSummaryFile.WriteElement("b", "Total")
       
   635 		componentSummaryFile.Write("</td>")
       
   636 		componentSummaryFile.Write("<td><center>")
       
   637 		componentSummaryFile.WriteElement("b", str(totalErrorCount))
       
   638 		componentSummaryFile.Write("</center></td>")
       
   639 		componentSummaryFile.Write("</tr>")
       
   640 
       
   641 		componentSummaryFile.Write("</table>")
       
   642 		componentSummaryFile.Close()
       
   643 
       
   644 	def WriteComponentReport(self, aHtmlRenderer, aOutputPath, aComponentFullPath, aComponentName):
       
   645 		totalErrorCount = 0
       
   646 		componentReportFile = CHtmlOutputFile(aOutputPath)
       
   647 		componentReportFile.Write("<font face=verdana>")
       
   648 		componentReportFile.WriteElement("h2", "Component Report")
       
   649 		componentReportFile.WriteElement("h3", "Component: "+aComponentFullPath)
       
   650 		componentReportFile.Write("<font face=verdana color=black>")
       
   651 		found = False
       
   652 		for category in KCategoryHtmlDisplayOrder:
       
   653 			if scanner.iCategoriedScripts.iScripts.has_key(category):
       
   654 				for script in scanner.iCategoriedScripts.iScripts[category]:
       
   655 					errorCount = scanner.iComponentManager.ScriptComponentErrorCount(aComponentFullPath, script.iScriptName)
       
   656 					if errorCount > 0:
       
   657 						found = True
       
   658 						break
       
   659 
       
   660 		if found:
       
   661 			componentReportFile.Write("<table border=\"1\" width=\"100%\">")
       
   662 			componentReportFile.Write("<tr bgcolor=\"#0099ff\">")
       
   663 			componentReportFile.WriteElement("th width=\"75%\"", "Problem")
       
   664 			componentReportFile.WriteElement("th", "Items Found")
       
   665 			componentReportFile.WriteElement("th", "Severity")
       
   666 			componentReportFile.Write("</tr>")
       
   667 			for category in KCategoryHtmlDisplayOrder:
       
   668 				if scanner.iCategoriedScripts.iScripts.has_key(category):
       
   669 					for script in scanner.iCategoriedScripts.iScripts[category]:
       
   670 						errorCount = scanner.iComponentManager.ScriptComponentErrorCount(aComponentFullPath, script.iScriptName)
       
   671 						if errorCount > 0:
       
   672 							componentReportFile.Write("<tr>")
       
   673 							componentReportFile.Write("<td>")
       
   674 							#scriptComponentPath = aHtmlRenderer.ScriptComponentPath(aComponentFullPath, script.iScriptName)
       
   675 							#componentReportFile.WriteLink(scriptComponentPath, script.iTitle)
       
   676 							componentReportFile.WriteLink(script.iScriptName+".html", script.iTitle)
       
   677 							componentReportFile.Write("</td>")
       
   678 							componentReportFile.Write("<td>")
       
   679 							componentReportFile.WriteElement("center", str(errorCount))
       
   680 							componentReportFile.Write("</td>")
       
   681 							componentReportFile.Write("<td>")
       
   682 							componentReportFile.WriteElement("center", KSeverityHTMLMap[script.iSeverity])
       
   683 							componentReportFile.Write("</td>")
       
   684 							componentReportFile.Write("</tr>")
       
   685 							totalErrorCount = totalErrorCount + errorCount
       
   686 			componentReportFile.Write("<tr>")
       
   687 			componentReportFile.Write("<td>")
       
   688 			componentReportFile.WriteElement("b", "Total")
       
   689 			componentReportFile.Write("</td>")
       
   690 			componentReportFile.Write("<td><center>")
       
   691 			componentReportFile.WriteElement("b", str(totalErrorCount))
       
   692 			componentReportFile.Write("</center></td>")
       
   693 			componentReportFile.Write("</tr>")
       
   694 			componentReportFile.Write("</table>")
       
   695 		else:
       
   696 			componentReportFile.WriteBreak()
       
   697 			componentReportFile.WriteElement("i", "There are no items to report for this component.")
       
   698 			componentReportFile.WriteBreak()
       
   699 		componentReportFile.Close()
       
   700 		return totalErrorCount
       
   701 
       
   702 
       
   703 class CHtmlScriptSummaryFiles:
       
   704 
       
   705 	# #######################################################
       
   706 	# CHtmlScriptSummaryFiles
       
   707 	# Encapsulates the script (problem) summary files for HTML output.
       
   708 	# For each script, there is a file listing the number of occurrences
       
   709 	# of that script's problem for each component. There is also a single
       
   710 	# index or summary file with links to each of the problem report file.
       
   711 
       
   712 	def CreateSummaries(self, aHtmlRenderer, aOutputDirectory):
       
   713 
       
   714 		totalErrorCount = 0
       
   715 
       
   716 		outputPath = os.path.normpath(os.path.join(aOutputDirectory, "problemIndex.html"))
       
   717 		scriptSummaryFile = CHtmlOutputFile(outputPath)
       
   718 		scriptSummaryFile.Write("<font face=verdana>")
       
   719 		scriptSummaryFile.WriteElement("h2", "Problem Summary")
       
   720 		scriptSummaryFile.Write("Source: "+scanner.iSource)
       
   721 		scriptSummaryFile.WriteBreak()
       
   722 		scriptSummaryFile.Write("Scan started at:   " + scanner.iStartTime)
       
   723 		scriptSummaryFile.WriteBreak()
       
   724 		scriptSummaryFile.Write("Scan completed at: " + scanner.iEndTime)
       
   725 		scriptSummaryFile.WriteBreak()
       
   726 		scriptSummaryFile.WriteBreak()
       
   727 		scriptSummaryFile.WriteLink("componentIndex.html", "View problems by component")
       
   728 		scriptSummaryFile.WriteBreak()
       
   729 		scriptSummaryFile.Write("<hr>")
       
   730 		scriptSummaryFile.WriteBreak()
       
   731 		for category in KCategoryHtmlDisplayOrder:
       
   732 			if scanner.iCategoriedScripts.iScripts.has_key(category):
       
   733 				scriptSummaryFile.WriteElement("h3", "Category: "+category)
       
   734 				scriptSummaryFile.Write("<table border=\"1\" width=\"100%\">")
       
   735 				scriptSummaryFile.Write("<tr bgcolor=\"#0099ff\">")
       
   736 				scriptSummaryFile.WriteElement("th width=\"75%\"", "Problem")
       
   737 				scriptSummaryFile.WriteElement("th", "Items Found")
       
   738 				scriptSummaryFile.WriteElement("th", "Severity")
       
   739 				scriptSummaryFile.Write("</tr>")
       
   740 				categoryErrorCount = 0
       
   741 				for script in scanner.iCategoriedScripts.iScripts[category]:
       
   742 					outputPath = os.path.normpath(os.path.join(aOutputDirectory, "byProblem"))
       
   743 					outputPath = os.path.normpath(os.path.join(outputPath, script.iScriptName+"Summary.html"))
       
   744 					errorCount = self.WriteScriptReport(aHtmlRenderer, outputPath, script)
       
   745 					categoryErrorCount = categoryErrorCount + errorCount
       
   746 					scriptSummaryFile.Write("<tr>")
       
   747 					scriptSummaryFile.Write("<td>")
       
   748 					relOutputPath = os.path.normpath(os.path.join("byProblem", script.iScriptName+"Summary.html"))
       
   749 					scriptSummaryFile.WriteLink(relOutputPath, script.iTitle)
       
   750 					scriptSummaryFile.Write("</td>")
       
   751 					scriptSummaryFile.Write("<td>")
       
   752 					scriptSummaryFile.WriteElement("center", str(errorCount))
       
   753 					scriptSummaryFile.Write("</td>")
       
   754 					scriptSummaryFile.Write("<td>")
       
   755 					scriptSummaryFile.WriteElement("center", KSeverityHTMLMap[script.iSeverity])
       
   756 					scriptSummaryFile.Write("</td>")
       
   757 					scriptSummaryFile.Write("</tr>")
       
   758 				totalErrorCount = totalErrorCount + categoryErrorCount
       
   759 				scriptSummaryFile.Write("<tr>")
       
   760 				scriptSummaryFile.Write("<td>")
       
   761 				scriptSummaryFile.WriteElement("b", "Category Total")
       
   762 				scriptSummaryFile.Write("</td>")
       
   763 				scriptSummaryFile.Write("<td>")
       
   764 				scriptSummaryFile.WriteElement("center", "<b>"+str(categoryErrorCount)+"</b>")
       
   765 				scriptSummaryFile.Write("</td>")
       
   766 				scriptSummaryFile.Write("</tr>")
       
   767 				scriptSummaryFile.Write("</table>")
       
   768 
       
   769 		scriptSummaryFile.WriteBreak()
       
   770 		scriptSummaryFile.WriteElement("b", "Total: " + str(totalErrorCount))
       
   771 		scriptSummaryFile.WriteBreak()
       
   772 
       
   773 		scriptSummaryFile.Close()
       
   774 
       
   775 	def WriteScriptReport(self, aHtmlRenderer, aOutputPath, aScript):
       
   776 		totalErrorCount = 0
       
   777 		scriptReportFile = CHtmlOutputFile(aOutputPath)
       
   778 		scriptReportFile.Write("<font face=verdana>")
       
   779 		scriptReportFile.WriteElement("h2", "Problem Report")
       
   780 		scriptReportFile.WriteElement("h3", "Problem: " + aScript.iTitle)
       
   781 		scriptReportFile.Write(aScript.iDescription)
       
   782 		scriptReportFile.WriteBreak()
       
   783 		scriptReportFile.WriteBreak()
       
   784 
       
   785 		found = False
       
   786 		for component in scanner.iComponentManager.iCompletedComponents:
       
   787 			errorCount = scanner.iComponentManager.ScriptComponentErrorCount(component.iFullPath, aScript.iScriptName)
       
   788 			if errorCount > 0:
       
   789 				found = True
       
   790 				break
       
   791 
       
   792 		if found:
       
   793 			scriptReportFile.Write("<table border=\"1\" width=\"100%\">")
       
   794 			scriptReportFile.Write("<tr bgcolor=\"#0099ff\">")
       
   795 			scriptReportFile.WriteElement("th width=\"80%\"", "Component")
       
   796 			scriptReportFile.WriteElement("th", "Items Found")
       
   797 			scriptReportFile.Write("</tr>")
       
   798 			for component in scanner.iComponentManager.iCompletedComponents:
       
   799 				errorCount = scanner.iComponentManager.ScriptComponentErrorCount(component.iFullPath, aScript.iScriptName)
       
   800 				if errorCount > 0:
       
   801 					scriptReportFile.Write("<tr>")
       
   802 					scriptReportFile.Write("<td>")
       
   803 					scriptComponentPath = aHtmlRenderer.ScriptComponentPath(component.iFullPath, aScript.iScriptName, "..")
       
   804 					scriptReportFile.WriteLink(scriptComponentPath, component.iFullPath)
       
   805 					scriptReportFile.Write("</td>")
       
   806 					scriptReportFile.Write("<td>")
       
   807 					scriptReportFile.WriteElement("center", str(errorCount))
       
   808 					scriptReportFile.Write("</td>")
       
   809 					scriptReportFile.Write("</tr>")
       
   810 					totalErrorCount = totalErrorCount + errorCount
       
   811 			scriptReportFile.Write("<tr>")
       
   812 			scriptReportFile.Write("<td>")
       
   813 			scriptReportFile.WriteElement("b", "Total")
       
   814 			scriptReportFile.Write("</td>")
       
   815 			scriptReportFile.Write("<td><center>")
       
   816 			scriptReportFile.WriteElement("b", str(totalErrorCount))
       
   817 			scriptReportFile.Write("</center></td>")
       
   818 			scriptReportFile.Write("</tr>")
       
   819 			scriptReportFile.Write("</table>")
       
   820 		else:
       
   821 			scriptReportFile.WriteBreak()
       
   822 			scriptReportFile.WriteElement("i", "There are no items of this problem type to report.")
       
   823 			scriptReportFile.WriteBreak()
       
   824 
       
   825 		scriptReportFile.Close()
       
   826 		return totalErrorCount
       
   827 
       
   828 
       
   829 class CHtmlScriptComponentFile:
       
   830 
       
   831 	# #######################################################
       
   832 	# CHtmlScriptComponentFile
       
   833 	# Encapsulates access to the HTML output files with the greatest amount of detail.
       
   834 	# Each of these files is for a specific problem (script) and for a specific component.
       
   835 
       
   836 	# The file handle is closed between each call to avoid exhausting the system
       
   837 	# limit for open file handles. Many of these files may be open at one time,
       
   838 	# and the number of open files is dependent on both the directory structure
       
   839 	# being traversed and the number of types of problems found.
       
   840 
       
   841 	def __init__(self, aLxrUrl, aLxrVersion):
       
   842 		self.iLxrUrl = aLxrUrl
       
   843 		self.iLxrVersion = aLxrVersion
       
   844 		
       
   845 	def BeginOutputFile(self, aOutputPath, aScript, aComponentName):
       
   846 		if not os.path.isdir(os.path.dirname(aOutputPath)):
       
   847 			os.makedirs(os.path.dirname(aOutputPath))
       
   848 		outputFile = file(aOutputPath, "w")
       
   849 		self.iScriptComponentFile = CHtmlOutputFileBase()
       
   850 		self.iScriptComponentFile.Write(outputFile, "<font face=verdana>")
       
   851 		self.iScriptComponentFile.WriteHeader(outputFile)
       
   852 		self.iScriptComponentFile.WriteElement(outputFile, "h2", "Detailed Problem Report")
       
   853 		self.iScriptComponentFile.WriteElement(outputFile, "h3", "Component: "+aComponentName)
       
   854 		self.iScriptComponentFile.WriteElement(outputFile, "h3", "Problem: "+aScript.iTitle)
       
   855 		self.iScriptComponentFile.Write(outputFile, aScript.iDescription)
       
   856 		self.iScriptComponentFile.WriteBreak(outputFile)
       
   857 		self.iScriptComponentFile.Write(outputFile, "<hr>")
       
   858 		self.iScriptComponentFile.WriteBreak(outputFile)
       
   859 		outputFile.close()
       
   860 
       
   861 	def ReportError(self, aOutputPath, aLineContext):
       
   862 		outputFile = file(aOutputPath, "a")
       
   863 		if self.iLxrUrl == None:
       
   864 			# Mozilla Firefox cannot open links to local files, 
       
   865 			# so it is necessary to convert local file path
       
   866 			filePath = "file:///" + aLineContext.iFileName
       
   867 		else:
       
   868 			# generate link to LXR server instead of local file system
       
   869 			filePath = self.iLxrUrl + aLineContext.iFileName[len(scanner.iComponentManager.iRootPath):]
       
   870 			if self.iLxrVersion <> None:
       
   871 				filePath = filePath + "?v="+self.iLxrVersion
       
   872 			filePath = filePath + '#%03d'%aLineContext.iLineNumber
       
   873 		self.iScriptComponentFile.WriteLink(outputFile, filePath, self.TrimFileName(aLineContext))
       
   874 		self.iScriptComponentFile.Write(outputFile, "(" + str(aLineContext.iLineNumber) + ") ")
       
   875 		self.iScriptComponentFile.Write(outputFile, aLineContext.iClassName+"::"+aLineContext.iMethodName+" ")
       
   876 		self.iScriptComponentFile.Write(outputFile, "<code><font color=red>"+self.CleanUpText(aLineContext.iLineText))
       
   877 		self.iScriptComponentFile.Write(outputFile, "</font></code>")
       
   878 		self.iScriptComponentFile.WriteBreak(outputFile)
       
   879 		if len(scanner.iRendererManager.iAnnotation)>0:
       
   880 			self.iScriptComponentFile.Write(outputFile, scanner.iRendererManager.iAnnotation)
       
   881 			self.iScriptComponentFile.WriteBreak(outputFile)
       
   882 			scanner.iRendererManager.iAnnotation = ""
       
   883 		outputFile.close()
       
   884 
       
   885 	def EndOutputFile(self, aOutputPath):
       
   886 		outputFile = file(aOutputPath, "a")
       
   887 		self.iScriptComponentFile.WriteFooter(outputFile)
       
   888 		outputFile.close()
       
   889 
       
   890 	def TrimFileName(self, aLineContext):
       
   891 		filename = aLineContext.iFileName
       
   892 		componentNameLen = len(aLineContext.iComponentName)
       
   893 		if len(filename) > componentNameLen:
       
   894 			if filename[0:componentNameLen] == aLineContext.iComponentName:
       
   895 				filename = filename[componentNameLen:]
       
   896 				if filename[0] == os.path.sep:
       
   897 					filename = filename[1:]
       
   898 		return filename
       
   899 
       
   900 	def CleanUpText(self, aLineText):
       
   901 		# check for sub-strings that look like HTML tags and preform clean up if needed
       
   902 		reTag = re.compile(r"""(<.+>)""", re.VERBOSE)
       
   903 		foundTag = reTag.search(aLineText)
       
   904 		if foundTag:
       
   905 			aNewLineText = aLineText.replace("<", "&lt;")
       
   906 			aNewLineText = aNewLineText.replace(">", "&gt;")
       
   907 			return aNewLineText
       
   908 		else:
       
   909 			return aLineText		
       
   910 
       
   911 			
       
   912 def ComponentCompare(a, b):
       
   913 	return cmp(os.path.normcase(a.iFullPath), os.path.normcase(b.iFullPath))
       
   914 
       
   915 
       
   916 class CHtmlRenderer(CRendererBase):
       
   917 
       
   918 	# #######################################################
       
   919 	# CHtmlRenderer - a renderer for HTML output
       
   920 
       
   921 	# I have nothing to offer but blood, toil, tears and sweat. 
       
   922 	#  - Winston Churchill, 1940 
       
   923 
       
   924 	def __init__(self, aRendererManager, aOutputDirectory, aLxrUrl, aLxrVersion):
       
   925 		self.RegisterSelf("html", "HTML renderer", aRendererManager)
       
   926 		self.iOutputDirectory = aOutputDirectory
       
   927 		if os.path.isdir(self.iOutputDirectory) != True :
       
   928 			os.makedirs(self.iOutputDirectory)
       
   929 		self.iScriptComponentFile = CHtmlScriptComponentFile(aLxrUrl, aLxrVersion)
       
   930 		self.iScriptComponentFilePaths = {}
       
   931 		print
       
   932 		print KVersion
       
   933 		print KCopyrightLine1
       
   934 		print KCopyrightLine2
       
   935 
       
   936 	def BeginFile(self, aFilename):
       
   937 		self.iFilename = aFilename
       
   938 		scanner.ReportAction("Scanning file " + aFilename)
       
   939 
       
   940 	def ReportError(self, aLineContext, aScript):
       
   941 		outputPath = self.ScriptComponentPath(aLineContext.iComponentName, aScript.iScriptName)
       
   942 		if not os.path.isfile(outputPath):
       
   943 			self.iScriptComponentFilePaths[aLineContext.iComponentName].append(outputPath)
       
   944 			self.iScriptComponentFile.BeginOutputFile(outputPath, aScript, aLineContext.iComponentName)
       
   945 		self.iScriptComponentFile.ReportError(outputPath, aLineContext)
       
   946 
       
   947 	def EndFile(self):
       
   948 		return
       
   949 
       
   950 	def BeginComponent(self, aComponent):
       
   951 		self.iScriptComponentFilePaths[aComponent.iFullPath] = []
       
   952 
       
   953 	def EndComponent(self, aComponent):
       
   954 		if self.iScriptComponentFilePaths.has_key(aComponent.iFullPath):
       
   955 			for outputPath in self.iScriptComponentFilePaths[aComponent.iFullPath]:
       
   956 				self.iScriptComponentFile.EndOutputFile(outputPath)
       
   957 			del self.iScriptComponentFilePaths[aComponent.iFullPath]
       
   958 		relativeComponentName = scanner.iComponentManager.RelativeComponentName(aComponent.iFullPath)
       
   959 		if len(relativeComponentName) < 1:	# root component - final component
       
   960 			scanner.iEndTime = datetime.datetime.now().ctime()
       
   961 			scanner.iComponentManager.iCompletedComponents.sort(ComponentCompare)
       
   962 			scriptSummaryFiles = CHtmlScriptSummaryFiles()
       
   963 			scriptSummaryFiles.CreateSummaries(self, self.iOutputDirectory)
       
   964 			componentSummaryFiles = CHtmlComponentSummaryFiles()
       
   965 			componentSummaryFiles.CreateSummaries(self, self.iOutputDirectory)
       
   966 
       
   967 	def ScriptComponentPath(self, aComponentName, aScriptName, aRel=None):
       
   968 		componentName = scanner.iComponentManager.ComponentName(aComponentName)
       
   969 		if aRel==None:
       
   970 			aRel = self.iOutputDirectory
       
   971 		outputPath = os.path.normpath(os.path.join(aRel, "byComponent"))
       
   972 		outputPath = os.path.normpath(os.path.join(outputPath, componentName))
       
   973 		outputPath = os.path.normpath(os.path.join(outputPath, aScriptName+".html"))
       
   974 		return outputPath
       
   975 
       
   976 
       
   977 class CRendererManager:
       
   978 
       
   979 	# #######################################################
       
   980 	# CRendererManager
       
   981 	# this class handles all the renderers 
       
   982 
       
   983 	def __init__(self):
       
   984 		# declare associative list of renderers: iRendererList[name]=renderer
       
   985 		self.iRendererList = {}
       
   986 		self.iAnnotation = ""
       
   987 
       
   988 	def AddRenderer(self, aRenderer):
       
   989 		self.iRendererList[aRenderer.iName.lower()] = aRenderer
       
   990 
       
   991 	def PrintListOfRenderers(self):
       
   992 		print("Renderers:")        
       
   993 		for name, renderer in self.iRendererList.items():
       
   994 			print("\t" + name + "\t" + renderer.iDescription)
       
   995 
       
   996 		print("")
       
   997 
       
   998 	def BeginFile(self, aFilename):
       
   999 		for name, renderer in self.iRendererList.items():
       
  1000 			renderer.BeginFile(aFilename)
       
  1001 
       
  1002 	def ReportError(self, aLineContext, aScript):
       
  1003 		for name, renderer in self.iRendererList.items():
       
  1004 			renderer.ReportError(aLineContext, aScript)
       
  1005 
       
  1006 	def ReportAnnotation(self, aAnnotation):
       
  1007 		self.iAnnotation = aAnnotation
       
  1008 
       
  1009 	def EndFile(self):
       
  1010 		for name, renderer in self.iRendererList.items():
       
  1011 			renderer.EndFile()
       
  1012 
       
  1013 	def BeginComponent(self, aComponent):
       
  1014 		for name, renderer in self.iRendererList.items():
       
  1015 			renderer.BeginComponent(aComponent)
       
  1016 
       
  1017 	def EndComponent(self, aComponent):
       
  1018 		for name, renderer in self.iRendererList.items():
       
  1019 			renderer.EndComponent(aComponent)
       
  1020 
       
  1021 
       
  1022 class CComponent:
       
  1023 
       
  1024 	# #######################################################
       
  1025 	# CComponent - a single component, identified by the
       
  1026 	# directory path to its source code
       
  1027 
       
  1028 	def __init__(self, aPath):
       
  1029 		self.iFullPath = aPath
       
  1030 		self.iScriptErrorCounts = {}
       
  1031 		self.iHasGroupDir = False
       
  1032 		self.iNumberOfLinesScanned = 0
       
  1033 
       
  1034 	def appendComponent(self, aComponent):
       
  1035 		for scriptName in aComponent.iScriptErrorCounts.keys():
       
  1036 			if self.iScriptErrorCounts.has_key(scriptName):
       
  1037 				self.iScriptErrorCounts[scriptName] += aComponent.iScriptErrorCounts[scriptName]
       
  1038 			else:
       
  1039 				self.iScriptErrorCounts[scriptName] = aComponent.iScriptErrorCounts[scriptName]
       
  1040 		self.iNumberOfLinesScanned += aComponent.iNumberOfLinesScanned
       
  1041 		return
       
  1042 
       
  1043 
       
  1044 class CComponentManager:
       
  1045 
       
  1046 	# #######################################################
       
  1047 	# CComponentManager - controls access to components
       
  1048 
       
  1049 	def __init__(self):
       
  1050 		self.iComponentStack = []
       
  1051 		self.iCompletedComponents = []
       
  1052 		self.iRootComponent = CComponent("")
       
  1053 		self.iUseFullComponentPath = False
       
  1054 
       
  1055 	def SetRoot(self, aRootPath):
       
  1056 		# set the list of root directories - used to left-trim component names
       
  1057 		self.iRootPath = self.SanitizePath(aRootPath)
       
  1058 
       
  1059 	def BeginDirectory(self, aPath):
       
  1060 		aPath = self.SanitizePath(aPath)
       
  1061 		if os.path.isdir(aPath):
       
  1062 			newComponent = CComponent(aPath)
       
  1063 			contents = os.listdir(aPath)
       
  1064 			for entry in contents:
       
  1065 				if (entry.upper() == "GROUP"):
       
  1066 					entryPath = os.path.normpath(os.path.join(aPath, entry))
       
  1067 					if os.path.isdir(entryPath):
       
  1068 						newComponent.iHasGroupDir = True
       
  1069 						break
       
  1070 			if len(self.iComponentStack) > 0:
       
  1071 				topComponent = self.iComponentStack[len(self.iComponentStack)-1]
       
  1072 				if (newComponent.iHasGroupDir or (not topComponent.iHasGroupDir)):
       
  1073 					self.BeginComponent(newComponent)
       
  1074 				else:
       
  1075 					scanner.iLog.Write(aPath + " taken as part of " + topComponent.iFullPath)
       
  1076 			else:
       
  1077 				self.BeginComponent(newComponent)
       
  1078 		else:
       
  1079 			scanner.iLog.Write("ERROR: CComponentManager::BeginDirectory: bad path "+aPath)
       
  1080 		return aPath
       
  1081 
       
  1082 	def EndDirectory(self, aPath, numberOfLinesScanned):
       
  1083 		aPath = self.SanitizePath(aPath)
       
  1084 		if len(self.iComponentStack) > 0:
       
  1085 			topComponent = self.iComponentStack[len(self.iComponentStack)-1]
       
  1086 			topComponent.iNumberOfLinesScanned += numberOfLinesScanned
       
  1087 			if (topComponent.iFullPath == aPath):
       
  1088 				self.EndComponent()
       
  1089 
       
  1090 	def ReportError(self, aLineContext, aScript):
       
  1091 		scanner.iRendererManager.ReportError(aLineContext, aScript)
       
  1092 		for component in self.iComponentStack:
       
  1093 			if component.iFullPath == aLineContext.iComponentName:
       
  1094 				if component.iScriptErrorCounts.has_key(aScript.iScriptName):
       
  1095 					component.iScriptErrorCounts[aScript.iScriptName] = component.iScriptErrorCounts[aScript.iScriptName] + 1
       
  1096 				else:
       
  1097 					component.iScriptErrorCounts[aScript.iScriptName] = 1
       
  1098 
       
  1099 	def ScriptComponentErrorCount(self, aComponentName, aScriptName):
       
  1100 		for component in self.iCompletedComponents:
       
  1101 			if component.iFullPath == aComponentName:
       
  1102 				if component.iScriptErrorCounts.has_key(aScriptName):
       
  1103 					return component.iScriptErrorCounts[aScriptName]
       
  1104 				else:
       
  1105 					return 0
       
  1106 		return 0
       
  1107 
       
  1108 	def BeginComponent(self, aComponent):
       
  1109 		scanner.iRendererManager.BeginComponent(aComponent)
       
  1110 		scanner.ReportAction("Begin component: " + aComponent.iFullPath)
       
  1111 		self.iComponentStack.append(aComponent)
       
  1112 
       
  1113 	def EndComponent(self):
       
  1114 		previousComponent = self.iComponentStack.pop()
       
  1115 		matchingComponent = self.MatchingComponent(previousComponent)
       
  1116 		if (matchingComponent <> None):
       
  1117 			matchingComponent.appendComponent(previousComponent)
       
  1118 		else:
       
  1119 			self.iCompletedComponents.append(previousComponent)
       
  1120 		scanner.ReportAction("End component: " + previousComponent.iFullPath)
       
  1121 		scanner.iRendererManager.EndComponent(previousComponent)
       
  1122 
       
  1123 	def MatchingComponent(self, aComponent):
       
  1124 		for component in self.iCompletedComponents:
       
  1125 			if (ComponentCompare(component, aComponent) == 0):
       
  1126 				return component
       
  1127 		return None
       
  1128 
       
  1129 	def CurrentComponentName(self):
       
  1130 		if len(self.iComponentStack) < 1:
       
  1131 			return None
       
  1132 		return self.iComponentStack[len(self.iComponentStack)-1].iFullPath
       
  1133 
       
  1134 	def SanitizePath(self, aPath):
       
  1135 		# Translate an unspecified or relative pathname into an absolute pathname
       
  1136 		if len(aPath) < 1:
       
  1137 			aPath = "."
       
  1138 		aPath = os.path.normpath(aPath)
       
  1139 		# translate "." and ".." into absolute paths
       
  1140 		aPath = os.path.abspath(aPath)
       
  1141 		return aPath
       
  1142 
       
  1143 	def ComponentName(self, aFullComponentName):
       
  1144 		if (self.iUseFullComponentPath):
       
  1145 			(unused, componentName) = os.path.splitdrive(aFullComponentName)
       
  1146 			if len(componentName) > 0:
       
  1147 				if componentName[0] == os.path.sep and len(componentName) > 1:
       
  1148 					componentName = componentName[1:]
       
  1149 			return componentName
       
  1150 		else:
       
  1151 			return self.RelativeComponentName(aFullComponentName)
       
  1152 
       
  1153 	def RelativeComponentName(self, aFullComponentName):
       
  1154 		# Remove the root path from the specified component name
       
  1155 		rootLen = len(self.iRootPath)
       
  1156 		if aFullComponentName[0:rootLen] == self.iRootPath:
       
  1157 			relativeComponentName = aFullComponentName[rootLen:]
       
  1158 		else:
       
  1159 			# this case is unexpected...but we'll try to make the best of it
       
  1160 			(unused, relativeComponentName) = os.path.splitdrive(aFullComponentName)
       
  1161 		# trim leading path separator, if present
       
  1162 		if len(relativeComponentName) > 0:
       
  1163 			if relativeComponentName[0] == os.path.sep and len(relativeComponentName) > 1:
       
  1164 				relativeComponentName = relativeComponentName[1:]
       
  1165 		return relativeComponentName
       
  1166 
       
  1167 
       
  1168 class CLineContext:
       
  1169 
       
  1170 	# #######################################################
       
  1171 	# CLineContext
       
  1172 	# A description of the line of source code currently being scanned
       
  1173 
       
  1174 	iComponentName = ""
       
  1175 	iFileName = ""
       
  1176 	iLineNumber = 0
       
  1177 	iClassName = ""
       
  1178 	iMethodName = ""
       
  1179 	iLineText = ""
       
  1180 
       
  1181 
       
  1182 class CCodeScanner:
       
  1183 
       
  1184 	# #######################################################
       
  1185 	# CCodeScanner - main application class
       
  1186 
       
  1187 	def __init__(self):
       
  1188 		self.iCategoriedScripts = CCategorisedScripts()
       
  1189 		self.iRendererManager   = CRendererManager()
       
  1190 		self.iComponentManager = CComponentManager()
       
  1191 		self.iLineContext = CLineContext()
       
  1192 		self.iDomConfig = None
       
  1193 		self.iVerbose = False
       
  1194 		self.iLog = None
       
  1195 		self.iSource = None
       
  1196 		self.iEncodedFileList = None
       
  1197 		self.iOutputDirectory = None
       
  1198 		self.iStartTimeObj = None
       
  1199 		self.iStartTime = None
       
  1200 		self.iEndTime = None
       
  1201 		self.iLxrUrl = None
       
  1202 		self.iLxrVersion = None
       
  1203 		self.iConfigFilename = ""
       
  1204 		self.iInputFilenames = ""
       
  1205 		self.iLogFilename = ""
       
  1206 		self.iOutputFormat = ""
       
  1207 		self.iTimeStampedOutput = ""
       
  1208 		self.iReMethod = re.compile(r"""
       
  1209 			((?P<class>\w+)::~?)?
       
  1210 			(?P<method>[A-Za-z0-9<>=!*\-+/]+)
       
  1211 			[\s\n]*
       
  1212 			\(
       
  1213 			[^;]*
       
  1214 			$
       
  1215 			""", re.VERBOSE)
       
  1216 
       
  1217 	def ReportError(self, aErrorMsg):
       
  1218 		self.iLog.Write(aErrorMsg)
       
  1219 		print aErrorMsg
       
  1220 
       
  1221 	def ReportAction(self, aAction):
       
  1222 		self.iLog.Write(aAction)
       
  1223 		if self.iVerbose:
       
  1224 			print aAction
       
  1225 
       
  1226 	def ReportInfo(self, aInfoMsg):
       
  1227 		self.iLog.Write(aInfoMsg)
       
  1228 		print aInfoMsg
       
  1229 
       
  1230 	def CleanOutputDirectory(self):
       
  1231 		self.iLog.Write("Deleting existing contents of output directory " + self.iOutputDirectory)
       
  1232 		for root, dirs, files in os.walk(self.iOutputDirectory, topdown = False):
       
  1233 			for name in files:
       
  1234 				os.remove(os.path.join(root, name))
       
  1235 			for name in dirs:
       
  1236 				os.rmdir(os.path.join(root, name))
       
  1237 
       
  1238 	def CheckSourceIncluded(self, aSourceFileName):
       
  1239 		if (self.iDomConfig <> None):
       
  1240 			for sourceNode in self.iDomConfig.getElementsByTagName("sources"):
       
  1241 				for excludeSourceNode in sourceNode.getElementsByTagName("exclude"):
       
  1242 					reExcludeSourceStr = excludeSourceNode.firstChild.nodeValue
       
  1243 					reExcludeSource = re.compile(reExcludeSourceStr, re.IGNORECASE)
       
  1244 					if reExcludeSource.search(aSourceFileName):
       
  1245 						self.ReportInfo("Note: excluding " + aSourceFileName + " : " + reExcludeSourceStr)
       
  1246 						return False
       
  1247 		return True
       
  1248 
       
  1249 	def CheckScriptEnabled(self, aScript):
       
  1250 		if (self.iDomConfig <> None):
       
  1251 			for scriptsNode in self.iDomConfig.getElementsByTagName("scripts"):
       
  1252 				for scriptNode in scriptsNode.getElementsByTagName(aScript.iScriptName):
       
  1253 					enabledAttr = scriptNode.getAttribute("enable")
       
  1254 					if (enabledAttr.lower() == "false"):
       
  1255 						return False
       
  1256 			for severitiesNode in self.iDomConfig.getElementsByTagName("severities"):
       
  1257 				for severityNode in severitiesNode.getElementsByTagName(KSeverityConfigMap[aScript.iSeverity]):
       
  1258 					enabledAttr = severityNode.getAttribute("enable")
       
  1259 					if (enabledAttr.lower() == "false"):
       
  1260 						return False
       
  1261 			for categoriesNode in self.iDomConfig.getElementsByTagName("categories"):
       
  1262 				for categoryNode in categoriesNode.getElementsByTagName(KCategoryConfigMap[aScript.iCategory]):
       
  1263 					enabledAttr = categoryNode.getAttribute("enable")
       
  1264 					if (enabledAttr.lower() == "false"):
       
  1265 						return False
       
  1266 		return True
       
  1267 
       
  1268 	def UpdateScriptCategory(self, aScript):
       
  1269 		if (self.iDomConfig <> None):
       
  1270 			for scriptsNode in self.iDomConfig.getElementsByTagName("scripts"):
       
  1271 				for scriptNode in scriptsNode.getElementsByTagName(aScript.iScriptName):
       
  1272 					if scriptNode.hasAttribute("category"):
       
  1273 						newCategory = scriptNode.getAttribute("category").lower()
       
  1274 						if (newCategory <> KCategoryConfigMap[aScript.iCategory]):
       
  1275 							for name, value in KCategoryConfigMap.items():
       
  1276 								if (newCategory == value):
       
  1277 									return name
       
  1278 		# no update needed, return original category
       
  1279 		return aScript.iCategory
       
  1280 
       
  1281 	def UpdateScriptSeverity(self, aScript):
       
  1282 		if (self.iDomConfig <> None):
       
  1283 			for scriptsNode in self.iDomConfig.getElementsByTagName("scripts"):
       
  1284 				for scriptNode in scriptsNode.getElementsByTagName(aScript.iScriptName):
       
  1285 					if scriptNode.hasAttribute("severity"):
       
  1286 						newSeverity = scriptNode.getAttribute("severity").lower()
       
  1287 						if (newSeverity <> KSeverityConfigMap[aScript.iSeverity]):
       
  1288 							for name, value in KSeverityConfigMap.items():
       
  1289 								if (newSeverity == value):
       
  1290 									return name
       
  1291 		# no update needed, return original severity
       
  1292 		return aScript.iSeverity
       
  1293 
       
  1294 	def ScanFile(self, aSourceFile):
       
  1295 		self.iLineContext.iFileName = aSourceFile
       
  1296 		self.iLineContext.iLineNumber = 0
       
  1297 		self.iLineContext.iClassName = ""
       
  1298 		self.iLineContext.iMethodName = ""
       
  1299 		self.iLineContext.iComponentName = self.iComponentManager.CurrentComponentName()
       
  1300 
       
  1301 		self.iRendererManager.BeginFile(aSourceFile)
       
  1302 
       
  1303 		# note source file extension - used for filtering later on
       
  1304 		(unused, sourceFileExt) = os.path.splitext(aSourceFile)
       
  1305 		if len(sourceFileExt) > 0 and sourceFileExt[0] == '.':
       
  1306 			sourceFileExt = sourceFileExt[1:]
       
  1307 
       
  1308 		# open, read, and preparse source file
       
  1309 		inputFileHandle = file(aSourceFile, "r")
       
  1310 		inputFileLines = inputFileHandle.readlines()
       
  1311 		inputFileHandle.close()
       
  1312 		
       
  1313 		(noQuoteFileLines, noCommentFileLines, noCommentOrQuoteFileLines, csCommands) = self.PreParseSourceFile(inputFileLines)
       
  1314 
       
  1315 		# bundle all the filtered versions of the file contents into
       
  1316 		# a hash to re-factor code
       
  1317 		fileContentsToTest = { KIgnoreNothing           : inputFileLines,
       
  1318 							   KIgnoreComments          : noCommentFileLines,
       
  1319 							   KIgnoreQuotes            : noQuoteFileLines,
       
  1320 							   KIgnoreCommentsAndQuotes : noCommentOrQuoteFileLines
       
  1321 							   }
       
  1322 
       
  1323 		# now apply test scripts to source file
       
  1324 		iBraceCount = 0
       
  1325 		iBraceCountList = []
       
  1326 		newCurrentClassName = ""
       
  1327 		newCurrentMethodName = ""
       
  1328 		self.iCurrentClassName = ""
       
  1329 		self.iCurrentMethodName = ""
       
  1330 		self.iCurrentMethodStart = -1
       
  1331 
       
  1332 		totalNumberOfLines = len(inputFileLines)
       
  1333 		reConstant = re.compile(r"""
       
  1334 			^\s*
       
  1335 			(static\s+)?
       
  1336 			const
       
  1337 			\s+
       
  1338 			\w+		# type
       
  1339 			\s*
       
  1340 			[\*&]?	# reference or pointer
       
  1341 			\s*
       
  1342 			\w+		# name
       
  1343 			\s*
       
  1344 			(=|\()
       
  1345 			""", re.VERBOSE)
       
  1346 		reInheritance = re.compile("[\s:]*(public|protected|private)\s*([\w:]+)")
       
  1347 		rePreprocessorIf = re.compile("^\s*\#(el)*if(.*)")
       
  1348 		rePreprocessorElse = re.compile("^\s*\#else")
       
  1349 		rePreprocessorEnd = re.compile("^\s*\#endif")
       
  1350 		reTypedef = re.compile("^\s*typedef")
       
  1351 		i = 0
       
  1352 		while (i < totalNumberOfLines):
       
  1353 			# for extra open braces in #if blocks
       
  1354 			if (rePreprocessorIf.search(noCommentOrQuoteFileLines[i])):
       
  1355 				iBraceCountList.append(iBraceCount)
       
  1356 			if (rePreprocessorElse.search(noCommentOrQuoteFileLines[i])):
       
  1357 				if (len(iBraceCountList) > 0):
       
  1358 					iBraceCount = iBraceCountList.pop()
       
  1359 			if (rePreprocessorEnd.search(noCommentOrQuoteFileLines[i])):
       
  1360 				if (len(iBraceCountList) > 0):
       
  1361 					iBraceCountList.pop()
       
  1362 
       
  1363 			if (newCurrentMethodName == ""):
       
  1364 				methodString = noCommentOrQuoteFileLines[i]
       
  1365 				currentLine = i
       
  1366 				m = self.iReMethod.search(methodString)
       
  1367 				if not m and (i + 1 < totalNumberOfLines):
       
  1368 					currentLine = i + 1
       
  1369 					methodString += noCommentOrQuoteFileLines[currentLine]
       
  1370 					m = self.iReMethod.search(methodString)
       
  1371 
       
  1372 				if m and (iBraceCount == 0) and (methodString.find("#") == -1) and (methodString.find("_LIT") == -1):	# must be at root level and not a preprocessor directive or a _LIT
       
  1373 					if not reTypedef.match(methodString) and not reConstant.match(methodString):  # must not be typedef or constant declaration 
       
  1374 						# check for cases where macros are used to declare a class
       
  1375 						# by searching for the inheritance part
       
  1376 						# eg. NONSHARABLE_CLASS(CMyClass) : public CBase
       
  1377 						isClass = reInheritance.search(methodString)
       
  1378 						if not isClass and (currentLine + 1 < totalNumberOfLines):
       
  1379 							methodString += noCommentOrQuoteFileLines[currentLine + 1]
       
  1380 							isClass = reInheritance.search(methodString)
       
  1381 						if not isClass:
       
  1382 							newCurrentMethodName = m.group('method')
       
  1383 							if m.group('class'):
       
  1384 								newCurrentClassName = m.group('class')
       
  1385 							else:
       
  1386 								newCurrentClassName = ""
       
  1387 
       
  1388 			iBraceCount += noCommentOrQuoteFileLines[i].count("{")
       
  1389 			if (iBraceCount > 0) and (newCurrentMethodName <> ""):
       
  1390 				self.iCurrentClassName = newCurrentClassName
       
  1391 				self.iCurrentMethodName = newCurrentMethodName
       
  1392 				self.iCurrentMethodStart = i
       
  1393 				newCurrentClassName = ""
       
  1394 				newCurrentMethodName = ""
       
  1395 
       
  1396 			self.iLineContext.iLineNumber = i+1
       
  1397 			self.iLineContext.iClassName = self.iCurrentClassName
       
  1398 			self.iLineContext.iMethodName = self.iCurrentMethodName
       
  1399 
       
  1400 			# perform all test scripts onto source file									   
       
  1401 			for script in self.iCategoriedScripts.AllScripts():
       
  1402 				if (script.iFileExts.count(sourceFileExt) > 0
       
  1403 				    and fileContentsToTest[script.iIgnore][i] != "\n"
       
  1404 					and	script.iCompare(fileContentsToTest[script.iIgnore], i, script.iReMatch, aSourceFile)):
       
  1405 					# skip any script that has been disabled via CodeScanner command(s) in sources
       
  1406 					if script.iScriptName.lower() in csCommands[i].lower():
       
  1407 						continue
       
  1408 					self.iLineContext.iLineText = fileContentsToTest[script.iIgnore][i]
       
  1409 					self.iComponentManager.ReportError(self.iLineContext, script)
       
  1410 
       
  1411 			iBraceCount -= noCommentOrQuoteFileLines[i].count("}")
       
  1412 			if (iBraceCount < 0):	# for extra close braces in #if blocks
       
  1413 				iBraceCount = 0
       
  1414 
       
  1415 			if (iBraceCount == 0):
       
  1416 				self.iCurrentClassName = ""
       
  1417 				self.iCurrentMethodName = ""
       
  1418 				self.iCurrentMethodStart = -1
       
  1419 
       
  1420 			i = i + 1
       
  1421 
       
  1422 		self.iRendererManager.EndFile()
       
  1423 		return totalNumberOfLines
       
  1424 
       
  1425 	def TraverseDirectory(self, aDirectory):
       
  1426 		# skip folders marked to be excluded in configuration file
       
  1427 		aPath = self.iComponentManager.SanitizePath(aDirectory)
       
  1428 		if (not self.CheckSourceIncluded(aPath)) or (not self.CheckSourceIncluded(aPath + os.path.sep)):
       
  1429 			return
       
  1430 		aDirectory = self.iComponentManager.BeginDirectory(aDirectory)
       
  1431 		contents = os.listdir(aDirectory)
       
  1432 		numberOfLinesScanned = 0
       
  1433 		for entry in contents:
       
  1434 			entryPath = os.path.normpath(os.path.join(aDirectory, entry))
       
  1435 			if os.path.isdir(entryPath):
       
  1436 				self.TraverseDirectory(entryPath)
       
  1437 			else:
       
  1438 				if self.CheckSourceIncluded(entryPath):
       
  1439 					numberOfLinesScanned += self.ScanFile(entryPath)
       
  1440 		self.iComponentManager.EndDirectory(aDirectory, numberOfLinesScanned)
       
  1441 
       
  1442 	def AddScript(self, aScript):
       
  1443 		enabled = self.CheckScriptEnabled(script)
       
  1444 		if enabled:
       
  1445 			aScript.iCategory = self.UpdateScriptCategory(aScript)
       
  1446 			aScript.iSeverity = self.UpdateScriptSeverity(aScript)
       
  1447 			self.iCategoriedScripts.AddScript(aScript)
       
  1448 		else:
       
  1449 			self.ReportInfo("Note: script '" + aScript.iScriptName + "' DISABLED")
       
  1450 
       
  1451 	def AddCustomScript(self, aScript):
       
  1452 		self.ReportInfo("Note: custom rule '" + aScript.iScriptName + "' ADDED")
       
  1453 		self.iCategoriedScripts.AddScript(aScript)
       
  1454 
       
  1455 	def PreParseSourceFile(self, aLines):
       
  1456 		# it provides 3 versions of input:
       
  1457 		# 	1. without quotes
       
  1458 		# 	2. without comments
       
  1459 		# 	3. without quotes and without comments
       
  1460 		
       
  1461 		inCommentBlock = 0
       
  1462 		noQuoteLines = []
       
  1463 		noCommentLines = []
       
  1464 		noCommentOrQuoteLines = []
       
  1465 		csCommands = []
       
  1466 		reCSCommand = re.compile("codescanner((::\w+)+)") # CodeScanner command(s) in comments
       
  1467 
       
  1468 		for line in aLines:
       
  1469 			noQuoteLine = ""
       
  1470 			noCommentLine = ""
       
  1471 			noCommentOrQuoteLine = ""
       
  1472 			csCommand = "\n"
       
  1473 
       
  1474 			i = 0
       
  1475 			startQuote = 0
       
  1476 			b = 0
       
  1477 			escCount = 0
       
  1478 
       
  1479 			while i < len(line):
       
  1480 				# skip quotes
       
  1481 				if not inCommentBlock and ((line[i] == "\"") or (line[i] == "\'")):
       
  1482 					startQuote = i
       
  1483 					i += 1
       
  1484 					while (i < len(line)):
       
  1485 						endIndex = line[i:].find(line[startQuote])
       
  1486 						if (endIndex <> -1):
       
  1487 							b = i + endIndex - 1
       
  1488 							escCount = 0
       
  1489 							while (line[b] == "\\"):
       
  1490 								escCount += 1
       
  1491 								b -= 1
       
  1492 
       
  1493 							i += endIndex + 1
       
  1494 							if (escCount % 2 == 0):
       
  1495 								noQuoteLine += "\"\""
       
  1496 								noCommentOrQuoteLine += "\"\""
       
  1497 								noCommentLine += line[startQuote:i]
       
  1498 								break
       
  1499 						else:
       
  1500 							#	print "Unterminated quote : " + line
       
  1501 							break
       
  1502 					continue	
       
  1503 				
       
  1504 				# parse comments
       
  1505 				if not inCommentBlock:
       
  1506 					if (line[i] == "/"):
       
  1507 						if (i < (len(line)-1)):
       
  1508 							if (line[i + 1] == "/"):
       
  1509 								noCommentLine += "\n"
       
  1510 								noCommentOrQuoteLine += "\n"
       
  1511 								noQuoteLine += line[i:]
       
  1512 								# look for CodeScanner command(s) in comments
       
  1513 								m = reCSCommand.search(line[i:])
       
  1514 								if m:
       
  1515 									csCommand = m.group(1)
       
  1516 								break
       
  1517 							elif (line[i + 1] == "*"):
       
  1518 								inCommentBlock = 1
       
  1519 								i += 2
       
  1520 								noQuoteLine += "/*"
       
  1521 								continue
       
  1522 
       
  1523 					noCommentLine += line[i]
       
  1524 					noCommentOrQuoteLine += line[i]
       
  1525 					noQuoteLine += line[i]
       
  1526 				else:
       
  1527 					# look for CodeScanner command(s) in comments
       
  1528 					m = reCSCommand.search(line[i:])
       
  1529 					if m:
       
  1530 						csCommand = m.group(1)
       
  1531 					endIndex = line[i:].find("*/")
       
  1532 					if (endIndex <> -1):
       
  1533 						inCommentBlock = 0
       
  1534 						noQuoteLine += line[i:i + endIndex + 2]
       
  1535 						i += endIndex + 2
       
  1536 						continue
       
  1537 					else:
       
  1538 						noCommentLine += "\n"
       
  1539 						noCommentOrQuoteLine += "\n"
       
  1540 						noQuoteLine = line[i:]
       
  1541 						break
       
  1542 				
       
  1543 				i += 1
       
  1544 
       
  1545 			noCommentLines.append(noCommentLine)
       
  1546 			noCommentOrQuoteLines.append(noCommentOrQuoteLine)
       
  1547 			noQuoteLines.append(noQuoteLine)
       
  1548 			csCommands.append(csCommand)
       
  1549 
       
  1550 		return [noQuoteLines, noCommentLines, noCommentOrQuoteLines, csCommands]
       
  1551 
       
  1552 	def ReadConfigFile(self):
       
  1553 		if self.iConfigFilename <> "":
       
  1554 			if (os.path.isfile(self.iConfigFilename)):
       
  1555 				self.iDomConfig = xml.dom.minidom.parse(self.iConfigFilename)
       
  1556 				if self.iVerbose:
       
  1557 					print "Note: using configuration file " + self.iConfigFilename
       
  1558 			else:
       
  1559 				self.ReportInfo("Unable to open specified configuration file: " + self.iConfigFilename)
       
  1560 				self.iLog.Close()
       
  1561 				sys.exit(2)
       
  1562 
       
  1563 	def ReadArgumentsFromConfigFile(self):
       
  1564 		if (self.iDomConfig <> None):
       
  1565 			for argumentsNode in self.iDomConfig.getElementsByTagName("arguments"):
       
  1566 				# read input file names
       
  1567 				for inputFileNode in argumentsNode.getElementsByTagName("input"):
       
  1568 					self.iInputFilenames += inputFileNode.firstChild.nodeValue + "::"
       
  1569 				# read output format
       
  1570 				for outputFormatNode in argumentsNode.getElementsByTagName("outputformat"):
       
  1571 					self.iOutputFormat += outputFormatNode.firstChild.nodeValue
       
  1572 				# read lxr URL
       
  1573 				for lxrURLNode in argumentsNode.getElementsByTagName("lxr"):
       
  1574 					self.iLxrUrl = lxrURLNode.firstChild.nodeValue
       
  1575 				# read lxr version
       
  1576 				for lxrVersionNode in argumentsNode.getElementsByTagName("lxrversion"):
       
  1577 					self.iLxrVersion = lxrVersionNode.firstChild.nodeValue
       
  1578 				# read time stamped output option
       
  1579 				for timeStampedOutputNode in argumentsNode.getElementsByTagName("timestampedoutput"):
       
  1580 					self.iTimeStampedOutput = timeStampedOutputNode.firstChild.nodeValue
       
  1581 
       
  1582 	def ReadCustomRulesFromConfigFile(self):
       
  1583 		if (self.iDomConfig <> None):
       
  1584 			for customRulesNode in self.iDomConfig.getElementsByTagName("customrules"):
       
  1585 				for customRuleNode in customRulesNode.getElementsByTagName("customrule"):
       
  1586 					ignoreComments = True
       
  1587 
       
  1588 					# read the name of the rule
       
  1589 					ruleName = ""
       
  1590 					for ruleNameNode in customRuleNode.getElementsByTagName("name"):
       
  1591 						if (ruleNameNode == None) or (ruleNameNode.firstChild == None) or (ruleNameNode.firstChild.nodeValue == None):
       
  1592 							continue
       
  1593 						else:
       
  1594 							ruleName = ruleNameNode.firstChild.nodeValue
       
  1595 					if len(ruleName) == 0:
       
  1596 						self.ReportError("Missing custom rule name in configuration file: " + self.iConfigFilename)
       
  1597 						continue
       
  1598 
       
  1599 					# read the keywords associated with the rule
       
  1600 					keywordList = []
       
  1601 					badKeywordElement = False
       
  1602 					for keywordNode in customRuleNode.getElementsByTagName("keyword"):
       
  1603 						# read keyword content
       
  1604 						if (keywordNode == None) or (keywordNode.firstChild == None) or (keywordNode.firstChild.nodeValue == None):
       
  1605 							badKeywordElement = True
       
  1606 							continue
       
  1607 						newKeyword = CCustomRuleKeyword()
       
  1608 						newKeyword.iContent = keywordNode.firstChild.nodeValue
       
  1609 
       
  1610 						# read keyword type
       
  1611 						if not keywordNode.hasAttribute("type"):
       
  1612 							badKeywordElement = True
       
  1613 							continue
       
  1614 						type = keywordNode.getAttribute("type").lower()
       
  1615 						if type in KCustomRuleKeywordMap.values():
       
  1616 							if type == KKeywordComment:
       
  1617 								ignoreComments = False
       
  1618 						else:
       
  1619 							type = KCustomRuleKeywordMap[KKeywordUnknown]
       
  1620 						newKeyword.iType = type
       
  1621 						keywordList.append(newKeyword)
       
  1622 					if (len(keywordList) == 0) or (badKeywordElement == True):
       
  1623 						self.ReportBadCustomRuleElement(ruleName, "keyword")
       
  1624 						continue
       
  1625 
       
  1626 					# read the file types associated with the rule
       
  1627 					fileTypeList = []
       
  1628 					badFileTypeElement = False
       
  1629 					for fileTypeNode in customRuleNode.getElementsByTagName("filetype"):
       
  1630 						if (fileTypeNode == None) or (fileTypeNode.firstChild == None) or (fileTypeNode.firstChild.nodeValue == None):
       
  1631 							badFileTypeElement = True
       
  1632 							continue
       
  1633 						newFileType = fileTypeNode.firstChild.nodeValue
       
  1634 						fileTypeList.append(newFileType.lower())
       
  1635 					if (len(fileTypeList) == 0) or (badFileTypeElement == True):
       
  1636 						self.ReportBadCustomRuleElement(ruleName, "file type")
       
  1637 						continue
       
  1638 
       
  1639 					# read the severity level of the rule
       
  1640 					severity = KSeverityLow
       
  1641 					for severityNode in customRuleNode.getElementsByTagName("severity"):
       
  1642 						if (severityNode == None) or (severityNode.firstChild == None) or (severityNode.firstChild.nodeValue == None):
       
  1643 							self.ReportBadCustomRuleElement(ruleName, "severity")
       
  1644 							continue
       
  1645 						severityValue = severityNode.firstChild.nodeValue
       
  1646 						for severityKey in KSeverityConfigMap.keys():
       
  1647 							if severityValue == KSeverityConfigMap[severityKey]:
       
  1648 								severity = severityKey
       
  1649 
       
  1650 					# read the tile of the rule
       
  1651 					title = ""
       
  1652 					for titleNode in customRuleNode.getElementsByTagName("title"):
       
  1653 						if (titleNode == None) or (titleNode.firstChild == None) or (titleNode.firstChild.nodeValue == None):
       
  1654 							continue
       
  1655 						title = titleNode.firstChild.nodeValue
       
  1656 					if len(title) == 0:
       
  1657 						self.ReportBadCustomRuleElement(ruleName, "title")
       
  1658 						continue
       
  1659 
       
  1660 					# read the description of the rule
       
  1661 					description = ""
       
  1662 					for descriptionNode in customRuleNode.getElementsByTagName("description"):
       
  1663 						if (descriptionNode == None) or (descriptionNode.firstChild == None) or (descriptionNode.firstChild.nodeValue == None):
       
  1664 							continue
       
  1665 						description = descriptionNode.firstChild.nodeValue
       
  1666 					if len(description) == 0:
       
  1667 						self.ReportBadCustomRuleElement(ruleName, "description")
       
  1668 						continue
       
  1669 
       
  1670 					# read the optional link of the rule
       
  1671 					link = None
       
  1672 					for linkNode in customRuleNode.getElementsByTagName("link"):
       
  1673 						if (linkNode == None) or (linkNode.firstChild == None) or (linkNode.firstChild.nodeValue == None):
       
  1674 							self.ReportBadCustomRuleElement(ruleName, "link")
       
  1675 							continue
       
  1676 						link = linkNode.firstChild.nodeValue
       
  1677 
       
  1678 					# create the RE string for the custom rule
       
  1679 					keywordMap = self.ConstructCustomRuleKeywordMap(keywordList)
       
  1680 					reString = self.ConstructCustomRuleREString(keywordMap)
       
  1681 					if len(reString) == 0:
       
  1682 						continue
       
  1683 					
       
  1684 					# create a script based on the custom rule
       
  1685 					aScript = CCustomScript(ruleName)
       
  1686 					aScript.iReString = reString
       
  1687 					aScript.iReMatch = re.compile(reString)
       
  1688 					aScript.iFileExts = fileTypeList
       
  1689 					aScript.iCategory = KCategoryOther
       
  1690 					if keywordMap.has_key(KKeywordBaseClass):
       
  1691 						aScript.iBaseClass = keywordMap[KKeywordBaseClass]
       
  1692 						aScript.iCompare = aScript.DefaultInheritanceCompare
       
  1693 					if ignoreComments:
       
  1694 						aScript.iIgnore = KIgnoreComments
       
  1695 					else:
       
  1696 						aScript.iIgnore = KIgnoreQuotes
       
  1697 					aScript.iSeverity = severity
       
  1698 					aScript.iTitle = title
       
  1699 					aScript.iIdeTitle = title
       
  1700 					aScript.iDescription = description
       
  1701 					if link <> None:
       
  1702 						aScript.iLink = link
       
  1703 					self.AddCustomScript(aScript)
       
  1704 		return
       
  1705 
       
  1706 	def ReportBadCustomRuleElement(self, name, element):
       
  1707 		self.ReportError("<customrule> element '" + name + "' has bad <" + element + "> child element in configuration file: " + self.iConfigFilename)
       
  1708 
       
  1709 	def ConstructCustomRuleKeywordMap(self, keywordList):
       
  1710 		reString = ""
       
  1711 		keywordMap = {}
       
  1712 		for keyword in keywordList:
       
  1713 			if keywordMap.has_key(keyword.iType):
       
  1714 				keywordMap[keyword.iType] = keywordMap[keyword.iType] + "|" + keyword.iContent
       
  1715 			else:
       
  1716 				keywordMap[keyword.iType] = keyword.iContent
       
  1717 		return keywordMap
       
  1718 
       
  1719 	def ConstructCustomRuleREString(self, keywordMap):
       
  1720 		# generate RE string based on the keyword types
       
  1721 		if keywordMap.has_key(KKeywordBaseClass):
       
  1722 			reString = "^\s*class\s+(\w+::)?(\w+)\s*:(.*)"
       
  1723 		elif keywordMap.has_key(KKeywordCall):
       
  1724 			reString = "(" + keywordMap[KKeywordCall] + ")\s*\(.*\)\s*;"
       
  1725 		elif keywordMap.has_key(KKeywordClassName):
       
  1726 			if keywordMap.has_key(KKeywordMethod):
       
  1727 				reString = "([A-Za-z0-9]+\s+" + keywordMap[KKeywordClassName] + "::)?(" + keywordMap[KKeywordMethod] + ")\s*\(.*\)\s*[^;]"
       
  1728 			else:
       
  1729 				reString = "^\s*class\s+(\w+::)?(" + keywordMap[KKeywordClassName] + ")"
       
  1730 		elif keywordMap.has_key(KKeywordComment):
       
  1731 			reString = "/(/|\*).*(" + keywordMap[KKeywordComment] + ")"
       
  1732 		elif keywordMap.has_key(KKeywordGeneric):
       
  1733 			reString = "(" + keywordMap[KKeywordGeneric] + ")"
       
  1734 		elif keywordMap.has_key(KKeywordLocal):
       
  1735 			reString = "^\s*[A-Z]\w*\s*[\*&\s]\s*(" + keywordMap[KKeywordLocal] + ")\w*\s*[;\(=]"
       
  1736 		elif keywordMap.has_key(KKeywordMacro):
       
  1737 			reString = "^\s*\#define\s+(" + keywordMap[KKeywordMacro] + ")"
       
  1738 		elif keywordMap.has_key(KKeywordMember):
       
  1739 			reString = "^\s*[A-Z]\w*\s*[\*&\s]\s*(" + keywordMap[KKeywordMember] + ")\w*\s*[;\(=]"
       
  1740 		elif keywordMap.has_key(KKeywordMethod):
       
  1741 			reString = "[A-Za-z0-9]+\s+[C|T|R][A-Za-z0-9]+::(" + keywordMap[KKeywordMethod] + ")\s*\(.*\)\s*[^;]"
       
  1742 		elif keywordMap.has_key(KKeywordParameter):
       
  1743 			reString = "({)*\s*(" + keywordMap[KKeywordParameter] + ")\s*=\s*(.*);"
       
  1744 		return reString
       
  1745 
       
  1746 
       
  1747 class CCustomRuleKeyword:
       
  1748 	# #######################################################
       
  1749 	# CCustomRuleKeyword - keyword associated with custom rules
       
  1750 
       
  1751 	def __init__(self):
       
  1752 		iContent = ""
       
  1753 		iType = "unknown"
       
  1754 
       
  1755 
       
  1756 # #######################################################
       
  1757 
       
  1758 class CEncodedFile:
       
  1759     def Extract(self, aBaseDirectory):
       
  1760         outputFileHandle = open(os.path.join(aBaseDirectory, self.iFilename), 'wb')
       
  1761         outputFileBinary = zlib.decompress(base64.decodestring(self.iFileBody))
       
  1762         outputFileHandle.write(outputFileBinary)
       
  1763         outputFileHandle.close()
       
  1764 
       
  1765 	iFilename = ""
       
  1766 	iFileBody = ""
       
  1767 
       
  1768 # #######################################################
       
  1769 
       
  1770 
       
  1771 class CEncodedFileList:
       
  1772 	def AddEncodedFile(self, aEncodedFile):
       
  1773 		self.iEncodedFileList[aEncodedFile.iFilename.lower()] = aEncodedFile
       
  1774 
       
  1775 	def ExtractEncodedFile(self, aFilename, aBaseDirectory):
       
  1776 		# look for the filename in our list of files
       
  1777 		filename = aFilename.lower()
       
  1778 		if (self.iEncodedFileList.has_key(filename)):
       
  1779 			self.iEncodedFileList[filename].Extract(aBaseDirectory)
       
  1780 		else:
       
  1781 			scanner.iLog.Write("Missing "+filename)
       
  1782 
       
  1783 	def ExtractAllEncodedFiles(self, aBaseDirectory):
       
  1784 		# run through associative array and extract everything
       
  1785 		for filename in self.iEncodedFileList.keys():
       
  1786 			self.ExtractEncodedFile(filename, aBaseDirectory)
       
  1787 
       
  1788 	# declare iEncodedFileList is an associative array
       
  1789 	iEncodedFileList = {}
       
  1790 
       
  1791 
       
  1792 # #######################################################
       
  1793 # main()
       
  1794 scanner = CCodeScanner()
       
  1795 
       
  1796 # process command line arguments
       
  1797 opts, args = getopt.getopt(sys.argv[1:], "hvc:i:l:o:x:r:t:", ["help", "verbose", "config=", "input=", "logfile=", "outputformat=", "lxr=", "lxrversion=", "timestampedoutput="])
       
  1798 for o, a in opts:
       
  1799 	if o in ("-h", "--help"):
       
  1800 		Usage(0)
       
  1801 	if o in ("-v", "--verbose"):
       
  1802 		scanner.iVerbose = True
       
  1803 	if o in ("-c", "--config"):
       
  1804 		scanner.iConfigFilename = a
       
  1805 	if o in ("-i", "--input"):
       
  1806 		scanner.iInputFilenames += a + "::"
       
  1807 	if o in ("-l", "--logfile"):
       
  1808 		scanner.iLogFilename = a
       
  1809 	if o in ("-o", "--outputformat"):
       
  1810 		scanner.iOutputFormat += a			
       
  1811 	if o in ("-x", "--lxr"):
       
  1812 		scanner.iLxrUrl = a
       
  1813 	if o in ("-r", "--lxrversion"):
       
  1814 		scanner.iLxrVersion = a
       
  1815 	if o in ("-t", "--timestampedoutput"):
       
  1816 		scanner.iTimeStampedOutput = a
       
  1817 
       
  1818 if len(args) < 1:
       
  1819 	Usage(1)
       
  1820 
       
  1821 scanner.iLog = CLogger(scanner.iLogFilename)
       
  1822 scanner.iLog.Write("Command line: " + str(sys.argv[1:]))
       
  1823 scanner.iLog.Write("Current working directory: " + os.getcwd())
       
  1824 
       
  1825 scanner.ReadConfigFile()
       
  1826 scanner.ReadArgumentsFromConfigFile()
       
  1827 scanner.ReadCustomRulesFromConfigFile()
       
  1828 
       
  1829 scanner.iSource = args[0]
       
  1830 scanner.iEncodedFileList = CEncodedFileList()
       
  1831 scanner.iStartTimeObj = datetime.datetime.now()
       
  1832 scanner.iStartTime = scanner.iStartTimeObj.ctime()
       
  1833 scanner.iOutputDirectory = scanner.iStartTimeObj.strftime("%a-%b-%d-%H-%M-%S-%Y")
       
  1834 
       
  1835 # invoke the pysco module to improve performance
       
  1836 psyco.full()
       
  1837 
       
  1838 # choose renderer based on command line arguments
       
  1839 if len(args) > 1:
       
  1840 	if ("off" in scanner.iTimeStampedOutput.lower()):
       
  1841 		scanner.iOutputDirectory = args[1]
       
  1842 	else:
       
  1843 		scanner.iOutputDirectory = os.path.normpath(os.path.join(args[1], scanner.iOutputDirectory))
       
  1844 	scanner.CleanOutputDirectory()
       
  1845 	if scanner.iOutputFormat <> "":
       
  1846 	#user specified output format
       
  1847 		if ("xml" in scanner.iOutputFormat.lower()):
       
  1848 			CXmlRenderer(scanner.iRendererManager, scanner.iOutputDirectory)
       
  1849 		if ("html" in scanner.iOutputFormat.lower()):
       
  1850 			CHtmlRenderer(scanner.iRendererManager, scanner.iOutputDirectory, scanner.iLxrUrl, scanner.iLxrVersion)
       
  1851 		if ("std" in scanner.iOutputFormat.lower()):
       
  1852 			CStdOutRenderer(scanner.iRendererManager)
       
  1853 	else:
       
  1854 	#default output format
       
  1855 		CHtmlRenderer(scanner.iRendererManager, scanner.iOutputDirectory, scanner.iLxrUrl, scanner.iLxrVersion)
       
  1856 else:
       
  1857 	CStdOutRenderer(scanner.iRendererManager)
       
  1858 
       
  1859 #!PARSE
       
  1860 
       
  1861 if (scanner.iVerbose):
       
  1862 	scanner.iCategoriedScripts.PrintListOfTestScripts()
       
  1863 	scanner.iRendererManager.PrintListOfRenderers()
       
  1864 
       
  1865 print
       
  1866 print "Scanning inititated : " + scanner.iStartTime
       
  1867 
       
  1868 if scanner.iInputFilenames <> "":
       
  1869 	scanner.iComponentManager.iUseFullComponentPath = True
       
  1870 	#additional input files
       
  1871 	inputFiles = scanner.iInputFilenames.split("::")
       
  1872 	for inputFile in inputFiles:
       
  1873 		if inputFile <> "":
       
  1874 			ScanDirOrFile(inputFile)
       
  1875 
       
  1876 argument = args[0]
       
  1877 ScanDirOrFile(argument)
       
  1878 
       
  1879 print "Scanning finished   : " + scanner.iEndTime
       
  1880 scanner.iLog.Close()
       
  1881 
       
  1882 if (scanner.iDomConfig <> None):
       
  1883 	scanner.iDomConfig.unlink()
       
  1884 
       
  1885 sys.exit(0)