Parcourir la source

The initial checkin of the local copy.

avkonst il y a 15 ans
commit
4b4cf81e29

+ 5 - 0
.includepath

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<includepath>
+  <includepathentry path="./lib" />
+</includepath>
+

+ 17 - 0
.project

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>SWI</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.epic.perleditor.perlbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.epic.perleditor.perlnature</nature>
+	</natures>
+</projectDescription>

BIN
dupindex/bin/dupindex.exe


+ 476 - 0
dupindex/dupindex.cpp

@@ -0,0 +1,476 @@
+/*
+ *  Software Index, Copyright 2010, Software Index Project Team
+ *  Link: http://swi.sourceforge.net
+ *
+ *  This file is part of Software Index Tool.
+ *
+ *  Software Index is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  Software Index is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Software Index.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+static const char SWI_EOF = '\x7'; // ASCII code 7 is used as EOF marker
+static const int  SWI_MAX_TEXT_LENGTH = 10000;
+
+/**
+ * Container for sortable stuff
+ * Strings are not moved, just bookmarks are sorted
+ */
+struct SwiBookmark
+{
+	int         m_possition;
+	const char* m_text;
+
+	// sort algorithm requires to define this operator
+	bool operator<(const SwiBookmark& another) const
+	{
+		return strcmp(another.m_text, m_text) < 0;
+	}
+};
+
+/**
+ * Container for the file/function record
+ */
+struct SwiRecord
+{
+	const char* functionName;
+	const char* fileName;
+	int         end;
+};
+
+/**
+ * Returns line number in intial record
+ * from the begging till the symbol by offset
+ */
+static int swiLineNumberGet(const char* text,
+							int         offset,
+							SwiRecord   files[],
+							int         fileIndex)
+{
+	const int start = (fileIndex == 0) ? 0 : files[fileIndex - 1].end;
+
+	int line = 1;
+	for (int i = start; i < offset; ++i)
+	{
+		if (text[i] == '\n')
+		{
+			++line;
+		}
+	}
+
+	return line;
+}
+
+/**
+ * Adds a character to the processed string,
+ * initializes new bookmark, and returns it
+ */
+static SwiBookmark swiProcessedCharacterAdd(char c, int originalPossition, char* processedText)
+{
+	static int processedPossition;
+	SwiBookmark bookmark;
+	bookmark.m_possition  = originalPossition;
+	bookmark.m_text = processedText + processedPossition;
+	processedText[processedPossition++] = c;
+	return bookmark;
+}
+
+/**
+ * Prints to stdout the found items
+ * The format of stdout is strictly defined by the caller: SWI/PROCESSOR
+ * Change it synchroniously with SWI/PROCESSOR code
+ */
+static void swiResultPrint(SwiBookmark bookmark,
+						   SwiRecord   files[],
+						   const char* originalText,
+						   int         numOfDuplicatedSymbols)
+{
+	int recordIndex = 0;
+	while (files[recordIndex].end <= bookmark.m_possition)
+	{
+		++recordIndex;
+	}
+
+	printf("duplication: file: '%s' function: '%s' possition: '%d' size: '%d' \n",
+		files[recordIndex].fileName,
+		files[recordIndex].functionName,
+		swiLineNumberGet(originalText, bookmark.m_possition, files, recordIndex),
+		numOfDuplicatedSymbols);
+}
+
+/**
+* Returns number of symbols which are equal from the beggining
+*/
+static int swiStringsCompare(SwiBookmark bookmarkFirst,
+							 SwiBookmark bookmarkSecond)
+{
+	int pos = 0;
+	for (; bookmarkFirst.m_text[pos] == bookmarkSecond.m_text[pos]; ++pos)
+	{
+		if (bookmarkFirst.m_text[pos] == SWI_EOF)
+		{
+			break;
+		}
+	}
+	return pos;
+}
+
+/**
+ * Checks if two strings are equal in the defined range of symbol's indexes:
+ * from the first symbol till upperLimit
+ */
+static bool swiStringsEqualCheck(SwiBookmark bookmarkFirst,
+								 SwiBookmark bookmarkSecond,
+								 int upperLimit,
+								 const char* endPos)
+{
+	if (bookmarkFirst.m_text + upperLimit >= endPos ||
+		bookmarkSecond.m_text + upperLimit >= endPos)
+	{
+		return false;
+	}
+
+	for (int i = upperLimit; i >= 0; --i)
+	{
+		if (bookmarkFirst.m_text[i] != bookmarkSecond.m_text[i])
+		{
+			return false;
+		}
+	}
+	return true;
+}
+
+/**
+ * Scans the original text into a processed text, which is returned.
+ * The processed string does not include the ignorable symbols
+ * The list of ignorable symbols is configured (usualy spaces)
+ * Bookmarks are installed for every newline
+ * if it begins from the 'regular' symbol
+ * The list of non-regular symbols is configred
+ */
+static const char* swiOriginalStringProcess(std::string               originalText,
+											std::vector<SwiBookmark>& bookmarks,
+											const char * ignorableSymbols,
+											const char * nonregularSymbols)
+{
+	char* const processedText = new char[originalText.length()];
+	bool  isNewlineDetected = true;
+
+	for (unsigned int i = 0; i < originalText.length(); ++i)
+	{
+		const char c = originalText[i];
+		bool isIgnorable  = false;
+		bool isRegular    = true;
+
+
+		// Processed text should not include the end-null symbol
+		if (c == '\0')
+		{
+			continue;
+		}
+
+		if (c == SWI_EOF)
+		{
+			swiProcessedCharacterAdd(c, i, processedText);
+			continue;
+		}
+
+		for (int j = 0; ignorableSymbols[j] != '\0'; ++j)
+		{
+			if (ignorableSymbols[j] == c)
+			{
+				isIgnorable = true;
+			}
+		}
+
+		for (int j = 0; nonregularSymbols[j] != '\0'; ++j)
+		{
+			if (nonregularSymbols[j] == c)
+			{
+				isRegular = false;
+			}
+		}
+
+		if (!isIgnorable)
+		{
+			if (isNewlineDetected && isRegular)
+			{
+				bookmarks.push_back(swiProcessedCharacterAdd(c, i, processedText));
+			}
+			else
+			{
+				swiProcessedCharacterAdd(c, i, processedText);
+			}
+
+			isNewlineDetected = false;
+		}
+
+		if (c == '\n')
+		{
+			isNewlineDetected = true;
+		}
+	}
+	swiProcessedCharacterAdd('\0', originalText.length(), processedText);
+
+	return processedText;
+}
+
+/**
+* Removes bookmarks which are in reported area already
+* It helps to prevent the unexpected reporting of the duplicatedSymbolsCount markers more than once
+*/
+static void swiBookmarksShift(std::vector<SwiBookmark>& bookmarks)
+{
+	unsigned int indexFrom = 0;
+	for (unsigned int indexTo = 0; indexTo < bookmarks.size() - 1; ++indexTo)
+	{
+		if (bookmarks[indexTo].m_text == 0)
+		{
+			if (indexFrom <= indexTo)
+			{
+				indexFrom = indexTo + 1;
+			}
+			while (indexFrom < bookmarks.size() &&
+				bookmarks[indexFrom].m_text == 0)
+			{
+				++indexFrom;
+			}
+
+			if (indexFrom == bookmarks.size())
+			{
+				break;
+			}
+
+			bookmarks[indexTo]= bookmarks[indexFrom];
+			bookmarks[indexFrom].m_text = 0;
+		}
+	}
+}
+
+/**
+ * Programm entry point
+ */
+int main(int argc, char* argv[])
+{
+	std::vector<SwiRecord>  files(0);
+	std::string commonString      = "";
+	int minLength         = 100;
+	int proximityFactor   = 100;
+	char * ignorabaleSymbols = new char[SWI_MAX_TEXT_LENGTH];
+	char * nonRegularSymbols = new char[SWI_MAX_TEXT_LENGTH];
+
+	strcpy(ignorabaleSymbols, " \t\n");
+	strcpy(nonRegularSymbols, "}");
+
+	while (1)
+	{
+		char * command = new char[SWI_MAX_TEXT_LENGTH];
+		scanf("%s", command);
+
+		if (strcmp(command, "start") == 0)
+		{
+			// Continue search algorithm
+			break;
+		}
+
+		if (strcmp(command, "exit") == 0)
+		{
+			// Breaking the search
+			exit(EXIT_SUCCESS);
+		}
+
+		if (strcmp(command, "init_length") == 0)
+		{
+			scanf("%d", &minLength);
+		}
+
+		if (strcmp(command, "init_proximity") == 0)
+		{
+			scanf("%d", &proximityFactor);
+
+			if (proximityFactor < 1 || proximityFactor > 100)
+			{
+				fprintf(stderr, "error: Proximity factor must be between 1 and 100 (inclusive)\n");
+				exit(EXIT_FAILURE);
+			}
+		}
+
+		if (strcmp(command, "init_ignorable") == 0)
+		{
+			// TODO: no ways to configure newline symbol
+			scanf("%s", ignorabaleSymbols);
+		}
+
+		if (strcmp(command, "init_nonregular") == 0)
+		{
+			// TODO: no ways to configure hewline symbol
+			scanf("%s", nonRegularSymbols);
+		}
+
+		if (strcmp(command, "init_file") == 0)
+		{
+			unsigned int fileLength = 0;
+			unsigned int functionLength = 0;
+			unsigned int textLength = 0;
+
+			scanf("%d", &fileLength);
+			scanf("%d", &functionLength);
+			scanf("%d", &textLength);
+
+			char * file     = new char[fileLength + 1];
+			char * function = new char[functionLength + 1];
+			char * text     = new char[textLength + 1 + 1]; // +1 for null pointer, +1 for EOF symbol
+
+			file[fileLength] = '\0';
+			function[functionLength] = '\0';
+			text[textLength] = SWI_EOF;
+			text[textLength + 1] = '\0';
+
+			fread(file, 1, 1, stdin); // Consume one empty symbol
+			fread(file, fileLength, 1, stdin);
+			fread(function, functionLength, 1, stdin);
+			fread(text, textLength, 1, stdin);
+
+			commonString += std::string(text);
+			SwiRecord record;
+			record.end        = commonString.length();
+			record.functionName = function;
+			record.fileName = file;
+			files.push_back(record);
+
+			delete text;
+		}
+
+		delete command;
+	}
+
+	if (commonString.length() == 0)
+	{
+		fprintf(stderr, "error: No files defined or they are empty\n");
+		exit(EXIT_FAILURE);
+	}
+
+	std::vector<SwiBookmark> bookmarks;
+	const char* processedText =
+		swiOriginalStringProcess(commonString, bookmarks, ignorabaleSymbols, nonRegularSymbols);
+	const char* processedEnd  = processedText + strlen(processedText);
+
+	std::sort(bookmarks.begin(), bookmarks.end());
+
+	while(1)
+	{
+		int longestDuplication = 0;
+		int firstInstanceIndex = 0;
+
+		// Find the two bookmarks that have the longest common substring.
+		for (unsigned int bookmarkIndex = 0; bookmarkIndex < bookmarks.size() - 1; ++bookmarkIndex)
+		{
+			if (bookmarks[bookmarkIndex + 1].m_text == 0)
+			{
+				break;
+			}
+
+			if (swiStringsEqualCheck(bookmarks[bookmarkIndex], bookmarks[bookmarkIndex + 1],
+				longestDuplication, processedEnd))
+			{
+				const int duplicatedSymbolsCount = swiStringsCompare(bookmarks[bookmarkIndex],
+					bookmarks[bookmarkIndex + 1]);
+				if (duplicatedSymbolsCount > longestDuplication)
+				{
+					firstInstanceIndex = bookmarkIndex;
+					longestDuplication = duplicatedSymbolsCount;
+				}
+			}
+		}
+
+		if (longestDuplication < minLength)
+		{
+			// Do not process too short strings
+			// This is the exit from the while loop
+			break;
+		}
+
+		int numberOfInstances        = 2;
+		int almostLongestDuplication = (longestDuplication * proximityFactor) / 100;
+		int initialIndexForScan = firstInstanceIndex;
+
+		// // Search for duplicated strings before the current pair.
+		for (int i = initialIndexForScan - 1; i >= 0; --i)
+		{
+			const int duplicatedSymbolsCount =
+				swiStringsCompare(bookmarks[initialIndexForScan], bookmarks[i]);
+
+			if (duplicatedSymbolsCount < almostLongestDuplication)
+			{
+				break;
+			}
+			numberOfInstances++;
+			if (longestDuplication > duplicatedSymbolsCount)
+			{
+				longestDuplication = duplicatedSymbolsCount;
+			}
+			firstInstanceIndex = i;
+		}
+
+		// Search for duplicated strings after the current pair.
+		for (unsigned int i = initialIndexForScan + 2; i < bookmarks.size(); ++i)
+		{
+			if (bookmarks[i].m_text == 0)
+			{
+				break;
+			}
+
+			const int duplicatedSymbolsCount = swiStringsCompare(bookmarks[initialIndexForScan],
+				bookmarks[i]);
+			if (duplicatedSymbolsCount < almostLongestDuplication)
+			{
+				break;
+			}
+			numberOfInstances++;
+			if (longestDuplication > duplicatedSymbolsCount)
+			{
+				longestDuplication = duplicatedSymbolsCount;
+			}
+		}
+
+		printf("info: group_start\n");
+		for (int i = 0; i < numberOfInstances; ++i)
+		{
+			swiResultPrint(bookmarks[firstInstanceIndex + i], &files[0],
+				commonString.c_str(), longestDuplication);
+
+			// Clear bookmarks that point out to the reported area.
+			const char* reportStart =
+				bookmarks[firstInstanceIndex + i].m_text;
+
+			for (unsigned int j = 0; j < bookmarks.size() - 1; ++j)
+			{
+				if (bookmarks[j].m_text >= reportStart &&
+					bookmarks[j].m_text < reportStart + longestDuplication)
+				{
+					bookmarks[j].m_text = 0;
+				}
+			}
+		}
+		printf("\n");
+
+		swiBookmarksShift(bookmarks);
+	}
+
+	delete [] processedText;
+	return 0;
+}
+
+

+ 20 - 0
dupindex/dupindex.sln

@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual C++ Express 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dupindex", "dupindex.vcproj", "{6D7440A9-F28B-4881-914F-79BDCF355F95}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{6D7440A9-F28B-4881-914F-79BDCF355F95}.Debug|Win32.ActiveCfg = Debug|Win32
+		{6D7440A9-F28B-4881-914F-79BDCF355F95}.Debug|Win32.Build.0 = Debug|Win32
+		{6D7440A9-F28B-4881-914F-79BDCF355F95}.Release|Win32.ActiveCfg = Release|Win32
+		{6D7440A9-F28B-4881-914F-79BDCF355F95}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

+ 197 - 0
dupindex/dupindex.vcproj

@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="windows-1251"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9,00"
+	Name="dupindex"
+	ProjectGUID="{6D7440A9-F28B-4881-914F-79BDCF355F95}"
+	RootNamespace="dupindex"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)bin"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="1"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				BufferSecurityCheck="true"
+				EnableFunctionLevelLinking="true"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath=".\dupindex.cpp"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>

+ 91 - 0
lib/Internal/Output.pm

@@ -0,0 +1,91 @@
+#
+#    Software Index, Copyright 2010, Software Index Project Team
+#    Link: http://swi.sourceforge.net
+#
+#    This file is part of Software Index Tool.
+#
+#    Software Index is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#
+#    Software Index is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with Software Index.  If not, see <http://www.gnu.org/licenses/>.
+#
+package Internal::Output;
+
+use strict;
+
+require Exporter;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $PREFERRED_PARSER);
+@ISA              = qw(Exporter);
+@EXPORT           = qw(DEBUG_ENABLED DEBUG STATUS PRINT);
+@EXPORT_OK        = qw(DEBUG_ENABLED DEBUG STATUS PRINT);
+$VERSION          = '1.0';
+$PREFERRED_PARSER = undef;
+
+#
+# Global variables
+#
+my $globalDebugEnabled = 0;
+
+#
+# Interfaces to get/set internal variables
+#
+
+sub DEBUG_ENABLED
+{
+    if (@_)
+    {
+        $globalDebugEnabled = shift();
+    }
+    return $globalDebugEnabled;
+}
+
+#
+# Interfaces to dump/print/publish the information
+#
+
+sub DEBUG
+{
+    my $text = shift();
+    if ( $globalDebugEnabled != 0 )
+    {
+        print "[SWI DEBUG  MESSAGE]: $text\n";
+    }
+}
+
+sub STATUS
+{
+    my $text = shift();
+    
+    print "[SWI STATUS MESSAGE]: $text\n";
+}
+
+sub PRINT
+{
+    my $file = shift();
+    my $line = shift();
+    my $severity = shift();
+    my $text = shift();
+    
+    $severity = lc $severity;
+    
+    if ( $severity ne 'debug' || $globalDebugEnabled != 0 )
+    {
+        if ($severity eq 'debug')
+        {
+            print STDOUT "$file:$line: $severity: $text\n";
+        }
+        elsif($severity eq 'error' || $severity eq 'warning' || $severity eq 'notice' || $severity eq 'info')
+        {
+            print STDERR "$file:$line: $severity: $text\n";
+        }
+    }
+}
+
+return 1;

+ 906 - 0
lib/SWI/Appraiser.pm

@@ -0,0 +1,906 @@
+#
+#    Software Index, Copyright 2010, Software Index Project Team
+#    Link: http://swi.sourceforge.net
+#
+#    This file is part of Software Index Tool.
+#
+#    Software Index is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#
+#    Software Index is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with Software Index.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+use strict;
+use XML::Simple;
+use Internal::Output;
+use FileHandle;
+use Data::Dumper;
+
+#
+# Export section
+#
+require Exporter;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $PREFERRED_PARSER);
+@ISA              = qw(Exporter);
+@EXPORT           = qw(swiAppraise);
+@EXPORT_OK        = qw();
+$VERSION          = '1.0';
+$PREFERRED_PARSER = undef;
+
+#
+# Subroutine for troubleshooting purposes
+#
+use Internal::Output;
+
+#
+# Global variables
+#
+my $config = undef;
+my $report = undef;
+
+#
+# Enter point
+#
+sub swiAppraise
+{
+    $config = shift();
+
+    my $reportBase = undef;
+
+    $report = XMLin(
+        $config->{"swi:report"}->{"swi:destination"} . "/"
+          . $config->{"swi:report"}->{"swi:xml"}->{"swi:name"} . ".x",
+        ForceArray =>
+          [ "swi:module", "swi:file", "swi:function", "swi:reference" ]
+    );
+
+    if ( defined( $config->{"swi:report"}->{"swi:xml"}->{"swi:baseline"} )
+        && $config->{"swi:report"}->{"swi:xml"}->{"swi:baseline"} ne "" )
+    {
+        $reportBase = XMLin(
+            $config->{"swi:report"}->{"swi:destination"} . "/"
+              . $config->{"swi:report"}->{"swi:xml"}->{"swi:baseline"},
+            ForceArray =>
+              [ "swi:module", "swi:file", "swi:function", "swi:reference" ]
+        );
+    }
+
+    my $projectStat = $report->{"swi:statistic"};
+    for (
+        my $moduleId = 0 ;
+        $moduleId <= $#{ $report->{"swi:module"} } ;
+        $moduleId++
+      )
+    {
+        my $moduleStat = $report->{"swi:module"}[$moduleId]->{"swi:statistic"};
+        for (
+            my $fileId = 0 ;
+            $fileId <= $#{ $report->{"swi:module"}[$moduleId]->{"swi:file"} } ;
+            $fileId++
+          )
+        {
+            my $fileStat =
+              $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+              ->{"swi:statistic"};
+            for (
+                my $functionId = 0 ;
+                $functionId <= $#{
+                    $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+                      ->{"swi:function"}
+                } ;
+                $functionId++
+              )
+            {
+                my $functionStat =
+                  $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+                  ->{"swi:function"}[$functionId]->{"swi:statistic"};
+
+                foreach my $keyStat ( keys %$functionStat )
+                {
+                    my $subStat = $functionStat->{$keyStat};
+                    foreach my $keySubStat ( keys %$subStat )
+                    {
+
+                        # add total per file
+                        $fileStat->{$keyStat}->{$keySubStat}->{"swi:total"} +=
+                          $subStat->{$keySubStat}->{'swi:exact'};
+                        $fileStat->{$keyStat}->{$keySubStat}->{"swi:average"} =
+                          $fileStat->{$keyStat}->{$keySubStat}->{"swi:total"} /
+                          $fileStat->{"swi:count"}->{"swi:functions"};
+
+                        # add total per module
+                        $moduleStat->{$keyStat}->{$keySubStat}->{"swi:total"} +=
+                          $subStat->{$keySubStat}->{'swi:exact'};
+                        $moduleStat->{$keyStat}->{$keySubStat}
+                          ->{"swi:average"} =
+                          $moduleStat->{$keyStat}->{$keySubStat}
+                          ->{"swi:total"} /
+                          $moduleStat->{"swi:count"}->{"swi:functions"};
+
+                        # add total per project
+                        $projectStat->{$keyStat}->{$keySubStat}
+                          ->{"swi:total"} +=
+                          $subStat->{$keySubStat}->{'swi:exact'};
+                        $projectStat->{$keyStat}->{$keySubStat}
+                          ->{"swi:average"} =
+                          $projectStat->{$keyStat}->{$keySubStat}
+                          ->{"swi:total"} /
+                          $projectStat->{"swi:count"}->{"swi:functions"};
+
+                        # add minimum per file
+                        if (
+                            !defined(
+                                $fileStat->{$keyStat}->{$keySubStat}
+                                  ->{"swi:min"}
+                            )
+                            || $fileStat->{$keyStat}->{$keySubStat}
+                            ->{"swi:min"} >
+                            $subStat->{$keySubStat}->{'swi:exact'}
+                          )
+                        {
+                            $fileStat->{$keyStat}->{$keySubStat}->{"swi:min"} =
+                              $subStat->{$keySubStat}->{'swi:exact'};
+                        }
+
+                        # add minimum per module
+                        if (
+                            !defined(
+                                $moduleStat->{$keyStat}->{$keySubStat}
+                                  ->{"swi:min"}
+                            )
+                            || $moduleStat->{$keyStat}->{$keySubStat}
+                            ->{"swi:min"} >
+                            $subStat->{$keySubStat}->{'swi:exact'}
+                          )
+                        {
+                            $moduleStat->{$keyStat}->{$keySubStat}
+                              ->{"swi:min"} =
+                              $subStat->{$keySubStat}->{'swi:exact'};
+                        }
+
+                        # add minimum per project
+                        if (
+                            !defined(
+                                $projectStat->{$keyStat}->{$keySubStat}
+                                  ->{"swi:min"}
+                            )
+                            || $projectStat->{$keyStat}->{$keySubStat}
+                            ->{"swi:min"} >
+                            $subStat->{$keySubStat}->{'swi:exact'}
+                          )
+                        {
+                            $projectStat->{$keyStat}->{$keySubStat}
+                              ->{"swi:min"} =
+                              $subStat->{$keySubStat}->{'swi:exact'};
+                        }
+
+                        # add maximum per file
+                        if (
+                            !defined(
+                                $fileStat->{$keyStat}->{$keySubStat}
+                                  ->{"swi:max"}
+                            )
+                            || $fileStat->{$keyStat}->{$keySubStat}
+                            ->{"swi:max"} <
+                            $subStat->{$keySubStat}->{'swi:exact'}
+                          )
+                        {
+                            $fileStat->{$keyStat}->{$keySubStat}->{"swi:max"} =
+                              $subStat->{$keySubStat}->{'swi:exact'};
+                        }
+
+                        # add maximum per module
+                        if (
+                            !defined(
+                                $moduleStat->{$keyStat}->{$keySubStat}
+                                  ->{"swi:max"}
+                            )
+                            || $moduleStat->{$keyStat}->{$keySubStat}
+                            ->{"swi:max"} <
+                            $subStat->{$keySubStat}->{'swi:exact'}
+                          )
+                        {
+                            $moduleStat->{$keyStat}->{$keySubStat}
+                              ->{"swi:max"} =
+                              $subStat->{$keySubStat}->{'swi:exact'};
+                        }
+
+                        # add maximum per project
+                        if (
+                            !defined(
+                                $projectStat->{$keyStat}->{$keySubStat}
+                                  ->{"swi:max"}
+                            )
+                            || $projectStat->{$keyStat}->{$keySubStat}
+                            ->{"swi:max"} <
+                            $subStat->{$keySubStat}->{'swi:exact'}
+                          )
+                        {
+                            $projectStat->{$keyStat}->{$keySubStat}
+                              ->{"swi:max"} =
+                              $subStat->{$keySubStat}->{'swi:exact'};
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    # generate full XML report
+    my $outputFile =
+        $config->{"swi:report"}->{"swi:destination"} . "/"
+      . $config->{"swi:report"}->{"swi:xml"}->{"swi:name"};
+    my $fh = new FileHandle( $outputFile, "w" )
+      or die("Can not open output file '$outputFile'!");
+
+    print $fh "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+    print $fh "<swi:report>\n";
+    print $fh "\n";
+
+    print $fh "  <swi:info>\n";
+    print $fh "    <swi:version>1.0</swi:version>\n";
+    if ( defined( $ENV{USER} ) )
+    {
+        print $fh "    <swi:user>" . $ENV{USER} . "</swi:user>\n";
+    }
+    print $fh "    <swi:generator>SWI/APPRAISER</swi:generator>\n";
+    print $fh "  </swi:info>\n";
+    print $fh "\n";
+
+    $projectStat = $report->{"swi:statistic"};
+    my $projectName = $config->{"swi:info"}->{"swi:project"}->{"swi:name"};
+    my $projectDiff =
+      swiReportModificationGet( $reportBase, $report, "swi:total" );
+    for (
+        my $moduleId = 0 ;
+        $moduleId <= $#{ $report->{"swi:module"} } ;
+        $moduleId++
+      )
+    {
+        my $moduleStat = $report->{"swi:module"}[$moduleId]->{"swi:statistic"};
+        my $moduleName = $report->{"swi:module"}[$moduleId]->{"swi:name"};
+        my $moduleBase =
+          swiReportObjectFind( $reportBase->{"swi:module"}, $moduleName );
+        my $moduleDiff =
+          swiReportModificationGet( $moduleBase,
+            $report->{"swi:module"}[$moduleId], "swi:total" );
+        print $fh "  <swi:module>\n";
+        print $fh "    <swi:name>" . $moduleName . "</swi:name>\n";
+        print $fh "    <swi:location>"
+          . $report->{"swi:module"}[$moduleId]->{"swi:location"}
+          . "</swi:location>\n";
+        print $fh "    <swi:modification>"
+          . $moduleDiff
+          . "</swi:modification>\n";
+        print $fh "\n";
+
+        for (
+            my $fileId = 0 ;
+            $fileId <= $#{ $report->{"swi:module"}[$moduleId]->{"swi:file"} } ;
+            $fileId++
+          )
+        {
+            my $fileStat =
+              $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+              ->{"swi:statistic"};
+            my $fileName =
+              $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+              ->{"swi:name"};
+            my $fileBase =
+              ( $moduleDiff eq "added" )
+              ? undef
+              : swiReportObjectFind( $moduleBase->{"swi:file"}, $fileName );
+            my $fileDiff =
+              swiReportModificationGet( $fileBase,
+                $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId],
+                "swi:total" );
+            print $fh "    <swi:file>\n";
+            print $fh "      <swi:name>" . $fileName . "</swi:name>\n";
+            print $fh "      <swi:location>"
+              . $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+              ->{"swi:location"} . "</swi:location>\n";
+            print $fh "      <swi:modification>"
+              . $fileDiff
+              . "</swi:modification>\n";
+            print $fh "\n";
+
+            for (
+                my $functionId = 0 ;
+                $functionId <= $#{
+                    $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+                      ->{"swi:function"}
+                } ;
+                $functionId++
+              )
+            {
+                my $functionStat =
+                  $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+                  ->{"swi:function"}[$functionId]->{"swi:statistic"};
+                my $functionName =
+                  $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+                  ->{"swi:function"}[$functionId]->{"swi:name"};
+                my $functionBase =
+                  ( $fileDiff eq "added" )
+                  ? undef
+                  : swiReportObjectFind( $fileBase->{"swi:function"},
+                    $functionName );
+                my $functionDiff = swiReportModificationGet(
+                    $functionBase,
+                    $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+                      ->{"swi:function"}[$functionId],
+                    "swi:exact"
+                );
+                print $fh "      <swi:function>\n";
+                print $fh "        "
+                  . XMLout( $functionName, RootName => 'swi:name' );
+                print $fh "        "
+                  . XMLout(
+                    $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+                      ->{"swi:function"}[$functionId]->{"swi:location"},
+                    RootName => 'swi:location'
+                  );
+                print $fh "        "
+                  . XMLout(
+                    $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+                      ->{"swi:function"}[$functionId]->{"swi:modifier"},
+                    RootName => 'swi:modifier'
+                  );
+                print $fh "        "
+                  . XMLout(
+                    $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+                      ->{"swi:function"}[$functionId]->{"swi:pointer"},
+                    RootName => 'swi:pointer'
+                  );
+                print $fh "        <swi:modification>"
+                  . $functionDiff
+                  . "</swi:modification>\n";
+                print $fh "        <swi:statistic>\n";
+
+                foreach my $keyStat ( keys %$functionStat )
+                {
+                    print $fh "          <" . $keyStat . ">\n";
+                    my $subStat = $functionStat->{$keyStat};
+                    foreach my $keySubStat ( keys %$subStat )
+                    {
+                        my ( $level, $suppress, $criteria ) =
+                          swiStatisticLevelGet(
+                            $keyStat,
+                            $keySubStat,
+                            "swi:exact",
+                            $projectName . "/"
+                              . $moduleName . "/"
+                              . $fileName . "/"
+                              . $functionName,
+                            $functionStat,
+                            "swi:function"
+                          );
+                        my $statDiff = swiStatisticDiffGet(
+                            $functionDiff,
+                            $functionStat->{$keyStat}->{$keySubStat}
+                              ->{'swi:exact'},
+                            $functionBase->{"swi:statistic"}->{$keyStat}
+                              ->{$keySubStat}->{"swi:exact"}
+                        );
+                        print $fh "            <"
+                          . $keySubStat
+                          . "><swi:exact swi:change=\""
+                          . $statDiff
+                          . "\" swi:level=\""
+                          . $level
+                          . "\" swi:suppress=\""
+                          . $suppress
+                          . "\" swi:criteria=\""
+                          . $criteria . "\">"
+                          . $functionStat->{$keyStat}->{$keySubStat}
+                          ->{'swi:exact'}
+                          . "</swi:exact></"
+                          . $keySubStat . ">\n";
+                    }
+                    print $fh "          </" . $keyStat . ">\n";
+                }
+                print $fh "        </swi:statistic>\n";
+
+                if (
+                    defined(
+                        $report->{"swi:module"}[$moduleId]
+                          ->{"swi:file"}[$fileId]->{"swi:function"}[$functionId]
+                          ->{'swi:reference'}
+                    )
+                  )
+                {
+                    my $refStr = XMLout(
+                        $report->{"swi:module"}[$moduleId]
+                          ->{"swi:file"}[$fileId]->{"swi:function"}[$functionId]
+                          ->{'swi:reference'},
+                        RootName => ''
+                    );
+                    $refStr =~ s/\n/\n      /g;
+                    $refStr =~ s/<anon /<swi:reference /g;
+                    print $fh "      ";
+                    print $fh $refStr;
+                    print $fh "\n";
+                }
+
+                print $fh "      </swi:function>\n";
+                print $fh "\n";
+            }
+            for (
+                my $functionId = 0 ;
+                $functionId <= $#{ $fileBase->{"swi:function"} } ;
+                $functionId++
+              )
+            {
+                my $functionOld = $fileBase->{"swi:function"}[$functionId];
+                if (
+                    swiReportObjectFind(
+                        $report->{"swi:module"}[$moduleId]
+                          ->{"swi:file"}[$fileId]->{"swi:function"},
+                        $functionOld->{"swi:name"}
+                    ) == undef
+                  )
+                {
+                    print $fh "      <swi:function>\n";
+                    print $fh "        <swi:name>"
+                      . $functionOld->{"swi:name"}
+                      . "</swi:name>\n";
+                    print $fh "        <swi:location>"
+                      . $functionOld->{"swi:location"}
+                      . "</swi:location>\n";
+                    print $fh
+                      "        <swi:modification>removed</swi:modification>\n";
+                    print $fh "      </swi:function>\n";
+                    print $fh "\n";
+                }
+            }
+
+            print $fh "      <swi:statistic>\n";
+            foreach my $keyStat ( keys %$fileStat )
+            {
+                print $fh "        <" . $keyStat . ">\n";
+                my $subStat = $fileStat->{$keyStat};
+                foreach my $keySubStat ( keys %$subStat )
+                {
+                    my @types = (
+                        "swi:exact", "swi:average",
+                        "swi:min",   "swi:max",
+                        "swi:total"
+                    );
+                    print $fh "          <" . $keySubStat . ">\n";
+                    foreach my $type (@types)
+                    {
+                        if (
+                            defined(
+                                $fileStat->{$keyStat}->{$keySubStat}->{$type}
+                            )
+                          )
+                        {
+                            my ( $level, $suppress, $criteria ) =
+                              swiStatisticLevelGet(
+                                $keyStat,
+                                $keySubStat,
+                                $type,
+                                $projectName . "/"
+                                  . $moduleName . "/"
+                                  . $fileName,
+                                $fileStat,
+                                "swi:file"
+                              );
+                            my $statDiff = swiStatisticDiffGet(
+                                $fileDiff,
+                                $fileStat->{$keyStat}->{$keySubStat}->{$type},
+                                $fileBase->{"swi:statistic"}->{$keyStat}
+                                  ->{$keySubStat}->{$type}
+
+                            );
+                            print $fh "            <" . $type
+                              . " swi:change=\""
+                              . $statDiff
+                              . "\" swi:level=\""
+                              . $level
+                              . "\" swi:suppress=\""
+                              . $suppress
+                              . "\" swi:criteria=\""
+                              . $criteria . "\">"
+                              . sprintf( "%.2f",
+                                $fileStat->{$keyStat}->{$keySubStat}->{$type} )
+                              . "</"
+                              . $type . ">\n";
+                        }
+                    }
+                    print $fh "          </" . $keySubStat . ">\n";
+                }
+                print $fh "        </" . $keyStat . ">\n";
+            }
+            print $fh "      </swi:statistic>\n";
+            print $fh "    </swi:file>\n";
+            print $fh "\n";
+        }
+        for (
+            my $fileId = 0 ;
+            $fileId <= $#{ $moduleBase->{"swi:file"} } ;
+            $fileId++
+          )
+        {
+            my $fileOld = $moduleBase->{"swi:file"}[$fileId];
+            if (
+                swiReportObjectFind(
+                    $report->{"swi:module"}[$moduleId]->{"swi:file"},
+                    $fileOld->{"swi:name"} ) == undef
+              )
+            {
+                print $fh "    <swi:file>\n";
+                print $fh "      <swi:name>"
+                  . $fileOld->{"swi:name"}
+                  . "</swi:name>\n";
+                print $fh "      <swi:location>"
+                  . $fileOld->{"swi:location"}
+                  . "</swi:location>\n";
+                print $fh
+                  "      <swi:modification>removed</swi:modification>\n";
+                print $fh "    </swi:file>\n";
+                print $fh "\n";
+            }
+        }
+
+        print $fh "    <swi:statistic>\n";
+        foreach my $keyStat ( keys %$moduleStat )
+        {
+            print $fh "      <" . $keyStat . ">\n";
+            my $subStat = $moduleStat->{$keyStat};
+            foreach my $keySubStat ( keys %$subStat )
+            {
+                my @types = (
+                    "swi:exact", "swi:average", "swi:min", "swi:max",
+                    "swi:total"
+                );
+                print $fh "        <" . $keySubStat . ">\n";
+                foreach my $type (@types)
+                {
+                    if (
+                        defined(
+                            $moduleStat->{$keyStat}->{$keySubStat}->{$type}
+                        )
+                      )
+                    {
+                        my ( $level, $suppress, $criteria ) =
+                          swiStatisticLevelGet( $keyStat, $keySubStat, $type,
+                            $projectName . "/" . $moduleName,
+                            $moduleStat, "swi:module" );
+                        my $statDiff = swiStatisticDiffGet(
+                            $moduleDiff,
+                            $moduleStat->{$keyStat}->{$keySubStat}->{$type},
+                            $moduleBase->{"swi:statistic"}->{$keyStat}
+                              ->{$keySubStat}->{$type}
+
+                        );
+                        print $fh "          <" . $type
+                          . " swi:change=\""
+                          . $statDiff
+                          . "\" swi:level=\""
+                          . $level
+                          . "\" swi:suppress=\""
+                          . $suppress
+                          . "\" swi:criteria=\""
+                          . $criteria . "\">"
+                          . sprintf( "%.2f",
+                            $moduleStat->{$keyStat}->{$keySubStat}->{$type} )
+                          . "</"
+                          . $type . ">\n";
+                    }
+                }
+                print $fh "        </" . $keySubStat . ">\n";
+
+            }
+            print $fh "      </" . $keyStat . ">\n";
+        }
+        print $fh "    </swi:statistic>\n";
+        print $fh "  </swi:module>\n";
+        print $fh "\n";
+    }
+    for (
+        my $moduleId = 0 ;
+        $moduleId <= $#{ $reportBase->{"swi:module"} } ;
+        $moduleId++
+      )
+    {
+        my $moduleOld = $reportBase->{"swi:module"}[$moduleId];
+        if (
+            swiReportObjectFind( $report->{"swi:module"},
+                $moduleOld->{"swi:name"} ) == undef
+          )
+        {
+            print $fh "  <swi:module>\n";
+            print $fh "    <swi:name>"
+              . $moduleOld->{"swi:name"}
+              . "</swi:name>\n";
+            print $fh "    <swi:location>"
+              . $moduleOld->{"swi:location"}
+              . "</swi:location>\n";
+            print $fh "    <swi:modification>removed</swi:modification>\n";
+            print $fh "  </swi:module>\n";
+            print $fh "\n";
+        }
+    }
+    print $fh "  <swi:statistic>\n";
+    foreach my $keyStat ( keys %$projectStat )
+    {
+        print $fh "    <" . $keyStat . ">\n";
+        my $subStat = $projectStat->{$keyStat};
+        foreach my $keySubStat ( keys %$subStat )
+        {
+            my @types =
+              ( "swi:exact", "swi:average", "swi:min", "swi:max", "swi:total" );
+            print $fh "      <" . $keySubStat . ">\n";
+            foreach my $type (@types)
+            {
+                if (
+                    defined( $projectStat->{$keyStat}->{$keySubStat}->{$type} )
+                  )
+                {
+                    my ( $level, $suppress, $criteria ) = swiStatisticLevelGet(
+                        $keyStat,     $keySubStat,  $type,
+                        $projectName, $projectStat, "swi:project"
+                    );
+                    my $statDiff = swiStatisticDiffGet(
+                        $projectDiff,
+                        $projectStat->{$keyStat}->{$keySubStat}->{$type},
+                        $reportBase->{"swi:statistic"}->{$keyStat}
+                          ->{$keySubStat}->{$type}
+
+                    );
+                    print $fh "        <" . $type
+                      . " swi:change=\""
+                      . $statDiff
+                      . "\" swi:level=\""
+                      . $level
+                      . "\" swi:suppress=\""
+                      . $suppress
+                      . "\" swi:criteria=\""
+                      . $criteria . "\">"
+                      . sprintf( "%.2f",
+                        $projectStat->{$keyStat}->{$keySubStat}->{$type} )
+                      . "</"
+                      . $type . ">\n";
+                }
+            }
+            print $fh "      </" . $keySubStat . ">\n";
+        }
+        print $fh "    </" . $keyStat . ">\n";
+    }
+    print $fh "  </swi:statistic>\n";
+    print $fh "</swi:report>\n";
+
+    return 0;
+}
+
+sub swiStatisticLevelGet
+{
+    my $keyStat    = shift();
+    my $keySubStat = shift();
+    my $type       = shift();
+    my $objName    = shift();
+    my $objStat    = shift();
+    my $objType    = shift();
+    my $statValue  = undef;
+
+    # Array of results: level, suppress level, criteria
+    my @returnResult = ( "undefined", "undefined", "" );
+
+    if (
+        defined( $config->{"swi:limits"}->{$keyStat}->{$keySubStat}->{$type} ) )
+    {
+        my $limit = $config->{"swi:limits"}->{$keyStat}->{$keySubStat}->{$type};
+        my $factor = 1;
+
+        if ( defined( $limit->{"swi:relation"} ) )
+        {
+            my @relation = undef;
+            @relation = split( /\//, $limit->{"swi:relation"} );
+
+            $factor =
+              $objStat->{ $relation[0] }->{ $relation[1] }->{ $relation[2] };
+
+            if ( !defined($factor) || $factor == 0 )
+            {
+                STATUS(
+"Wrong configuration for the limit '$keyStat/$keySubStat/$type'. Relation "
+                      . $limit->{"swi:relation"}
+                      . " is not found or points to zero value for object '$objName'"
+                );
+                $factor = 1;
+            }
+        }
+
+        $statValue = $objStat->{$keyStat}->{$keySubStat}->{$type} / $factor;
+        $statValue = sprintf( "%.2f", $statValue );
+
+        if (   $limit->{"swi:warning"} > $limit->{"swi:notice"}
+            && $limit->{"swi:notice"} > $limit->{"swi:info"} )
+        {
+            if ( $statValue > $limit->{"swi:warning"} )
+            {
+                $returnResult[0] = "warning";
+                $returnResult[2] = "["
+                  . $statValue
+                  . " greater than "
+                  . $limit->{"swi:warning"} . "]";
+            }
+            elsif ( $statValue > $limit->{"swi:notice"} )
+            {
+                $returnResult[0] = "notice";
+                $returnResult[2] = "["
+                  . $statValue
+                  . " greater than "
+                  . $limit->{"swi:notice"} . "]";
+            }
+            elsif ( $statValue > $limit->{"swi:info"} )
+            {
+                $returnResult[0] = "info";
+                $returnResult[2] = "["
+                  . $statValue
+                  . " greater than "
+                  . $limit->{"swi:info"} . "]";
+            }
+            else
+            {
+                $returnResult[0] = "regular";
+            }
+        }
+        elsif ($limit->{"swi:warning"} < $limit->{"swi:notice"}
+            && $limit->{"swi:notice"} < $limit->{"swi:info"} )
+        {
+            if ( $statValue < $limit->{"swi:warning"} )
+            {
+                $returnResult[0] = "warning";
+                $returnResult[2] = "["
+                  . $statValue
+                  . " less than "
+                  . $limit->{"swi:warning"} . "]";
+            }
+            elsif ( $statValue < $limit->{"swi:notice"} )
+            {
+                $returnResult[0] = "notice";
+                $returnResult[2] = "["
+                  . $statValue
+                  . " less than "
+                  . $limit->{"swi:notice"} . "]";
+            }
+            elsif ( $statValue < $limit->{"swi:info"} )
+            {
+                $returnResult[0] = "info";
+                $returnResult[2] =
+                  "[" . $statValue . " less than " . $limit->{"swi:info"} . "]";
+            }
+            else
+            {
+                $returnResult[0] = "regular";
+            }
+        }
+        else
+        {
+            STATUS(
+"Wrong settings in configuration file (<limits> section): swi:limit/$keyStat/$keySubStat/$type"
+            );
+            $returnResult[0] = "unresolved";
+        }
+
+        # check if suppressed
+        my $isFound = 0;
+
+      LOOPPATTERNS:
+        foreach ( @{ $limit->{"swi:suppress"}->{"swi:pattern"} } )
+        {
+            my $pattern = $_;
+            if ( ref($pattern) eq "HASH" && defined( $pattern->{"swi:level"} ) )
+            {
+                my $content = $pattern->{"content"};
+                if ( $objName =~ m/$content/ )
+                {
+                    if ( $isFound == 0 )
+                    {
+                        $returnResult[1] = $pattern->{"swi:level"};
+                        $isFound = 1;
+                    }
+                    else
+                    {
+
+                        # This object is matched by several patterns
+                        if ( $returnResult[1] ne $pattern->{"swi:level"} )
+                        {
+
+                            # and levels are not equal in different patterns
+                            STATUS(
+"Configuration is wrong: $objName is matched by several patterns"
+                            );
+                            $returnResult[1] = "unresolved";
+                        }
+                    }
+                }
+            }
+            else
+            {
+                STATUS(
+                "Wrong settings in configuration file (<limits/suppress> section): swi:limit/$keyStat/$keySubStat/$type: "
+                . "Level is missed in pattern for the object '$objType'"
+                );
+                $returnResult[1] = "unresolved";
+                $returnResult[2] = "[]";
+            }
+        }
+    }
+
+    return @returnResult;
+}
+
+sub swiStatisticDiffGet
+{
+    my $objDiff = shift();
+    my $newStat = shift();
+    my $oldStat = shift();
+    if ( $objDiff ne "added" )
+    {
+        return sprintf( "%.2f", $newStat - $oldStat );
+    }
+    return "";
+}
+
+sub swiReportObjectFind
+{
+    my $objects = shift();
+    my $objName = shift();
+
+    foreach (@$objects)
+    {
+        if (   $_->{"swi:name"} eq $objName
+            && $_->{"swi:modification"} ne "removed" )
+        {
+            return $_;
+        }
+    }
+
+    return undef;
+}
+
+sub swiReportModificationGet
+{
+    my $objBase  = shift();
+    my $objNew   = shift();
+    my $statType = shift();
+
+    if ( !defined($objBase) )
+    {
+        return "added";
+    }
+
+    my $newCrc =
+      $objNew->{"swi:statistic"}->{"swi:checksum"}->{"swi:source"}->{$statType};
+    my $newLength =
+      $objNew->{"swi:statistic"}->{"swi:length"}->{"swi:source"}->{$statType};
+    my $newDup =
+      $objNew->{"swi:statistic"}->{"swi:duplication"}->{"swi:executable"}
+      ->{$statType};
+
+    if ( $objBase->{"swi:statistic"}->{"swi:checksum"}->{"swi:source"}
+        ->{$statType} != $newCrc ||
+         $objBase->{"swi:statistic"}->{"swi:length"}->{"swi:source"}
+        ->{$statType} != $newLength )
+    {
+        return "modified";
+    }
+    if ( $objBase->{"swi:statistic"}->{"swi:duplication"}->{"swi:executable"}
+        ->{$statType} != $newDup )
+    {
+        return "cloned";
+    }
+
+    return "unmodified";
+}
+
+return 1;

+ 337 - 0
lib/SWI/Converter.pm

@@ -0,0 +1,337 @@
+#
+#    Software Index, Copyright 2010, Software Index Project Team
+#    Link: http://swi.sourceforge.net
+#
+#    This file is part of Software Index Tool.
+#
+#    Software Index is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#
+#    Software Index is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with Software Index.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+use strict;
+use XML::Simple;
+use FileHandle;
+use Data::Dumper;
+
+#
+# Export section
+#
+require Exporter;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $PREFERRED_PARSER);
+@ISA              = qw(Exporter);
+@EXPORT           = qw(swiConvert);
+@EXPORT_OK        = qw();
+$VERSION          = '1.0';
+$PREFERRED_PARSER = undef;
+
+#
+# Global variables
+#
+my $config = undef;
+
+#
+# Enter point
+#
+sub swiConvert
+{
+    $config = shift();
+
+    my $report   = undef;
+    my $exitCode = 0;
+
+    $report = XMLin(
+        $config->{"swi:report"}->{"swi:destination"} . "/"
+          . $config->{"swi:report"}->{"swi:xml"}->{"swi:name"},
+        ForceArray =>
+          [ "swi:module", "swi:file", "swi:function", "swi:reference" ]
+    );
+
+    # generate notification report
+    my $fh = new FileHandle(
+        $config->{"swi:report"}->{"swi:destination"} . "/"
+          . $config->{"swi:report"}->{"swi:notifications"}->{"swi:name"},
+        "w"
+      )
+      or die("Can not open output file!");
+
+    if ( defined( $ENV{USER} ) )
+    {
+        print $fh "User\t" . $ENV{USER} . "\n";
+    }
+    print $fh "\n";
+
+    my $projectStat     = $report->{"swi:statistic"};
+    my $projectName     = $config->{"swi:info"}->{"swi:project"}->{"swi:name"};
+    my $projectLocation = $config->{"swi:report"}->{"swi:destination"};
+    my $projectDiff     = "modified";
+    $exitCode +=
+      swiNotificationPrint( $fh, $projectName, $projectLocation, undef,
+        $projectStat, $projectDiff );
+    for (
+        my $moduleId = 0 ;
+        $moduleId <= $#{ $report->{"swi:module"} } ;
+        $moduleId++
+      )
+    {
+        my $moduleStat = $report->{"swi:module"}[$moduleId]->{"swi:statistic"};
+        my $moduleName = $report->{"swi:module"}[$moduleId]->{"swi:name"};
+        my $moduleLocation =
+          $report->{"swi:module"}[$moduleId]->{"swi:location"};
+        my $moduleDiff =
+          $report->{"swi:module"}[$moduleId]->{"swi:modification"};
+        $exitCode +=
+          swiNotificationPrint( $fh, $projectName . "/" . $moduleName,
+            $moduleLocation, undef, $moduleStat, $moduleDiff );
+        for (
+            my $fileId = 0 ;
+            $fileId <= $#{ $report->{"swi:module"}[$moduleId]->{"swi:file"} } ;
+            $fileId++
+          )
+        {
+            my $fileStat =
+              $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+              ->{"swi:statistic"};
+            my $fileName =
+              $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+              ->{"swi:name"};
+            my $fileLocation =
+              $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+              ->{"swi:location"};
+            my $fileDiff =
+              $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+              ->{"swi:modification"};
+            $exitCode += swiNotificationPrint(
+                $fh, $projectName . "/" . $moduleName . "/" . $fileName,
+                $moduleLocation, $fileLocation . ":0",
+                $fileStat, $fileDiff
+            );
+            for (
+                my $functionId = 0 ;
+                $functionId <= $#{
+                    $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+                      ->{"swi:function"}
+                } ;
+                $functionId++
+              )
+            {
+                my $functionRefs =
+                  $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+                  ->{"swi:function"}[$functionId]->{"swi:reference"};
+                my $functionStat =
+                  $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+                  ->{"swi:function"}[$functionId]->{"swi:statistic"};
+                my $functionName =
+                  $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+                  ->{"swi:function"}[$functionId]->{"swi:name"};
+                my $functionLocation =
+                  $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+                  ->{"swi:function"}[$functionId]->{"swi:location"};
+                my $functionDiff =
+                  $report->{"swi:module"}[$moduleId]->{"swi:file"}[$fileId]
+                  ->{"swi:function"}[$functionId]->{"swi:modification"};
+                $exitCode += swiNotificationPrint(
+                    $fh,
+                    $projectName . "/"
+                      . $moduleName . "/"
+                      . $fileName . "/"
+                      . $functionName,
+                    $moduleLocation,
+                    $functionLocation,
+                    $functionStat,
+                    $functionDiff,
+                    $functionRefs
+                );
+            }
+        }
+    }
+    $fh->close();
+
+    $fh = new FileHandle(
+        $config->{"swi:report"}->{"swi:destination"} . "/"
+          . $config->{"swi:report"}->{"swi:notifications"}->{"swi:name"},
+        "r"
+      )
+      or die("Can not open input file!");
+
+    while (<$fh>)
+    {
+        print STDERR $_;
+    }
+    $fh->close();
+
+    return $exitCode;
+}
+
+sub swiNotificationPrint
+{
+    my $file         = shift();
+    my $objName      = shift();
+    my $modLocation  = shift();
+    my $fileLocation = shift();
+    my $objStat      = shift();
+    my $objDiff      = shift();
+    my $objRefs      = shift();
+    my $returnCode   = 0;
+
+    if ( !defined($fileLocation) )
+    {
+        $fileLocation = ".";
+    }
+
+    # Print 'swi:modifications'
+    if (   $objDiff ne "unmodified"
+        && $config->{"swi:report"}->{"swi:notifications"}->{"swi:print"}
+        ->{ "swi:" . $objDiff }->{"swi:modifications"} eq "on" )
+    {
+        my $notification =
+            "$modLocation/$fileLocation: " . "info"
+          . ": Object "
+          . $objName
+          . " has been "
+          . $objDiff
+          . "\n\tObject         : "
+          . $objName . "\n";
+        print $file $notification;
+        print $file "\n";
+    }
+
+    # Print 'swi:failures'
+    foreach my $keyStat ( keys %$objStat )
+    {
+        my $subStat = $objStat->{$keyStat};
+        foreach my $keySubStat ( keys %$subStat )
+        {
+            my $types = $objStat->{$keyStat}->{$keySubStat};
+            foreach my $type ( keys %$types )
+            {
+                my $statInfo = $objStat->{$keyStat}->{$keySubStat}->{$type};
+                if (
+                    !(
+                        $statInfo->{"swi:level"} eq $statInfo->{"swi:suppress"}
+                        || (   $statInfo->{"swi:level"} eq "regular"
+                            && $statInfo->{"swi:suppress"} eq "undefined" )
+                    )
+                  )
+                {
+                    my $notification =
+                        "$modLocation/$fileLocation: "
+                      . $statInfo->{"swi:level"}
+                      . ": Index '"
+                      . "$keyStat/$keySubStat/$type"
+                      . "' exceeds the limit"
+                      . "\n\tObject         : "
+                      . $objName
+                      . "\n\tIndex value    : "
+                      . $statInfo->{"content"}
+                      . "\n\tModification   : "
+                      . $objDiff . " / "
+                      . $statInfo->{"swi:change"}
+                      . "\n\tSeverity       : "
+                      . $statInfo->{"swi:level"}
+                      . "\n\tCriteria       : "
+                      . $statInfo->{"swi:criteria"}
+                      . "\n\tSuppress level : "
+                      . $statInfo->{"swi:suppress"} . "\n";
+
+                    if ( $config->{"swi:report"}->{"swi:notifications"}
+                        ->{"swi:print"}->{ "swi:" . $objDiff }->{"swi:failures"}
+                        eq "on" )
+                    {
+                        print $file $notification;
+
+                        # Print 'swi:duplications'
+                        if (   $keyStat eq "swi:duplication"
+                            && $keySubStat eq "swi:symbols"
+                            && $config->{"swi:report"}->{"swi:notifications"}
+                            ->{"swi:print"}->{ "swi:" . $objDiff }
+                            ->{"swi:duplications"} eq "on" )
+                        {
+                            die('Internal Error occured!')
+                              if not defined($objRefs);
+                            print $file "\n";
+                            foreach my $dupData ( @{$objRefs} )
+                            {
+                                if ( $dupData->{'swi:ref:type'} eq 'dup' )
+                                {
+                                    print $file $modLocation . "/"
+                                      . $dupData->{'swi:dup:file'} . ":"
+                                      . $dupData->{'swi:dup:line'}
+                                      . ": warning: '"
+                                      . $dupData->{'swi:dup:size'}
+                                      . "' executable symbols are duplicated in '"
+                                      . $dupData->{'swi:dup:function'}
+                                      . "' function\n";
+                                }
+                            }
+                        }
+                        print $file "\n";
+                    }
+
+                    if ( $config->{"swi:report"}->{"swi:notifications"}
+                        ->{"swi:error"}->{ "swi:" . $objDiff } eq "on" )
+                    {
+                        $returnCode++;
+                    }
+                }
+                if (   $statInfo->{"swi:level"} eq "unresolved"
+                    || $statInfo->{"swi:suppress"} eq "unresolved" )
+                {
+                    my $notification =
+                        "$modLocation/$fileLocation: "
+                      . $statInfo->{"swi:level"}
+                      . ": The level/severity of index '"
+                      . "$keyStat/$keySubStat/$type"
+                      . "' is unresolved"
+                      . "\n\tObject         : "
+                      . $objName
+                      . "\n\tIndex value    : "
+                      . $statInfo->{"content"}
+                      . "\n\tModification   : "
+                      . $objDiff . " / "
+                      . $statInfo->{"swi:change"}
+                      . "\n\tSeverity       : "
+                      . $statInfo->{"swi:level"}
+                      . "\n\tCriteria       : "
+                      . $statInfo->{"swi:criteria"}
+                      . "\n\tSuppress level : "
+                      . $statInfo->{"swi:suppress"} . "\n\n";
+
+                    print $file $notification;
+                    $returnCode++;
+                }
+            }
+        }
+    }
+
+    # Print 'swi:scanmessages'
+    if ( $config->{"swi:report"}->{"swi:notifications"}->{"swi:print"}
+        ->{ "swi:" . $objDiff }->{"swi:scanmessages"} eq "on" )
+    {
+        foreach my $scanData ( @{$objRefs} )
+        {
+            if ( $scanData->{'swi:ref:type'} eq 'scan' )
+            {
+                print $file $modLocation . "/"
+                  . $scanData->{'swi:scan:file'} . ":"
+                  . $scanData->{'swi:scan:line'}
+                  . ": warning: '"
+                  . $scanData->{'swi:scan:message'}
+                  . "\n";
+                $returnCode++;
+            }
+        }
+    }
+
+    return $returnCode;
+}
+
+return 1;

+ 185 - 0
lib/SWI/Launcher.pm

@@ -0,0 +1,185 @@
+#
+#    Software Index, Copyright 2010, Software Index Project Team
+#    Link: http://swi.sourceforge.net
+#
+#    This file is part of Software Index Tool.
+#
+#    Software Index is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#
+#    Software Index is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with Software Index.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+use strict;
+use XML::Simple;
+use FileHandle;
+
+#
+# Export section
+#
+require Exporter;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $PREFERRED_PARSER);
+@ISA              = qw(Exporter);
+@EXPORT           = qw(swiLaunch);
+@EXPORT_OK        = qw();
+$VERSION          = '1.0';
+$PREFERRED_PARSER = undef;
+
+#
+# Subroutine for troubleshooting purposes
+#
+use Internal::Output;
+
+#
+# Include SWI libs
+#
+require SWI::Appraiser;
+require SWI::Converter;
+require SWI::Merger;
+require SWI::Processor;
+
+#
+# Global variables
+#
+my $config = undef;
+
+#
+# Enter point
+#
+sub swiLaunch
+{
+    my $returnCode       = 0;
+    my $rootLocation     = shift();
+    my $swiConfiguration = shift();
+
+    # $returnCode == 0 => no critical errors and warnings
+    # $returnCode >  0 => no critical errors, there are warnings
+    # $returnCode <  0 => there are critical errors
+
+    if ( $returnCode >= 0 )
+    {
+        if ( $swiConfiguration eq "" )
+        {
+            STATUS("Configuration file should be specified!");
+            $returnCode = -1;
+        }
+        else
+        {
+            STATUS("Configuration file: $swiConfiguration.");
+        }
+    }
+
+    if ( $returnCode >= 0 )
+    {
+        if ( swiConfigurationValidate($swiConfiguration) != 0 )
+        {
+            STATUS("Wrong configuration file!");
+            $returnCode = -2;
+        }
+    }
+
+    if ( $returnCode >= 0 )
+    {
+
+        # Generate report for every module separately
+        for (
+            my $i = 0 ;
+            $i <= $#{ $config->{"swi:modules"}->{"swi:module"} } ;
+            $i++
+          )
+        {
+            STATUS( "Processing module: '"
+                  . $config->{"swi:modules"}->{"swi:module"}[$i]
+                  ->{"swi:name"} . "'." );
+            my $result = swiProcess( $config, $i, $rootLocation );
+            if ( $result < 0 )
+            {
+                STATUS(
+                    "The are problems to report the index for the module!");
+                $returnCode = -5;
+            }
+            elsif ( $result > 0 )
+            {
+                STATUS("The are scan warnings and/or errors.");
+                $returnCode = $result;
+            }
+            else
+            {
+                STATUS("The module has been processed succesfully.");
+            }
+        }
+    }
+
+    if ( $returnCode >= 0 )
+    {
+
+        # Merge reports
+        if ( swiMerge($config) )
+        {
+            STATUS("The are problems to merge files to one report!");
+            $returnCode = -3;
+        }
+        else
+        {
+            STATUS("Merged report has been created.");
+        }
+    }
+
+    if ( $returnCode >= 0 )
+    {
+
+        # Add average/min/max/total values and generate final XML report
+        if ( swiAppraise($config) )
+        {
+            STATUS(
+                "The are problems to add average/min/max/total values!");
+            $returnCode = -4;
+        }
+        else
+        {
+            STATUS("Average/min/max/total values have been added.");
+        }
+    }
+
+    if ( $returnCode >= 0 )
+    {
+
+        # Convert results
+        my $result = swiConvert($config);
+        if ( $result < 0 )
+        {
+            STATUS("The are problems to convert the report!");
+            $returnCode = -5;
+        }
+        elsif ( $result > 0 )
+        {
+            STATUS("Report has been converted. There are exceeded limitations.");
+            $returnCode = $result;
+        }
+        else
+        {
+            STATUS("Report has been converted.");
+        }
+    }
+
+    return $returnCode;
+}
+
+sub swiConfigurationValidate
+{
+    $config =
+      XMLin( shift(),
+        ForceArray => [ "swi:module", "swi:rule", "swi:pattern" ] );
+
+    DEBUG("Configuration structure is: " . Dumper($config));
+    return 0;
+}
+
+return 1;

+ 105 - 0
lib/SWI/Merger.pm

@@ -0,0 +1,105 @@
+#
+#    Software Index, Copyright 2010, Software Index Project Team
+#    Link: http://swi.sourceforge.net
+#
+#    This file is part of Software Index Tool.
+#
+#    Software Index is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#
+#    Software Index is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with Software Index.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+use strict;
+use FileHandle;
+use XML::Simple;
+
+#
+# Export section
+#
+require Exporter;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $PREFERRED_PARSER);
+@ISA              = qw(Exporter);
+@EXPORT           = qw(swiMerge);
+@EXPORT_OK        = qw();
+$VERSION          = '1.0';
+$PREFERRED_PARSER = undef;
+
+#
+# Enter point
+#
+sub swiMerge
+{
+    my $config         = shift();
+    
+    my $reportLocation =
+        $config->{"swi:report"}->{"swi:destination"} . "/"
+      . $config->{"swi:report"}->{"swi:xml"}->{"swi:name"};
+
+    my $fh = new FileHandle( $reportLocation . ".x", "w" ) or die ("Can not open output file '$reportLocation'!");
+
+    print $fh "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+    print $fh "<swi:report>\n";
+    print $fh "\n";
+    print $fh "  <swi:info>\n";
+    print $fh "    <swi:version>1.0</swi:version>\n";
+    if (defined($ENV{USER}))
+    {
+        print $fh "    <swi:user>" . $ENV{USER} . "</swi:user>\n";
+    }
+    print $fh "    <swi:generator>SWI/MERGER</swi:generator>\n";
+    print $fh "  </swi:info>\n";
+    print $fh "\n";
+    
+    my $modulesCount = $#{ $config->{"swi:modules"}->{"swi:module"} } + 1;
+    my $filesCount = 0;
+    my $functionsCount = 0;
+
+    for ( my $i = 0 ; $i < $modulesCount ; $i++ )
+    {
+        my $modFh = new FileHandle( "$reportLocation.$i", "r" ) or die ("Can not open input file '$reportLocation.$i'!");
+        my @lines = <$modFh>;
+        $modFh->close();
+        for ( my $j = 3 ; $j < $#lines ; $j++ )
+        {
+            print $fh $lines[$j];
+            if ($lines[$j] =~ m/^[ ]*<swi:count>[ ]*$/)
+            {
+                if ($lines[$j+1] =~ m/^[ ]*<swi:files[ ]+swi:exact="([0-9]*)"[ ]*\/>[ ]*$/)
+                {
+                    my $numFilesInModule = $1;
+                    if ($lines[$j+2] =~ m/^[ ]*<swi:functions[ ]+swi:exact="([0-9]*)"[ ]*\/>[ ]*$/)
+                    {
+                        my $numFunctionsInModule = $1;
+                        $functionsCount += $numFunctionsInModule;
+                        $filesCount += $numFilesInModule;
+                    }
+                }
+            }
+        }
+    }
+
+    print $fh "  <swi:statistic>\n";
+    print $fh "    <swi:count>\n";
+    print $fh "      <swi:modules swi:exact=\"" . $modulesCount . "\" />\n";
+    print $fh "      <swi:files swi:exact=\"" . $filesCount . "\" />\n";
+    print $fh "      <swi:functions swi:exact=\"" . $functionsCount . "\" />\n";
+    print $fh "    </swi:count>\n";
+    print $fh "  </swi:statistic>\n";
+    print $fh "\n";
+
+    print $fh "</swi:report>\n";
+    
+    $fh->close();
+
+    return 0;
+}
+
+return 1;

Fichier diff supprimé car celui-ci est trop grand
+ 1499 - 0
lib/SWI/Processor.pm


+ 324 - 0
lib/String/CRC/Cksum.pm

@@ -0,0 +1,324 @@
+
+package String::CRC::Cksum;
+
+#use 5.6.1;
+use strict;
+use warnings;
+use Carp;
+
+require Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(cksum);
+our @EXPORT = qw();
+our $VERSION = '0.03';
+
+use fields qw(cksum size);
+
+my @crctab = (
+    0x00000000,
+    0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
+    0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
+    0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
+    0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
+    0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
+    0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
+    0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
+    0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
+    0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
+    0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
+    0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
+    0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
+    0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
+    0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
+    0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
+    0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
+    0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
+    0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
+    0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
+    0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
+    0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
+    0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
+    0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
+    0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
+    0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
+    0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+);
+
+
+sub new {
+    my $class = shift;
+    my String::CRC::Cksum $self = fields::new(ref $class || $class);
+    return $self->reset;
+}   # new
+
+
+sub reset {
+    my String::CRC::Cksum $self = shift;
+    $self->{cksum} = $self->{size} = 0;
+    return $self;
+}   # reset
+
+
+sub add {
+    use integer;
+    my String::CRC::Cksum $self = shift;
+    my $cksum = $self->{cksum};
+    my $size = $self->{size};
+
+    while(@_) {
+        my $n = length $_[0];
+
+        for(my $i = 0; $i < $n; ++$i) {
+            my $c = unpack 'C', substr $_[0], $i, 1;
+            $cksum = ($cksum << 8) ^ $crctab[($cksum >> 24) ^ $c];
+            ++$size;
+        }
+
+    }
+    continue { shift }
+
+    $self->{cksum} = $cksum;
+    $self->{size} = $size;
+
+    return $self;
+}   # add
+
+
+sub addfile {
+    my String::CRC::Cksum $self = shift;
+    my $stat;
+
+    local $_;
+    while(my $ifd = shift) {
+        $self->add($_) while $stat = read $ifd, $_, 4096;
+
+        if(! defined $stat) {
+            croak "error reading from filehandle: $!";
+        }
+    }
+
+    return $self;
+}   # addfile
+
+
+sub peek {
+    use integer;
+    my String::CRC::Cksum $self = shift;
+    my $cksum = $self->{cksum};
+    my $size = $self->{size};
+
+    # Extend with the length of the data
+    while($size != 0) {
+        my $c = $size & 0377;
+        $size >>= 8;
+        $cksum = ($cksum << 8) ^ $crctab[($cksum >> 24) ^ $c];
+    }
+    $cksum = ~ $cksum;
+
+    no integer;
+    my $crc = $cksum;
+    $crc += 4294967296 if $crc < 0;
+
+    return wantarray ? ($crc, $self->{size}) : $crc;
+
+}   # addfile
+
+
+sub result {
+    my String::CRC::Cksum $self = shift;
+    my ($cksum, $size) = $self->peek;
+    $self->reset;
+    return wantarray ? ($cksum, $size) : $cksum;
+}   # result
+
+
+sub cksum(@) {
+    my $sum = String::CRC::Cksum->new;
+
+    while(@_) {
+        if(ref $_[0])
+            { $sum->addfile($_[0]) }
+        else
+            { $sum->add($_[0]) }
+    }
+    continue { shift }
+
+    return $sum->result;
+}   # cksum
+
+1;
+
+__END__
+
+=head1 NAME
+
+String::CRC::Cksum - Perl extension for calculating checksums
+in a manner compatible with the POSIX cksum program.
+
+=head1 SYNOPSIS
+
+B<OO style>:
+  use String::CRC::Cksum;
+
+  $cksum = String::CRC::Cksum->new;
+  $cksum1 = $cksum->new;     # clone (clone is reset)
+
+  $cksum->add("string1");
+  $cksum->add("string2");
+  $cksum->add("string3", "string4", "string5", ...);
+  ...
+  ($cksum, $size) = $cksum->peek;
+  $cksum->add("string6", ...);
+  ...
+  ($cksum, $size) = $cksum->result;
+
+  $cksum1->addfile(\*file1);     # note: adding many files
+  $cksum1->addfile(\*file2);     # is probably a silly thing
+  $cksum1->addfile(\*file3);     # to do, but you *could*...
+  ...
+
+B<Functional style>:
+  use String::CRC::Cksum qw(cksum);
+
+  $cksum = cksum("string1", "string2", ...);
+
+  ($cksum, $size) = cksum("string1", "string2", ...);
+
+  $cksum = cksum(\*FILE);
+
+  ($cksum, $size) = cksum(\*FILE);
+
+=head1 DESCRIPTION
+
+The String::CRC::Cksum module calculates a 32 bit CRC,
+generating the same CRC value as the POSIX cksum program.
+If called in a list context, returns the length of the data
+object as well, which is useful for fully emulating
+the cksum program. The returned checksum will always be
+a non-negative integral number in the range 0..2^32-1.
+
+Despite its name, this module is able to compute the
+checksum of files as well as of strings.
+Just pass in a reference to a filehandle,
+or a reference to any object that can respond to
+a read() call and eventually return 0 at "end of file".
+
+Beware: consider proper use of binmode()
+if you are on a non-UNIX platform
+or processing files derived from other platforms.
+
+The object oriented interface can be used
+to progressively add data into the checksum
+before yielding the result.
+
+The functional interface is a convenient way
+to get a checksum of a single data item.
+
+None of the routines make local copies of passed-in strings
+so you can safely Cksum large strings safe in the knowledge
+that there won't be any memory issues.
+
+Passing in multiple files is acceptable,
+but perhaps of questionable value.
+However I don't want to hamper your creativity...
+
+=head1 FUNCTIONS                                                        
+
+The following functions are provided
+by the "String::CRC::Cksum" module.
+None of these functions are exported by default.
+
+=over 4
+
+=item B<new()>
+
+Creates a new String::CRC::Cksum object
+which is in a reset state, ready for action.
+If passed an existing String::CRC::Cksum object,
+it takes only the class -
+ie yields a fresh, reset object.
+
+=item B<reset()>
+
+Resets the Cksum object to the intialized state.
+An interesting phenomenom is,
+the CRC is not zero but 0xFFFFFFFF
+for a reset Cksum object.
+The returned size of a reset item will be zero.
+
+=item B<add("string", ...)>
+
+Progressively inject data into the Cksum object
+prior to requesting the final result.
+
+=item B<addfile(\*FILE, ...)>
+
+Progressively inject all (remaining) data from the file
+into the Cksum object prior to requesting the final result.
+The file handle passed in
+need only respond to the read() function to be usable,
+so feel free to pass in IO handles as needed.
+[hmmm - methinks I should have a test for that]
+
+=item B<peek($)>
+
+Yields the CRC checksum
+(and optionally the total size in list context)
+but does not reset the Cksum object.
+Repeated calls to peek() may be made
+and more data may be added.
+
+=item B<result($)>
+
+Yields the CRC checksum
+(and optionally the total size in list context)
+and then resets the Cksum object.
+
+=item B<cksum(@)>
+
+A convenient functional interface
+that may be passed a list of strings and filehandles.
+It will instantiate a Cksum object,
+apply the data and return the result
+in one swift, sweet operation.
+See how much I'm looking after you?
+
+NOTE: the filehandles must be passed as \*FD
+because I'm detecting a file handle using the ref() function.
+Therefore any blessed IO handle will also satisfy ref()
+and be interpreted as a file handle.
+
+=back
+
+=head2 EXPORT
+
+None by default.
+
+=head1 SEE ALSO
+
+manpages: cksum(1) or cksum(C) depending on your flavour of UNIX.
+
+http://www.opengroup.org/onlinepubs/007904975/utilities/cksum.html
+
+=head1 AUTHOR
+
+Andrew Hamm, E<lt>ahamm@cpan.orgE<gt>.
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright disclaimed 2003 by Andrew Hamm
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+Since I collected the algorithm
+from the Open Group web pages,
+they might have some issues but I doubt it.
+Let better legal minds than mine
+determine the issues if you need.
+[hopefully the CPAN and PAUSE administrators and/or testers
+will understand the issues better,
+and will replace this entire section
+with something reasonable - hint hint.]
+
+=cut

Fichier diff supprimé car celui-ci est trop grand
+ 3284 - 0
lib/XML/Simple.pm


+ 646 - 0
lib/XML/Simple/FAQ.pod

@@ -0,0 +1,646 @@
+package XML::Simple::FAQ;
+1;
+
+__END__
+
+=head1 Frequently Asked Questions about XML::Simple
+
+
+=head1 Basics
+
+
+=head2 What is XML::Simple designed to be used for?
+
+XML::Simple is a Perl module that was originally developed as a tool for
+reading and writing configuration data in XML format.  You can use it for
+many other purposes that involve storing and retrieving structured data in
+XML.
+
+You might also find XML::Simple a good starting point for playing with XML
+from Perl.  It doesn't have a steep learning curve and if you outgrow its
+capabilities there are plenty of other Perl/XML modules to 'step up' to.
+
+
+=head2 Why store configuration data in XML anyway?
+
+The many advantages of using XML format for configuration data include:
+
+=over 4
+
+=item *
+
+Using existing XML parsing tools requires less development time, is easier
+and more robust than developing your own config file parsing code
+
+=item *
+
+XML can represent relationships between pieces of data, such as nesting of
+sections to arbitrary levels (not easily done with .INI files for example)
+
+=item *
+
+XML is basically just text, so you can easily edit a config file (easier than
+editing a Win32 registry)
+
+=item *
+
+XML provides standard solutions for handling character sets and encoding
+beyond basic ASCII (important for internationalization)
+
+=item *
+
+If it becomes necessary to change your configuration file format, there are
+many tools available for performing transformations on XML files
+
+=item *
+
+XML is an open standard (the world does not need more proprietary binary
+file formats)
+
+=item *
+
+Taking the extra step of developing a DTD allows the format of configuration
+files to be validated before your program reads them (not directly supported
+by XML::Simple)
+
+=item *
+
+Combining a DTD with a good XML editor can give you a GUI config editor for
+minimal coding effort
+
+=back
+
+
+=head2 What isn't XML::Simple good for?
+
+The main limitation of XML::Simple is that it does not work with 'mixed
+content' (see the next question).  If you consider your XML files contain
+marked up text rather than structured data, you should probably use another
+module.
+
+If you are working with very large XML files, XML::Simple's approach of
+representing the whole file in memory as a 'tree' data structure may not be
+suitable.
+
+
+=head2 What is mixed content?
+
+Consider this example XML:
+
+  <document>
+    <para>This is <em>mixed</em> content.</para>
+  </document>
+
+This is said to be mixed content, because the E<lt>paraE<gt> element contains
+both character data (text content) and nested elements.
+
+Here's some more XML:
+
+  <person>
+    <first_name>Joe</first_name>
+    <last_name>Bloggs</last_name>
+    <dob>25-April-1969</dob>
+  </person>
+
+This second example is not generally considered to be mixed content.  The
+E<lt>first_nameE<gt>, E<lt>last_nameE<gt> and E<lt>dobE<gt> elements contain
+only character data and the  E<lt>personE<gt> element contains only nested
+elements.  (Note: Strictly speaking, the whitespace between the nested
+elements is character data, but it is ignored by XML::Simple).
+
+
+=head2 Why doesn't XML::Simple handle mixed content?
+
+Because if it did, it would no longer be simple :-)
+
+Seriously though, there are plenty of excellent modules that allow you to
+work with mixed content in a variety of ways.  Handling mixed content
+correctly is not easy and by ignoring these issues, XML::Simple is able to
+present an API without a steep learning curve.
+
+
+=head2 Which Perl modules do handle mixed content?
+
+Every one of them except XML::Simple :-)
+
+If you're looking for a recommendation, I'd suggest you look at the Perl-XML
+FAQ at:
+
+  http://perl-xml.sourceforge.net/faq/
+
+
+=head1 Installation
+
+
+=head2 How do I install XML::Simple?
+
+If you're running ActiveState Perl, you've probably already got XML::Simple 
+(although you may want to upgrade to version 1.09 or better for SAX support).
+
+If you do need to install XML::Simple, you'll need to install an XML parser
+module first.  Install either XML::Parser (which you may have already) or
+XML::SAX.  If you install both, XML::SAX will be used by default.
+
+Once you have a parser installed ...
+
+On Unix systems, try:
+
+  perl -MCPAN -e 'install XML::Simple'
+
+If that doesn't work, download the latest distribution from
+ftp://ftp.cpan.org/pub/CPAN/authors/id/G/GR/GRANTM , unpack it and run these
+commands:
+
+  perl Makefile.PL
+  make
+  make test
+  make install
+
+On Win32, if you have a recent build of ActiveState Perl (618 or better) try
+this command:
+
+  ppm install XML::Simple
+
+If that doesn't work, you really only need the Simple.pm file, so extract it
+from the .tar.gz file (eg: using WinZIP) and save it in the \site\lib\XML 
+directory under your Perl installation (typically C:\Perl).
+
+
+=head2 I'm trying to install XML::Simple and 'make test' fails
+
+Is the directory where you've unpacked XML::Simple mounted from a file server
+using NFS, SMB or some other network file sharing?  If so, that may cause
+errors in the the following test scripts:
+
+  3_Storable.t
+  4_MemShare.t
+  5_MemCopy.t
+
+The test suite is designed to exercise the boundary conditions of all
+XML::Simple's functionality and these three scripts exercise the caching
+functions.  If XML::Simple is asked to parse a file for which it has a cached
+copy of a previous parse, then it compares the timestamp on the XML file with
+the timestamp on the cached copy.  If the cached copy is *newer* then it will
+be used.  If the cached copy is older or the same age then the file is
+re-parsed.  The test scripts will get confused by networked filesystems if
+the workstation and server system clocks are not synchronised (to the
+second).
+
+If you get an error in one of these three test scripts but you don't plan to
+use the caching options (they're not enabled by default), then go right ahead
+and run 'make install'.  If you do plan to use caching, then try unpacking
+the distribution on local disk and doing the build/test there.
+
+It's probably not a good idea to use the caching options with networked
+filesystems in production.  If the file server's clock is ahead of the local
+clock, XML::Simple will re-parse files when it could have used the cached
+copy.  However if the local clock is ahead of the file server clock and a
+file is changed immediately after it is cached, the old cached copy will be
+used.
+
+Is one of the three test scripts (above) failing but you're not running on
+a network filesystem?  Are you running Win32?  If so, you may be seeing a bug
+in Win32 where writes to a file do not affect its modfication timestamp.
+
+If none of these scenarios match your situation, please confirm you're
+running the latest version of XML::Simple and then email the output of
+'make test' to me at grantm@cpan.org
+
+=head2 Why is XML::Simple so slow?
+
+If you find that XML::Simple is very slow reading XML, the most likely reason
+is that you have XML::SAX installed but no additional SAX parser module.  The
+XML::SAX distribution includes an XML parser written entirely in Perl.  This is
+very portable but not very fast.  For better performance install either
+XML::SAX::Expat or XML::LibXML.
+
+
+=head1 Usage
+
+=head2 How do I use XML::Simple?
+
+If you had an XML document called /etc/appconfig/foo.xml you could 'slurp' it
+into a simple data structure (typically a hashref) with these lines of code:
+
+  use XML::Simple;
+
+  my $config = XMLin('/etc/appconfig/foo.xml');
+
+The XMLin() function accepts options after the filename.
+
+
+=head2 There are so many options, which ones do I really need to know about?
+
+Although you can get by without using any options, you shouldn't even
+consider using XML::Simple in production until you know what these two
+options do:
+
+=over 4
+
+=item *
+
+forcearray
+
+=item *
+
+keyattr
+
+=back
+
+The reason you really need to read about them is because the default values
+for these options will trip you up if you don't.  Although everyone agrees
+that these defaults are not ideal, there is not wide agreement on what they
+should be changed to.  The answer therefore is to read about them (see below)
+and select values which are right for you.
+
+
+=head2 What is the forcearray option all about?
+
+Consider this XML in a file called ./person.xml:
+
+  <person>
+    <first_name>Joe</first_name>
+    <last_name>Bloggs</last_name>
+    <hobbie>bungy jumping</hobbie>
+    <hobbie>sky diving</hobbie>
+    <hobbie>knitting</hobbie>
+  </person>
+
+You could read it in with this line:
+
+  my $person = XMLin('./person.xml');
+
+Which would give you a data structure like this:
+
+  $person = {
+    'first_name' => 'Joe',
+    'last_name'  => 'Bloggs',
+    'hobbie'     => [ 'bungy jumping', 'sky diving', 'knitting' ]
+  };
+
+The E<lt>first_nameE<gt> and E<lt>last_nameE<gt> elements are represented as
+simple scalar values which you could refer to like this:
+
+  print "$person->{first_name} $person->{last_name}\n";
+
+The E<lt>hobbieE<gt> elements are represented as an array - since there is
+more than one.  You could refer to the first one like this:
+
+  print $person->{hobbie}->[0], "\n";
+
+Or the whole lot like this:
+
+  print join(', ', @{$person->{hobbie}} ), "\n";
+
+The catch is, that these last two lines of code will only work for people
+who have more than one hobbie.  If there is only one E<lt>hobbieE<gt>
+element, it will be represented as a simple scalar (just like
+E<lt>first_nameE<gt> and E<lt>last_nameE<gt>).  Which might lead you to write
+code like this:
+
+  if(ref($person->{hobbie})) {
+    print join(', ', @{$person->{hobbie}} ), "\n";
+  }
+  else {
+    print $person->{hobbie}, "\n";
+  }
+
+Don't do that.
+
+One alternative approach is to set the forcearray option to a true value:
+
+  my $person = XMLin('./person.xml', forcearray => 1);
+
+Which will give you a data structure like this:
+
+  $person = {
+    'first_name' => [ 'Joe' ],
+    'last_name'  => [ 'Bloggs' ],
+    'hobbie'     => [ 'bungy jumping', 'sky diving', 'knitting' ]
+  };
+
+Then you can use this line to refer to all the list of hobbies even if there
+was only one:
+
+  print join(', ', @{$person->{hobbie}} ), "\n";
+
+The downside of this approach is that the E<lt>first_nameE<gt> and
+E<lt>last_nameE<gt> elements will also always be represented as arrays even
+though there will never be more than one:
+
+  print "$person->{first_name}->[0] $person->{last_name}->[0]\n";
+
+This might be OK if you change the XML to use attributes for things that
+will always be singular and nested elements for things that may be plural:
+
+  <person first_name="Jane" last_name="Bloggs">
+    <hobbie>motorcycle maintenance</hobbie>
+  </person>
+
+On the other hand, if you prefer not to use attributes, then you could
+specify that any E<lt>hobbieE<gt> elements should always be represented as
+arrays and all other nested elements should be simple scalar values unless
+there is more than one:
+
+  my $person = XMLin('./person.xml', forcearray => [ 'hobbie' ]);
+
+The forcearray option accepts a list of element names which should always
+be forced to an array representation:
+
+  forcearray => [ qw(hobbie qualification childs_name) ]
+
+See the XML::Simple manual page for more information.
+
+
+=head2 What is the keyattr option all about?
+
+Consider this sample XML:
+
+  <catalog>
+    <part partnum="1842334" desc="High pressure flange" price="24.50" />
+    <part partnum="9344675" desc="Threaded gasket"      price="9.25" />
+    <part partnum="5634896" desc="Low voltage washer"   price="12.00" />
+  </catalog>
+
+You could slurp it in with this code:
+
+  my $catalog = XMLin('./catalog.xml');
+
+Which would return a data structure like this:
+
+  $catalog = {
+      'part' => [
+          {
+            'partnum' => '1842334',
+            'desc'    => 'High pressure flange',
+            'price'   => '24.50'
+          },
+          {
+            'partnum' => '9344675',
+            'desc'    => 'Threaded gasket',
+            'price'   => '9.25'
+          },
+          {
+            'partnum' => '5634896',
+            'desc'    => 'Low voltage washer',
+            'price'   => '12.00'
+          }
+      ]
+  };
+
+Then you could access the description of the first part in the catalog
+with this code:
+
+  print $catalog->{part}->[0]->{desc}, "\n";
+
+However, if you wanted to access the description of the part with the
+part number of "9344675" then you'd have to code a loop like this:
+
+  foreach my $part (@{$catalog->{part}}) {
+    if($part->{partnum} eq '9344675') {
+      print $part->{desc}, "\n";
+      last;
+    }
+  }
+
+The knowledge that each E<lt>partE<gt> element has a unique partnum attribute
+allows you to eliminate this search.  You can pass this knowledge on to
+XML::Simple like this:
+
+  my $catalog = XMLin($xml, keyattr => ['partnum']);
+
+Which will return a data structure like this:
+
+  $catalog = {
+    'part' => {
+      '5634896' => { 'desc' => 'Low voltage washer',   'price' => '12.00' },
+      '1842334' => { 'desc' => 'High pressure flange', 'price' => '24.50' },
+      '9344675' => { 'desc' => 'Threaded gasket',      'price' => '9.25'  }
+    }
+  };
+
+XML::Simple has been able to transform $catalog->{part} from an arrayref to
+a hashref (keyed on partnum).  This transformation is called 'array folding'.
+
+Through the use of array folding, you can now index directly to the
+description of the part you want:
+
+  print $catalog->{part}->{9344675}->{desc}, "\n";
+
+The 'keyattr' option also enables array folding when the unique key is in a
+nested element rather than an attribute.  eg:
+
+  <catalog>
+    <part>
+      <partnum>1842334</partnum>
+      <desc>High pressure flange</desc>
+      <price>24.50</price>
+    </part>
+    <part>
+      <partnum>9344675</partnum>
+      <desc>Threaded gasket</desc>
+      <price>9.25</price>
+    </part>
+    <part>
+      <partnum>5634896</partnum>
+      <desc>Low voltage washer</desc>
+      <price>12.00</price>
+    </part>
+  </catalog>
+
+See the XML::Simple manual page for more information.
+
+
+=head2 So what's the catch with 'keyattr'?
+
+One thing to watch out for is that you might get array folding even if you
+don't supply the keyattr option.  The default value for this option is:
+
+  [ 'name', 'key', 'id']
+
+Which means if your XML elements have a 'name', 'key' or 'id' attribute (or
+nested element) then they may get folded on those values.  This means that
+you can take advantage of array folding simply through careful choice of
+attribute names.  On the hand, if you really don't want array folding at all,
+you'll need to set 'key attr to an empty list:
+
+  my $ref = XMLin($xml, keyattr => []);
+
+A second 'gotcha' is that array folding only works on arrays.  That might
+seem obvious, but if there's only one record in your XML and you didn't set
+the 'forcearray' option then it won't be represented as an array and
+consequently won't get folded into a hash.  The moral is that if you're
+using array folding, you should always turn on the forcearray option.
+
+You probably want to be as specific as you can be too.  For instance, the
+safest way to parse the E<lt>catalogE<gt> example above would be:
+
+  my $catalog = XMLin($xml, keyattr => { part => 'partnum'},
+                            forcearray => ['part']);
+
+By using the hashref for keyattr, you can specify that only E<lt>partE<gt>
+elements should be folded on the 'partnum' attribute (and that the
+E<lt>partE<gt> elements should not be folded on any other attribute).
+
+By supplying a list of element names for forcearray, you're ensuring that
+folding will work even if there's only one E<lt>partE<gt>.  You're also
+ensuring that if the 'partnum' unique key is supplied in a nested element
+then that element won't get forced to an array too.
+
+
+=head2 How do I know what my data structure should look like?
+
+The rules are fairly straightforward:
+
+=over 4
+
+=item *
+
+each element gets represented as a hash
+
+=item *
+
+unless it contains only text, in which case it'll be a simple scalar value
+
+=item *
+
+or unless there's more than one element with the same name, in which case
+they'll be represented as an array
+
+=item *
+
+unless you've got array folding enabled, in which case they'll be folded into
+a hash
+
+=item *
+
+empty elements (no text contents B<and> no attributes) will either be
+represented as an empty hash, an empty string or undef - depending on the value
+of the 'suppressempty' option.
+
+=back
+
+If you're in any doubt, use Data::Dumper, eg:
+
+  use XML::Simple;
+  use Data::Dumper;
+  
+  my $ref = XMLin($xml);
+
+  print Dumper($ref);
+
+
+=head2 I'm getting 'Use of uninitialized value' warnings
+
+You're probably trying to index into a non-existant hash key - try
+Data::Dumper.
+
+
+=head2 I'm getting a 'Not an ARRAY reference' error
+
+Something that you expect to be an array is not.  The two most likely causes
+are that you forgot to use 'forcearray' or that the array got folded into a
+hash - try Data::Dumper.
+
+
+=head2 I'm getting a 'No such array field' error
+
+Something that you expect to be a hash is actually an array.  Perhaps array
+folding failed because one element was missing the key attribute - try
+Data::Dumper.
+
+
+=head2 I'm getting an 'Out of memory' error
+
+Something in the data structure is not as you expect and Perl may be trying
+unsuccessfully to autovivify things - try Data::Dumper.
+
+If you're already using Data::Dumper, try calling Dumper() immediately after
+XMLin() - ie: before you attempt to access anything in the data structure.
+
+
+=head2 My element order is getting jumbled up
+
+If you read an XML file with XMLin() and then write it back out with
+XMLout(), the order of the elements will likely be different.  (However, if
+you read the file back in with XMLin() you'll get the same Perl data
+structure).
+
+The reordering happens because XML::Simple uses hashrefs to store your data
+and Perl hashes do not really have any order.
+
+It is possible that a future version of XML::Simple will use Tie::IxHash
+to store the data in hashrefs which do retain the order.  However this will
+not fix all cases of element order being lost.
+
+If your application really is sensitive to element order, don't use
+XML::Simple (and don't put order-sensitive values in attributes).
+
+
+=head2 XML::Simple turns nested elements into attributes
+
+If you read an XML file with XMLin() and then write it back out with
+XMLout(), some data which was originally stored in nested elements may end up
+in attributes.  (However, if you read the file back in with XMLin() you'll
+get the same Perl data structure).
+
+There are a number of ways you might handle this:
+
+=over 4
+
+=item *
+
+use the 'forcearray' option with XMLin()
+
+=item *
+
+use the 'noattr' option with XMLout()
+
+=item *
+
+live with it
+
+=item *
+
+don't use XML::Simple
+
+=back
+
+
+=head2 Why does XMLout() insert E<lt>nameE<gt> elements (or attributes)?
+
+Try setting keyattr => [].
+
+When you call XMLin() to read XML, the 'keyattr' option controls whether arrays
+get 'folded' into hashes.  Similarly, when you call XMLout(), the 'keyattr'
+option controls whether hashes get 'unfolded' into arrays.  As described above,
+'keyattr' is enabled by default.
+
+=head2 Why are empty elements represented as empty hashes?
+
+An element is always represented as a hash unless it contains only text, in
+which case it is represented as a scalar string.
+
+If you would prefer empty elements to be represented as empty strings or the
+undefined value, set the 'suppressempty' option to '' or undef respectively.
+
+=head2 Why is ParserOpts deprecated?
+
+The C<ParserOpts> option is a remnant of the time when XML::Simple only worked
+with the XML::Parser API.  Its value is completely ignored if you're using a
+SAX parser, so writing code which relied on it would bar you from taking
+advantage of SAX.
+
+Even if you are using XML::Parser, it is seldom necessary to pass options to
+the parser object.  A number of people have written to say they use this option
+to set XML::Parser's C<ProtocolEncoding> option.  Don't do that, it's wrong,
+Wrong, WRONG!  Fix the XML document so that it's well-formed and you won't have
+a problem.
+
+Having said all of that, as long as XML::Simple continues to support the
+XML::Parser API, this option will not be removed.  There are currently no plans
+to remove support for the XML::Parser API.
+
+=cut
+
+

+ 924 - 0
swi_config_sample.xml

@@ -0,0 +1,924 @@
+<!--
+   
+    Software Index, Copyright 2010, Software Index Project Team
+    Link: http://swi.sourceforge.net
+
+    This file is part of Software Index Tool.
+
+    Software Index is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, version 3 of the License.
+
+    Software Index is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Software Index.  If not, see <http://www.gnu.org/licenses/>.
+   
+-->
+
+<!--
+    Configuration file is formed in XML format.
+    
+    Use this file as a 'configration file description' and
+    create new configs using this file as a baseline.
+    
+    Comments below provides with the description for all options.
+    Commented XML sections make patterns for your specific extensions.
+    Some of them demostrates usage examples.
+-->
+
+
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Root node 'swi:configuration' is mandatory. It's name is hardcoded. -->
+<swi:configuration>
+
+  <!-- Section 'swi:info' is used for descriptive purposes. -->
+  <swi:info>
+    <!--
+        Option 'swi:version' should be equal to 1.
+        It is reserved for further extensions.
+    -->
+    <swi:version>1</swi:version>
+    <!--
+        Option 'swi:project/swi:name' is used in the final report.
+        All objects are references in the following format:
+        YOUR_PROJECT_NAME/YOUR_MODULE_NAME/FILE/FUNCTION_NAME
+        Changes in this sections automatically reflected in the report.
+    -->
+    <swi:project>
+      <!-- Modify this option in order to refer to the actual name of your solution -->
+      <swi:name>YOUR_PROJECT_NAME</swi:name>
+    </swi:project>
+    <!--
+        This section for tracing purposes.
+        If you workflow assumes history records in project files,
+        this is the place to keep your records
+    -->
+    <swi:history>
+      <!-- Section 'swi:revision' can be repeated several times -->
+      <swi:revision>
+        <swi:name>ALFA</swi:name>
+        <swi:user>USER</swi:user>
+        <swi:comment>Sample configuration with the description created</swi:comment>
+      </swi:revision>
+      <!--
+          Add here the next 'swi:revision' section, for example:
+          <swi:revision>
+            <swi:name>BETA</swi:name>
+            <swi:user>USER</swi:user>
+            <swi:comment>Description of the next update</swi:comment>
+          </swi:revision>
+      -->
+    </swi:history>
+  </swi:info>
+
+  <!-- Section 'swi:modules' define where to get sources and how to process them. -->
+  <swi:modules>
+    <!-- Section 'swi:module' can be repeated several times -->
+    <swi:module>
+      <!--
+          Option 'swi:name' is used in the final report.
+          All objects are references in the following format:
+          YOUR_PROJECT_NAME/YOUR_MODULE_NAME/FILE/FUNCTION_NAME
+          Changes in this sections automatically reflected in the report.
+         
+          Modify this option in order to refer to the actual name of your module
+      -->
+      <swi:name>YOUR_MODULE_NAME</swi:name>
+      <!--
+          This is a full path to the directory where module's source files are stored
+          It can be relative or full path (recommended).
+          If it is relative path, you need to run Software Index tool from the relatively correct folder.
+      -->
+      <swi:location>/path/to/my/module</swi:location>
+      <!-- Section 'swi:files' which files to process and which to miss -->
+      <swi:files>
+        <!--
+            This option is a regular expression.
+            If file name (in module's location) is mathced by this expression,
+            it is included to the list for processing.
+            Otherwise, it is missed and not touched by Software Index tool.
+            The example below matches files with the following extensions:
+            .c, .h, .cpp, .hpp
+        -->
+        <swi:include>^.*[.][chCH]([pP][pP])?$</swi:include>
+        <!--
+            This option is a regular expression.
+            If file name (in module's location) is mathced by this expression,
+            it is excluded from the list for processing
+            (even if it was previously mathced by the previous option).
+            The example below matches files with the following extensions:
+            .gz.c, .gz.h, .gz.cpp, .gz.hpp
+        -->
+        <swi:exclude>^.*[.][gG][zZ][.][chCH]([pP][pP])?$</swi:exclude>
+      </swi:files>
+      
+      <!--
+          The Software Index does not parse 'preprocessor' statements, like a C/C++ compiler.
+          It just removes lines with preprocessor defines, includes, conditions and so on.
+          As a result a code may include unfolded preprocessor strings
+          which are normally replaced/removed by a compiler.
+          
+          For example, the initial code
+              line  1: #define MAXIMUM_NUMBER (100)
+              line  2: #ifdef DEBUG
+              line  3:     if (currentNumber < MAXIMUM_NUMBER) {
+              line  4:         printf("New overflow detected: %d\n", currentNumber);
+              line  5:         count++;
+              line  6:     }
+              line  7: #else
+              line  8:     if (currentNumber < MAXIMUM_NUMBER) {
+              line  9:         count++;
+              line 10:     }
+              line 11: #endif
+          is converted to:
+              line  1: 
+              line  2: 
+              line  3:     if (currentNumber < MAXIMUM_NUMBER) {
+              line  4:         printf("New overflow detected: %d\n", currentNumber);
+              line  5:         count++;
+              line  6:     }
+              line  7: 
+              line  8:     if (currentNumber < MAXIMUM_NUMBER) {
+              line  9:         count++;
+              line 10:     }
+              line 11: 
+          In the last example, the final code is parsable.
+          If all your preprocessor statements are in the similar style and does not
+          corrupt the structure of block start/end delimeters as in the example above,
+          you do not need to bother about preprocessor stuff.
+          
+          However, if the initial code includes lines like these:
+              line  1: #define MAXIMUM_NUMBER (100)
+              line  2:     if (currentNumber < MAXIMUM_NUMBER) {
+              line  3: #ifdef DEBUG
+              line  4:         printf("New overflow detected: %d\n", currentNumber);
+              line  5:         count++;
+              line  6:     }
+              line  7: #else
+              line  8:         count++;
+              line  9:     }
+              line 10: #endif
+          it is not parsable, because brackets mismatch is detected after preprocessing
+          (see lines 6 and 9 below):
+              line  1: 
+              line  2:     if (currentNumber < MAXIMUM_NUMBER) {
+              line  3: 
+              line  4:         printf("New overflow detected: %d\n", currentNumber);
+              line  5:         count++;
+              line  6:     }
+              line  7: 
+              line  8:         count++;
+              line  9:     }
+              line 10: 
+          Thus, in order to use Software Index tool it is necessary to refactor places,
+          like in the example above.
+          
+          The section 'swi:preprocessor' is useful in other cases.
+          For example, if the initial code includes the following lines:
+              line  1: #define DECLARE_HANDLER(handlerName)                  \
+              line  2:     int handlerName(unsigned int stateId,             \
+              line  3:                     unsigned int eventId,             \
+              line  4:                     struct EVENT_DATA_TYPE eventData)
+              line  5: 
+              line  6: DECLARE_HANDLER(leftButtonClick);
+              line  7: DECLARE_HANDLER(rightButtonClick);
+              line  8: 
+              line  9: DECLARE_HANDLER(leftButtonClick)
+              line 10: {
+              line 11:     /* This function is called when left mouse button is clicked */
+              line 12:     ...
+              line 13: }
+              line 14: 
+              line 15: DECLARE_HANDLER(rightButtonClick)
+              line 16: {
+              line 17:     /* This function is called when right mouse button is clicked */
+              line 18:     ...
+              line 19: }
+              line 20: 
+          Software Index tool detects function DECLARE_HANDLER twice. As a result, they will be named:
+              'DECLARE_HANDLER' and 'DECLARE_HANDLER:1' accordingly.
+          
+          The better solution is to preprocess these strings using the preprocessor feature.
+          For this particular example, it is recommended to define the rule which replaces the string:
+              lines  6,7,9,15: DECLARE_HANDLER(_xxx_)
+          by
+              lines  6,7,9,15: int _xxx_(unsigned int stateId, unsigned int eventId, struct EVENT_DATA_TYPE eventData)
+          where
+              _xxx_ is the actual function name.
+          In other words, the 'swi:preprocessor' section should include the following XML tags:
+              <swi:rule>
+                <swi:filepattern>your_regexp_file_pattern</swi:filepattern>
+                <swi:searchpattern>([^_a-zA-Z0-9])DECLARE_HANDLER\(([^_a-zA-Z0-9])\)</swi:searchpattern>
+                <swi:replacepattern>${1}int ${2}(unsigned int stateId, unsigned int eventId, struct EVENT_DATA_TYPE eventData)</swi:replacepattern>
+              </swi:rule>
+          As a result, the functions will be detected with the correct names:
+              'leftButtonClick' and 'rightButtonClick' accordingly.
+      -->
+      <swi:preprocessor>
+        <!-- Section 'swi:rule' can be repeated several times -->
+        <swi:rule>
+          <!--
+              This option is a regular expression.
+              If name of file which is under processing is mathced by this expression,
+              it is preprocessed according to the rule definitions.
+              Otherwise, it is missed and not touched by the internal preprocessor tool.
+              The example below matches files with the following extensions:
+              .h, .hpp
+          -->
+          <swi:filepattern>^.*[.][hH]([pP][pP])?$</swi:filepattern>
+          <!--
+              Options 'swi:searchpattern' and 'swi:replacepattern' are regular expressions.
+              Preprocessor searches the code by the 'swi:searchpattern' pattern.
+              If it is found it is replaced by 'swi:replacepattern' string,
+              which is interpolated before: variables ${1}, ${2}, ... are replaced by actual strings
+              from 'swi:searchpattern' regexp hooks.
+          -->
+          <swi:searchpattern>([^_a-zA-Z0-9])DECLARE_HANDLER\(([^_a-zA-Z0-9])\)</swi:searchpattern>
+          <swi:replacepattern>${1}int ${2}(unsigned int stateId, unsigned int eventId, struct EVENT_DATA_TYPE eventData)</swi:replacepattern>
+        </swi:rule>
+        <!--
+            Add here the next 'swi:rule' section, for example:
+            <swi:rule>
+              <swi:filepattern>.*</swi:filepattern>
+              <swi:searchpattern>(\s+)(union\s*)({)</swi:searchpattern>
+              <swi:replacepattern>$1$2 _noname_ $3</swi:replacepattern>
+            </swi:rule>
+        -->
+      </swi:preprocessor>
+      
+      <!--
+          Scaner tool is used for global searching and violation reporting.
+          If the scaner finds something, it reports the message which is also configured
+          and increases the exit code from the Software Index Tool.
+          
+          For example, if you codding style requires to open the block begging from the new line:
+              line  1: 
+              line  2:     if (currentNumber < MAXIMUM_NUMBER)
+              line  3:     {
+              line  4:         count++;
+              line  5:     }
+              line  6: 
+          it is possible to check it by scaner defining the 'swi:searchpattern' in the following way:
+              [\n][^{][{]
+          This regular expression matches the code which is 'badly' formated:
+              line  1: 
+              line  2:     if (currentNumber < MAXIMUM_NUMBER) {
+              line  3:         count++;
+              line  4:     }
+              line  5: 
+          
+          More examples. If you codding style does not allow to have noname structures, enums or unions:
+              line  1: 
+              line  2:     typedef struct _NAME_HERE_IS_MANDATORY_
+              line  3:     {
+              line  4:         int a;
+              line  5:     } my_type;
+              line  6: 
+          it is possible to check it by scaner defining the 'swi:searchpattern' in the following way:
+              (\s+)((union)|(enum)|(struct))(\s*{)
+          This regular expression matches the code which is 'wrongly' written:
+              line  1: 
+              line  2:     typedef struct // noname here
+              line  3:     {
+              line  4:         int a;
+              line  5:     } my_type;
+              line  6: 
+          And if the 'swi:messagepattern' equals to:
+              Noname '$2' detected.
+          the error report looks like this:
+              file.c:2: warning: Noname 'struct' detected.
+      -->
+      <swi:scanner>
+        <!-- Section 'swi:rule' can be repeated several times -->
+        <swi:rule>
+          <!--
+              This option is a regular expression.
+              If name of file which is under processing is mathced by this expression,
+              it is scaned according to the rule definitions.
+              Otherwise, it is missed and not touched by the internal scaner tool.
+              The example below matches all files.
+          -->
+          <swi:filepattern>.*</swi:filepattern>
+          <!--
+              Options 'swi:searchpattern' and 'swi:messagepattern' are regular expressions.
+              Scaner searches the code by the 'swi:searchpattern' pattern.
+              If it is found the 'swi:messagepattern' string is printed to STDERR,
+              'swi:messagepattern' string is interpolated: variables ${1}, ${2}, ... are replaced by actual strings
+              from 'swi:searchpattern' regexp hooks.
+          -->
+          <swi:searchpattern>(\s+)((union)|(enum)|(struct))(\s*{)</swi:searchpattern>
+          <swi:messagepattern>Noname '$2' detected.</swi:messagepattern>
+      	  <!--
+      	      The 'swi:codecontent' option defines the content for scanner, it can be:
+      	    
+      	          initial         - the initial source content
+      	          code            - the initial code (without comments)
+      	          comments        - comments only (no code)
+      	          nopreprocessor  - preprocessor strings excluded (without comments and preprocessor)
+      	          nostrings       - strings excluded (without comments and strings)
+      	          purified        - strings and preprocessor excluded (without comments, strings and preprocessor)
+                  commentheader   - comments before function's header
+      	          functionname    - name of a function
+                  functionhead    - purified function's header, no body
+                  functionbody    - purified function's body, no header
+      	    
+      	      By default, the 'purified' code is scanned
+      	  -->
+          <swi:codecontent>purified</swi:codecontent>
+        </swi:rule>
+        <!--
+            Add here the next 'swi:rule' section, for example:
+            <swi:rule>
+              <swi:filepattern>.*</swi:filepattern>
+              <swi:searchpattern>#define\s*([_a-zA-Z0-9]+)\s*[\(]?([0-9]+)[\)]?</swi:searchpattern>
+              <swi:messagepattern>Define '${1}' of the number '${2}'
+                   should be replaced by 'static const int ${1} = ${2};'</swi:messagepattern>
+              <swi:codecontent>nostrings</swi:codecontent>
+            </swi:rule>
+        -->
+      </swi:scanner>
+      
+      <!--
+          Indexer measures common software statistics per functions, files, modules and project.
+          They are:
+          
+              STATICTIC-GROUP / STATISTIC-NAME     - DESCRIPTION
+              =============== / ================== - ============================
+              swi:length      / swi:source         - total number of symbols
+              swi:length      / swi:blank          - number of space symbols
+              swi:length      / swi:executable     - number of executable symbols
+              swi:length      / swi:comment        - number of symbols inside comments
+              swi:length      / swi:function:name  - number of symbols in name of a function
+              swi:lines       / swi:comment:header - number of lines in a comment before a function/file
+              swi:lines       / swi:source         - total number of lines
+              swi:lines       / swi:blank          - number of empty lines
+              swi:lines       / swi:executable     - number of executable lines
+              swi:lines       / swi:comment        - number of lines with comments
+              swi:complexity  / swi:blocks         - number of blocks
+              swi:complexity  / swi:cyclomatic     - McCabe's (Mayer's) Cyclomatic complexity metric
+              swi:complexity  / swi:maxdepth       - Maximum indent level
+              swi:count       / swi:functions      - Number of functions
+              swi:count       / swi:files          - Number of files
+              swi:count       / swi:modules        - Number of modules
+              swi:checksum    / swi:source         - Checksum for the source code (compare purposes)
+              
+          Every statistic is reported incombination with the following types:
+
+              STATICTIC-TYPE  - DESCRIPTION
+              =============== - ============================
+              swi:exact       - exact value
+              swi:average     - average value within a distribution
+              swi:min         - minimum value within a distribution
+              swi:max         - maximum value within a distribution
+              swi:total       - sum of values within a distribution
+              
+          Cumulative types (swi:average, swi:min, swi:max and swi:total) are reported if they are applicable.
+      -->
+      <swi:indexer:common>
+        <!-- No settings currently available -->
+      </swi:indexer:common>
+      
+	  <!--
+	      Duplication indexer searches for identical code fragments, calculates total number of symbols
+		  in continues duplicated fragments per function, file, module and project.
+          
+          Note: blank symbols (spaces, tabulations, newlines) are ignored when strings are compared.
+		  
+		  The duplciation statistic is reported by reference 'swi:duplication/swi:symbols':
+          
+              STATICTIC-GROUP / STATISTIC-NAME     - DESCRIPTION
+              =============== / ================== - ============================
+              swi:duplication / swi:symbols        - Number of duplicated symbols
+              
+          The statistic is reported incombination with the following types:
+          
+              STATICTIC-TYPE  - DESCRIPTION
+              =============== - ============================
+              swi:exact       - exact value
+              swi:average     - average value within a distribution
+              swi:min         - minimum value within a distribution
+              swi:max         - maximum value within a distribution
+              swi:total       - sum of values within a distribution
+              
+          Cumulative types (swi:average, swi:min, swi:max and swi:total) are reported if they are applicable.
+		  
+		  This internal tool also collects pointers to duplicated fragments and prints them.
+		  Also, they can be easily extracted from the final report for other needs.
+	  -->
+      <swi:indexer:dup>
+      	<!--
+      	    The 'swi:codecontent' option defines the content for the duplicatiion searcher, it can be:
+      	    
+      	        initial         - the initial source content
+      	        code            - the initial code (without comments)
+      	        comments        - comments only (no code)
+      	        nopreprocessor  - preprocessor strings excluded (without comments and preprocessor)
+      	        nostrings       - strings excluded (without comments and strings)
+      	        purified        - strings and preprocessor excluded (without comments, strings and preprocessor)
+                commentheader   - comments before function's header
+                functionname    - name of a function
+                functionhead    - purified function's header, no body
+                functionbody    - purified function's body, no header
+      	    
+      	    By default, the 'purified' content is used.
+            
+            Recomendation: if Software Index tool detects a log of duplicated fragments
+            which are within function's header (declarations), usually it is the case when
+            where are overloaded functions with a huge list of arguments, it is recommended
+            to set 'swi:codecontent' option to 'functionbody' value.
+      	-->
+      	<swi:codecontent>purified</swi:codecontent>
+      	<!--
+      	    The 'swi:enabled' option activates/deativates the calculation of the duplication index:
+      	    
+      	        on  - the search tool is launched and the statistic is calculated
+      	        off - the search tool is not started, the statistic is reported by zeros
+      	    
+      	    By default, the this is not enabled
+      	-->
+        <swi:enabled>on</swi:enabled>
+      	<!--
+      	    The 'swi:minlength' option defines the minimal length of the duplicated fragment.
+            In other words, if the duplicated fragment is found it is at least 'swi:minlength' in length
+            Too small value, for example 10, results in excessive growth of the total duplication index
+            Too large value may cause the empty search result.
+      	-->
+        <swi:minlength>100</swi:minlength>
+      	<!--
+      	    The 'swi:proximity' option allows to report two code fragments as duplicated
+            even if they are not matched exactly till the end.
+            It helps to search 'almost' duplicated code fragments instead of 'exactly' duplicated.
+            
+            This option is a value from 1 till 100.
+            
+            For example, if there is found group of duplicated code fragments
+            which are 100 symbols in length and 'swi:proximity' is equal to 80,
+            this group will be extended by other found code fragments
+            which have 80 or more the same symbols.
+      	-->
+        <swi:proximity>100</swi:proximity>
+        <!--
+            All code fragments are related to some function.
+            Except global defines, declarations, class definitions and so on.
+            Software Index attaches global code to the 'dummy' function with name '>>>GLOBAL<<<'.
+            
+            The 'swi:globalcode' configures whether the global code should be included
+            to the scope of search for duplication.
+
+      	        on  - global code is scaned for duplication
+      	        off - global code is missed
+      	    
+      	    By default, the this option is 'off'.
+        -->
+        <swi:globalcode>on</swi:globalcode>
+      </swi:indexer:dup>
+
+    </swi:module>
+    
+    <!--
+        Add here the next 'swi:module' section.
+    -->
+
+  </swi:modules>
+  
+  <!--
+      The 'swi:report' section defines the location for reports and output settings.
+  -->
+  <swi:report>
+    <!--
+        This is a full path to the directory where report files should be stored
+        It can be relative or full path (recommended).
+        If it is relative path, you need to run Software Index tool from the relatively correct folder.
+    -->
+    <swi:destination>/path/to/the/destination/folder</swi:destination>
+    <!--
+        The main report is generated to XML format.
+        Other files are created from the XML file by converters.
+    -->
+    <swi:xml>
+      <!-- Name of final XML file in 'swi:destination' directory. -->
+      <swi:name>swi_report_sample.xml</swi:name>
+      <!--
+          Software Index process software modules independantly from their versions.
+          
+          However, if it is executed with a reference to a report generated by 
+          Software Index tool for the baseline versions of your modules
+              (baseline version - previous version of the product, the state of product
+               before the time when changes and updates and/or additions applied)
+          it has additional information and can report the modification status
+          for every statistic (increased/decreased) and for every
+          function, file and module (added, removed, modified, cloned or unmodified).
+          
+          Description of modification statuses:
+              added      - there was no object in the baseline version
+                           and it was added to the current version
+              removed    - there was object in the baseline version
+                           and it was removed from the current version
+              modified   - there was object in the baseline version
+                           and it was changed/updated
+              cloned     - there was object in the baseline version,
+                           it was not changed/updated in the new version,
+                           but duplication index (swi:duplication/swi:executable)
+                           was decreased or increased
+              unmodified - the object was now touched
+          
+          In addition to the extra info, it helps to filter the output information.
+          For example, it is possible to print limit overheads for added and
+          modified functions only (missing analogues messages for untouched objects).
+          
+          As a result, Software Index tool can be deployed to the software development
+          process without extra work and remakes in your old sources.
+          You can always start to measure/control the characteristics of
+          objectes (functions) which are affected by your recent updates only,
+          improving the total quality step-by-step (version-by-version).
+          See 'Example 1' in the next section for examples.
+          
+          The 'swi:baseline' option points out to the report for the baseline version.
+          It should be stored in 'swi:destination' directory.
+          If it is not defined,
+          it is considered that all objects (functions, files and so on) were added.
+      -->
+      <swi:baseline>swi_report_sample.xml</swi:baseline>
+    </swi:xml>
+    <!--
+        Notifications are prinited to file and to stderr stream.
+        They highlight exceeded limitations of indexes/statistics
+        and other notices, warnings and errors.
+    -->
+    <swi:notifications>
+      <!-- Name of log file in 'swi:destination' directory. -->
+      <swi:name>swi_report_sample.log</swi:name>
+      <!--
+          Flags in XML sub-tags in 'swi:error' section
+          activate/deactivate counter of errors/notifications.
+          If the object with the corresponding modification status
+          has some notification (exceeded limit, for example),
+          the error counter is increased.
+          The final value of the error counter is returned
+          to the operation system as exit code.
+          Thus, if there are no notifications/errors, exit code is zero.
+          
+          Options should be equal to some value from the list below:
+      	      on  - counter is increased
+      	      off - counter is not increased
+          
+          Example 1: 'easy deployment of the tool'
+              added      - 'on'
+              removed    - 'on' or 'off'
+              modified   - 'on'
+              cloned     - 'on' or 'off'
+              unmodified - 'off'
+              
+          Example 2: 'global control'
+              added      - 'on'
+              removed    - 'on'
+              modified   - 'on'
+              cloned     - 'on'
+              unmodified - 'on'
+
+          Example 3: 'always success (zero exit code)'
+              added      - 'off'
+              removed    - 'off'
+              modified   - 'off'
+              cloned     - 'off'
+              unmodified - 'off'
+      -->
+      <swi:error>
+        <swi:added>on</swi:added>
+        <swi:removed>on</swi:removed>
+        <swi:modified>on</swi:modified>
+        <swi:cloned>on</swi:cloned>
+        <swi:unmodified>on</swi:unmodified>
+      </swi:error>
+      <!--
+          The 'swi:print' section defines which messages should be printed
+          to the log and to stderr stream. By analogy with the previous 
+          configuration section, the flags are defined per modification status.
+          There are three types of messages which are configred by 
+          'swi:failures', 'swi:modifications' and 'swi:duplications' options.
+          
+          (*) 'swi:failures'      - notifications about broken limits
+              'swi:modifications' - notes about added/modified/cloned/removed objects
+              'swi:duplications'  - pointers to the duplicated regions in files
+          (*) 'swi:scanmessages'  - messages collected by the internal scaner tool
+                                    (see 'swi:scaner' section above)
+          
+          (*) marks types of messages which affect the exit code (see 'swi:error' section above).
+      -->
+      <swi:print>
+        <swi:added>
+          <swi:failures>on</swi:failures>
+          <swi:modifications>off</swi:modifications>
+          <swi:duplications>on</swi:duplications>
+          <swi:scanmessages>on</swi:scanmessages>
+        </swi:added>
+        <swi:removed>
+          <swi:failures>on</swi:failures>
+          <swi:modifications>off</swi:modifications>
+          <swi:duplications>on</swi:duplications>
+          <swi:scanmessages>on</swi:scanmessages>
+        </swi:removed>
+        <swi:modified>
+          <swi:failures>on</swi:failures>
+          <swi:modifications>off</swi:modifications>
+          <swi:duplications>on</swi:duplications>
+          <swi:scanmessages>on</swi:scanmessages>
+        </swi:modified>
+        <swi:cloned>
+          <swi:failures>on</swi:failures>
+          <swi:modifications>off</swi:modifications>
+          <swi:duplications>on</swi:duplications>
+          <swi:scanmessages>on</swi:scanmessages>
+        </swi:cloned>
+        <swi:unmodified>
+          <swi:failures>on</swi:failures>
+          <swi:modifications>off</swi:modifications>
+          <swi:duplications>on</swi:duplications>
+          <swi:scanmessages>on</swi:scanmessages>
+        </swi:unmodified>
+      </swi:print>
+    </swi:notifications>
+  </swi:report>
+  
+  <!--
+      Software Index tool is able to validate
+      that the particular statistic is in the acceptable range.
+      
+      Limits should be defined per:
+      STATISTIC-GROUP / STATISTIC-NAME / STATISTIC-TYPE
+      (See sections 'swi:indexer:common' and 'swi:indexer:dup' above)
+  -->
+  <swi:limits>
+    <!-- 
+        For example, if it is required to have proper (not too short)
+        comment header, it is necessary to limit the statistic:
+            'swi:lines/swi:comment:header/swi:exact'
+        It is demonstarted in the following section:
+    -->
+    <!-- STATISTIC-GROUP -->
+    <swi:lines>
+      <!-- STATISTIC-NAME -->
+      <swi:comment:header>
+        <!-- STATISTIC-TYPE -->
+        <swi:exact>
+          <!--
+              After that, actual limits should be defined for three levels:
+                  info, notice, warning
+                  
+              The common rule is that options should be in order:
+                  'swi:info' < 'swi:notice' < 'swi:warning'
+                OR:
+                  'swi:info' > 'swi:notice' > 'swi:warning'
+                  
+              Note: limits can be negative, in this case they do not have any sense.
+              
+              In the example below, no messages are printed for a function
+              if comment header before includes 5 lines at least.
+              If it is so, the 'regular' level is attached to this function.
+              If comment has 3-4 lines, level 'info' is assigned.
+              If comment has 1-2 lines, level 'notice' is assigned.
+              If ther� is no comments at all (0 lines), level 'warning' is assigned.
+          -->
+          <swi:info>5</swi:info>
+          <swi:notice>3</swi:notice>
+          <swi:warning>1</swi:warning>
+          
+          <!--
+              If some object is evaluated in non-regular level (info/notice/warning)
+              and you consider that it is too excessive to fix it
+              (or just there are no needs/benefits to do it),
+              it is possible to define the exception and suppress the notification
+              in the 'swi:suppress' section below.
+              
+              The exception should be defined in combination with 'swi:level' option.
+              If there is notification for the object:
+                  MY_PROJECT/MY_MODULE/my_file.c/myFunction
+              with 'notice' level, the 'swi:level' should be equal to 'notice'.
+              Otherwise, the notification will be still printed.
+              In other words, severity of the message and suppress level should be equal.
+              
+              Also, one rule can suppress several objects. An object is considered as
+              suppressed if it is matched by regular expression in 'swi:pattern' option.
+              
+              For example, the regexp pattern
+                  MY_PROJECT/MY_MODULE/my_file.c/.*
+              suppresses all functions in my_file.c in scope of
+              MY_MODULE module in MY_PROJECT project.
+              
+              However, it it NOT recommended to use multiple suppressing,
+              due to possible missmatch in severity of messages:
+              For example, if there are two functions:
+                  MY_PROJECT/MY_MODULE/my_file.c/myFunction1
+                  MY_PROJECT/MY_MODULE/my_file.c/myFunction2
+              and one of them is reported by the message with 'notice' level
+              but the second is evaluated in 'regular' level,
+              the 'swi:pattern' rule:
+                  MY_PROJECT/MY_MODULE/my_file.c/.*
+              will always result in the unsuppressable message,
+              either for the first or for the second function.
+              
+              It is better to suppress all objects by their full reference.
+              For example (see the previous example for prehistory)
+                  <swi:pattern swi:level="notice">^MY_PROJECT/MY_MODULE/my_file.c/myFunction1$</swi:pattern>
+              Symbols '^' and '$' are special regexp modifiers. They require immidiate borders in the name.
+          -->
+          <swi:suppress>
+            <!-- Section 'swi:pattern' can be repeated several times -->
+            <swi:pattern swi:level="notice">^MY_PROJECT/MY_MODULE/my_file.c/myFunction1$</swi:pattern>
+            <!--
+                Add here the next 'swi:pattern' section, for example:
+                <swi:pattern swi:level="warning">^.*/.*/file2.c/operator new[]$</swi:pattern>
+            -->
+          </swi:suppress>
+	  
+        </swi:exact>
+      </swi:comment:header>
+
+      <!-- STATISTIC-NAME -->
+      <swi:comment>
+        <!--
+            This is the next example of a limit for statistic.
+            It demonstrates how to limit the relative values.
+            For example, it is required to have at least 30% of comments per every function.
+            This is defined by the relation:
+            
+            'swi:lines/swi:comment/swi:exact' / 'swi:lines/swi:executable/swi:exact' * 100%.
+            
+            In this case it is necessary to limit 'swi:lines/swi:comment/swi:exact'
+            (what we do here) in the relation with 'swi:lines/swi:executable/swi:exact'.
+        -->
+        <swi:exact swi:relation="swi:lines/swi:executable/swi:exact">
+          <!-- 0.3  is 30% -->
+          <swi:info>0.3</swi:info>
+          <!-- 0.25 is 25% -->
+          <swi:notice>0.25</swi:notice>
+          <!-- 0.2  is 20% -->
+          <swi:warning>0.2</swi:warning>
+          
+          <!--
+              Add here suppress section, if it is needed. For example:
+              <swi:suppress>
+                <swi:pattern swi:level="notice">^MY_PROJECT/MY_MODULE/my_file.c/myFunction1$</swi:pattern>
+                <swi:pattern swi:level="info">^MY_PROJECT/MY_MODULE/my_file.c/myFunction2$</swi:pattern>
+              </swi:suppress>
+          -->
+        </swi:exact>
+      </swi:comment>
+      
+      <!-- STATISTIC-NAME -->
+      <swi:executable>
+        <!--
+            The previous examples require to limit the low bound of exact value.
+            This sample demonstrates the limitation for total upper value.
+            
+            For example, sometimes it is useful to require to have short files,
+            because they are easily maintainable in most cases.
+            The settings in this sections allows to do it:
+            no messages are printed for a file
+            if total number of executable lines less than or equal to 1000.
+        -->
+        <swi:total>
+          <swi:info>1000</swi:info>
+          <swi:notice>1500</swi:notice>
+          <swi:warning>2000</swi:warning>
+          
+          <!--
+              Most likely that you will need to suppress messages for modules,
+              because total value of lines in a module is a sum of lines in all files.
+          -->
+          <swi:suppress>
+            <!-- Section 'swi:pattern' can be repeated several times -->
+            <swi:pattern swi:level="warning">^MY_PROJECT/MY_MODULE$</swi:pattern>
+            <!--
+                Add here the next 'swi:pattern' section, for example:
+                <swi:pattern swi:level="warning">^MY_PROJECT/MY_MODULE$</swi:pattern>
+            -->
+          </swi:suppress>
+        </swi:total>
+      </swi:executable>
+      
+      <!-- STATISTIC-NAME -->
+      <swi:source>
+        <!--
+            This example is an extension for the previous.
+            Here the limitation is applied for total values of files, modules and project
+            at the same time.
+            
+            The settings in this sections allows to do it:
+            a) no messages (info) are printed for a file
+               if total number of source lines less than or equal to 1000.
+            b) no messages (notices) are printed for a module
+               if total number of source lines less than or equal to 10000.
+            c) no messages (notices) are printed for a project
+               if total number of source lines less than or equal to 100000.
+        -->
+        <swi:total>
+          <!-- Limit for files -->
+          <swi:info>1000</swi:info>
+          <!-- Limit for modules -->
+          <swi:notice>10000</swi:notice>
+          <!-- Limit for the project -->
+          <swi:warning>100000</swi:warning>
+          
+          <!--
+              Most likely that you will need to suppress info messages for modules and
+              notice messages for the project,
+              because total value of lines in a module is a sum of lines in all files,
+              and total value of lines in project is a sum of lines in all modules.
+          -->
+          <swi:suppress>
+            <!-- Section 'swi:pattern' can be repeated several times -->
+            <swi:pattern swi:level="info">^MY_PROJECT/MY_MODULE$</swi:pattern>
+            <swi:pattern swi:level="notice">^MY_PROJECT$</swi:pattern>
+            <!--
+                Add here the next 'swi:pattern' section, for example:
+                <swi:pattern swi:level="info">^MY_PROJECT/MY_MODULE$</swi:pattern>
+            -->
+          </swi:suppress>
+        </swi:total>
+      </swi:source>
+
+    </swi:lines>
+
+    <!-- STATISTIC-GROUP -->
+    <swi:complexity>
+      <!-- STATISTIC-NAME -->
+      <swi:cyclomatic>
+        <!-- STATISTIC-TYPE -->
+        <swi:exact>
+          <!--
+              In the example below, no messages are printed for a function
+              if cyclomatic complexity index less than or equal to 7.
+              It indicates about proper (low) level of logical
+              branching in a subroutine. Low level of this index
+              more or less grants decreased costs for mainteinability
+              and further development, minimal probability of bad fixes,
+              better and easier understanding of a logical part of SW.
+          -->
+          <swi:info>7</swi:info>
+          <swi:notice>10</swi:notice>
+          <swi:warning>15</swi:warning>
+          <!--
+              Add here suppress section, if it is needed. For example:
+              <swi:suppress>
+                <swi:pattern swi:level="notice">^MY_PROJECT/MY_MODULE/my_file.c/myFunction1$</swi:pattern>
+                <swi:pattern swi:level="info">^MY_PROJECT/MY_MODULE/my_file.c/myFunction2$</swi:pattern>
+              </swi:suppress>
+          -->
+        </swi:exact>
+      </swi:cyclomatic>
+    </swi:complexity>
+    
+    <!--
+        The following section defines the limitation for duplication index.
+        In the example, it is required to control exact relative
+        duplication index for functions and total relative
+        duplication index for files, modules and project.
+    -->
+    <!-- STATISTIC-GROUP -->
+    <swi:duplication>
+      <!-- STATISTIC-NAME -->
+      <swi:symbols>
+        <!-- STATISTIC-TYPE -->
+        <swi:exact swi:relation="swi:length/swi:executable/swi:exact">
+          <swi:info>0.30</swi:info>
+          <swi:notice>0.40</swi:notice>
+          <swi:warning>0.60</swi:warning>
+          <!--
+              Add here suppress section, if it is needed. For example:
+              <swi:suppress>
+                <swi:pattern swi:level="notice">^MY_PROJECT/MY_MODULE/my_file.c/myFunction1$</swi:pattern>
+                <swi:pattern swi:level="info">^MY_PROJECT/MY_MODULE/my_file.c/myFunction2$</swi:pattern>
+              </swi:suppress>
+          -->
+        </swi:exact>
+        
+        <!-- STATISTIC-TYPE -->
+        <swi:total swi:relation="swi:length/swi:executable/swi:total">
+          <swi:info>0.30</swi:info>
+          <swi:notice>0.40</swi:notice>
+          <swi:warning>0.60</swi:warning>
+          <!--
+              Add here suppress section, if it is needed. For example:
+              <swi:suppress>
+                <swi:pattern swi:level="warning">^MY_PROJECT/MY_MODULE/my_file.c$</swi:pattern>
+                <swi:pattern swi:level="notice">^MY_PROJECT/MY_MODULE$</swi:pattern>
+                <swi:pattern swi:level="info">^MY_PROJECT$</swi:pattern>
+              </swi:suppress>
+          -->
+        </swi:total>
+      </swi:symbols>
+    </swi:duplication>
+    
+    <!--
+        Add here more sections with definition of limits if it is needed.
+    -->
+    
+  </swi:limits>
+
+</swi:configuration>

+ 30 - 0
swi_main.pl

@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+#
+#    Software Index, Copyright 2010, Software Index Project Team
+#    Link: http://swi.sourceforge.net
+#
+#    This file is part of Software Index Tool.
+#
+#    Software Index is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#
+#    Software Index is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with Software Index.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+use strict;
+use Cwd qw(abs_path);
+
+$0 =~ m/(.*)swi_main.pl$/;
+my $globalRootDirectory      = abs_path($1);
+
+push(@INC, "$globalRootDirectory/lib");
+require SWI::Launcher;
+
+exit swiLaunch($globalRootDirectory, @ARGV);

BIN
test/Metrics.xlsx


+ 181 - 0
test/swi_config_ddd.xml

@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="utf-8"?>
+<swi:configuration>
+  
+  <swi:info>
+    <swi:version>1</swi:version>
+    <swi:project>
+      <swi:name>DDD</swi:name>
+    </swi:project>
+    <swi:history>
+      <swi:revision>
+        <swi:name>ALFA</swi:name>
+        <swi:user>USER</swi:user>
+        <swi:comment>Configuration for DDD project</swi:comment>
+      </swi:revision>
+    </swi:history>
+  </swi:info>
+      
+  <swi:modules>
+    <swi:module>
+      <swi:name>DDD-SOURCES</swi:name>
+      <swi:location>../GNU-SRC/ddd/ddd</swi:location>
+      <swi:files>
+        <swi:include>^.*[chCH]$</swi:include>
+        
+        <!--
+          Excluding the following files:
+          FILE:             - REASON:
+          
+          ddd.info.txt.gz.C - just too big file
+          vsl-lex.C         - brackets mismatch without preprocessor work
+          rxscan.C          - undefined block start without preprocessor work
+          vsl-gramma.C      - definition of local variables in old style
+        -->
+        <swi:exclude>ddd.info.txt.gz.C|vsl-lex.C|rxscan.C|vsl-gramma.C</swi:exclude>
+      </swi:files>
+      
+      <swi:preprocessor>
+        <swi:rule>
+          <swi:filepattern>^PrintGC.h$</swi:filepattern>
+          <swi:searchpattern>([^_a-zA-Z0-9])(DECLARE_TYPE_INFO)([^_a-zA-Z0-9])</swi:searchpattern>
+          <swi:replacepattern>$1$2;$3</swi:replacepattern>
+        </swi:rule>
+        <swi:rule>
+          <swi:filepattern>.*</swi:filepattern>
+          <swi:searchpattern>(\s+)(union\s*)({)</swi:searchpattern>
+          <swi:replacepattern>$1$2 _noname_ $3</swi:replacepattern>
+        </swi:rule>
+      </swi:preprocessor>
+      
+      <swi:scanner>
+      	<!--
+      	    The codecontent option defines the content for scanner, it can be:
+      	    
+      	    initial         - the initial source content
+      	    code            - the initial code (without comments)
+      	    comments        - comments only (no code)
+      	    nopreprocessor  - preprocessor strings excluded (without comments and preprocessor)
+      	    nostrings       - strings excluded (without comments and strings)
+      	    purified        - strings and preprocessor excluded (without comments, strings and preprocessor)
+      	    
+      	    By default, the 'purified' content is scanned
+      	-->
+        <swi:rule>
+          <swi:filepattern>.*</swi:filepattern>
+          <swi:searchpattern>(\s+)((union)|(enum)|(struct))(\s*{)</swi:searchpattern>
+          <swi:messagepattern>Noname '$2' detected.</swi:messagepattern>
+          <swi:codecontent>purified</swi:codecontent>
+        </swi:rule>
+      </swi:scanner>
+      
+      <swi:indexer:common>
+        <!-- No settings currently available -->
+      </swi:indexer:common>
+      
+      <swi:indexer:dup>
+      	<!--
+      	    The codecontent option defines the content for scanner, it can be:
+      	    
+      	    initial         - the initial source content
+      	    code            - the initial code (without comments)
+      	    comments        - comments only (no code)
+      	    nopreprocessor  - preprocessor strings excluded (without comments and preprocessor)
+      	    nostrings       - strings excluded (without comments and strings)
+      	    purified        - strings and preprocessor excluded (without comments, strings and preprocessor)
+      	    
+      	    By default, the 'purified' content is scanned
+      	-->
+      	<swi:codecontent>purified</swi:codecontent>
+        <swi:enabled>on</swi:enabled>
+        <swi:minlength>100</swi:minlength>
+        <swi:proximity>100</swi:proximity>
+        <swi:globalcode>on</swi:globalcode>
+      </swi:indexer:dup>
+
+    </swi:module>
+ 
+  </swi:modules>
+  
+  <swi:report>
+    <swi:destination>.</swi:destination>
+    <swi:xml>
+      <swi:name>swi_report_ddd.xml</swi:name>
+    </swi:xml>
+    <swi:notifications>
+      <swi:name>swi_report_ddd.log</swi:name>
+      <swi:error>
+        <swi:added>on</swi:added>
+        <swi:removed>on</swi:removed>
+        <swi:modified>on</swi:modified>
+        <swi:cloned>on</swi:cloned>
+        <swi:unmodified>on</swi:unmodified>
+      </swi:error>
+      <swi:print>
+        <swi:added>
+          <swi:failures>on</swi:failures>
+          <swi:modifications>off</swi:modifications>
+          <swi:duplications>on</swi:duplications>
+          <swi:scanmessages>on</swi:scanmessages>
+        </swi:added>
+        <swi:removed>
+          <swi:failures>on</swi:failures>
+          <swi:modifications>off</swi:modifications>
+          <swi:duplications>on</swi:duplications>
+          <swi:scanmessages>on</swi:scanmessages>
+        </swi:removed>
+        <swi:modified>
+          <swi:failures>on</swi:failures>
+          <swi:modifications>off</swi:modifications>
+          <swi:duplications>on</swi:duplications>
+          <swi:scanmessages>on</swi:scanmessages>
+        </swi:modified>
+        <swi:cloned>
+          <swi:failures>on</swi:failures>
+          <swi:modifications>off</swi:modifications>
+          <swi:duplications>on</swi:duplications>
+          <swi:scanmessages>on</swi:scanmessages>
+        </swi:cloned>
+        <swi:unmodified>
+          <swi:failures>on</swi:failures>
+          <swi:modifications>off</swi:modifications>
+          <swi:duplications>on</swi:duplications>
+          <swi:scanmessages>on</swi:scanmessages>
+        </swi:unmodified>
+      </swi:print>
+    </swi:notifications>
+  </swi:report>
+  
+  <swi:limits>
+    <swi:lines>
+      <swi:comment:header>
+        <swi:exact>
+          <swi:info>5</swi:info>
+          <swi:notice>3</swi:notice>
+          <swi:warning>1</swi:warning>
+	  
+          <swi:suppress>
+              <swi:pattern swi:level="warning">.*</swi:pattern>
+          </swi:suppress>
+	  
+        </swi:exact>
+      </swi:comment:header>
+    </swi:lines>
+
+    <swi:duplication>
+      <swi:symbols>
+        <swi:exact swi:relation="swi:length/swi:executable/swi:exact">
+          <swi:info>0.28</swi:info>
+          <swi:notice>0.40</swi:notice>
+          <swi:warning>0.60</swi:warning>
+          <!--
+          <swi:suppress>
+              <swi:pattern level="warning">.*</swi:pattern>
+          </swi:suppress>
+          -->
+        </swi:exact>
+      </swi:symbols>
+    </swi:duplication>
+    
+  </swi:limits>
+
+</swi:configuration>

+ 108 - 0
test/swi_config_test.xml

@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8"?>
+<swi:configuration>
+  
+  <swi:info>
+    <swi:version>1</swi:version>
+    <swi:project>
+      <swi:name>DDD</swi:name>
+    </swi:project>
+    <swi:history>
+      <swi:revision>
+        <swi:name>ALFA</swi:name>
+        <swi:user>avkonst</swi:user>
+        <swi:comment>Configuration for TEST project</swi:comment>
+      </swi:revision>
+    </swi:history>
+  </swi:info>
+      
+  <swi:modules>
+    <swi:module>
+      <swi:name>TEST_FOLDER</swi:name>
+      <swi:location>test</swi:location>
+      <swi:files>
+        <swi:include>.*[chCH]</swi:include>
+        <swi:exclude></swi:exclude>
+      </swi:files>
+      
+      <swi:dupfinder>
+        <swi:enabled>on</swi:enabled>
+      </swi:dupfinder>
+      
+    </swi:module>
+ 
+  </swi:modules>
+  
+  <swi:report>
+    <swi:destination>.</swi:destination>
+    <swi:xml>
+      <swi:name>swi_report_test.xml</swi:name>
+    </swi:xml>
+    <swi:notifications>
+      <swi:name>swi_report_test.log</swi:name>
+      <swi:stdout>on</swi:stdout>
+      <swi:error>
+        <swi:added>on</swi:added>
+        <swi:removed>on</swi:removed>
+        <swi:modified>on</swi:modified>
+        <swi:cloned>on</swi:cloned>
+        <swi:unmodified>on</swi:unmodified>
+      </swi:error>
+      <swi:print>
+        <swi:added>
+          <swi:failures>on</swi:failures>
+          <swi:modifications>off</swi:modifications>
+        </swi:added>
+        <swi:removed>
+          <swi:failures>on</swi:failures>
+          <swi:modifications>off</swi:modifications>
+        </swi:removed>
+        <swi:modified>
+          <swi:failures>on</swi:failures>
+          <swi:modifications>off</swi:modifications>
+        </swi:modified>
+        <swi:cloned>
+          <swi:failures>on</swi:failures>
+          <swi:modifications>off</swi:modifications>
+        </swi:cloned>
+        <swi:unmodified>
+          <swi:failures>on</swi:failures>
+          <swi:modifications>off</swi:modifications>
+        </swi:unmodified>
+      </swi:print>
+    </swi:notifications>
+    <swi:dup>
+      <swi:name>swi_report_ddd.dup</swi:name>
+    </swi:dup>
+  </swi:report>
+  
+  <swi:limits>
+    <swi:lines>
+    
+      <swi:comment:header>
+        <swi:exact>
+          <swi:info>5</swi:info>
+          <swi:notice>3</swi:notice>
+          <swi:warning>1</swi:warning>
+	  
+          <swi:suppress>
+              <swi:pattern swi:level="info">.*</swi:pattern>
+          </swi:suppress>
+	  
+        </swi:exact>
+      </swi:comment:header>
+    </swi:lines>
+
+
+    <swi:duplication>
+      <swi:symbols>
+        <swi:exact swi:relation="swi:length/swi:executable/swi:exact">
+          <swi:info>0.28</swi:info>
+          <swi:notice>0.40</swi:notice>
+          <swi:warning>0.60</swi:warning>
+        </swi:exact>
+      </swi:symbols>
+    </swi:duplication>
+
+  </swi:limits>
+
+</swi:configuration>