From 511c43aec78279ecaa6008bd7f4f08159d3a5c00 Mon Sep 17 00:00:00 2001
From: Tobias Markmann <tm@ayena.de>
Date: Mon, 18 Apr 2016 18:46:39 +0200
Subject: Update 3rdParty lcov to version 1.12

The old version caused errors when running
BuildTools/Coverage/GenerateCoverageResults.sh.

Test-Information:

Successfully ran BuildTools/Coverage/GenerateCoverageResults.sh
on OS X 10.11.4.

Change-Id: If6f53a85e8051388c6d5b17d980f0c3fa8e65b6e

diff --git a/3rdParty/LCov/gendesc b/3rdParty/LCov/gendesc
index 522ef69..7287c83 100755
--- a/3rdParty/LCov/gendesc
+++ b/3rdParty/LCov/gendesc
@@ -38,10 +38,12 @@
 use strict;
 use File::Basename; 
 use Getopt::Long;
+use Cwd qw/abs_path/;
 
 
 # Constants
-our $lcov_version	= 'LCOV version 1.9';
+our $tool_dir		= abs_path(dirname($0));
+our $lcov_version	= "LCOV version 1.12";
 our $lcov_url		= "http://ltp.sourceforge.net/coverage/lcov.php";
 our $tool_name		= basename($0);
 
@@ -67,9 +69,6 @@ our $input_filename;
 $SIG{__WARN__} = \&warn_handler;
 $SIG{__DIE__} = \&die_handler;
 
-# Prettify version string
-$lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
-
 # Parse command line options
 if (!GetOptions("output-filename=s" => \$output_filename,
 		"version" =>\$version,
@@ -153,13 +152,13 @@ sub gen_desc()
 	local *OUTPUT_HANDLE;
 	my $empty_line = "ignore";
 
-	open(INPUT_HANDLE, $input_filename)
+	open(INPUT_HANDLE, "<", $input_filename)
 		or die("ERROR: cannot open $input_filename!\n");
 
 	# Open output file for writing
 	if ($output_filename)
 	{
-		open(OUTPUT_HANDLE, ">$output_filename")
+		open(OUTPUT_HANDLE, ">", $output_filename)
 			or die("ERROR: cannot create $output_filename!\n");
 	}
 	else
diff --git a/3rdParty/LCov/genhtml b/3rdParty/LCov/genhtml
index d74063a..cf1b7f5 100755
--- a/3rdParty/LCov/genhtml
+++ b/3rdParty/LCov/genhtml
@@ -1,6 +1,6 @@
 #!/usr/bin/perl -w
 #
-#   Copyright (c) International Business Machines  Corp., 2002,2010
+#   Copyright (c) International Business Machines  Corp., 2002,2012
 #
 #   This program is free software;  you can redistribute it and/or modify
 #   it under the terms of the GNU General Public License as published by
@@ -65,17 +65,23 @@
 #
 
 use strict;
-use File::Basename; 
+use File::Basename;
+use File::Temp qw(tempfile);
 use Getopt::Long;
 use Digest::MD5 qw(md5_base64);
+use Cwd qw/abs_path/;
 
 
 # Global constants
 our $title		= "LCOV - code coverage report";
-our $lcov_version	= 'LCOV version 1.9';
+our $tool_dir		= abs_path(dirname($0));
+our $lcov_version	= "LCOV version 1.12";
 our $lcov_url		= "http://ltp.sourceforge.net/coverage/lcov.php";
 our $tool_name		= basename($0);
 
+# Specify coverage rate default precision
+our $default_precision = 1;
+
 # Specify coverage rate limits (in %) for classifying file entries
 # HI:   $hi_limit <= rate <= 100          graph color: green
 # MED: $med_limit <= rate <  $hi_limit    graph color: orange
@@ -145,6 +151,7 @@ our $BR_BRANCH		= 1;
 our $BR_TAKEN		= 2;
 our $BR_VEC_ENTRIES	= 3;
 our $BR_VEC_WIDTH	= 32;
+our $BR_VEC_MAX		= vec(pack('b*', 1 x $BR_VEC_WIDTH), 0, $BR_VEC_WIDTH);
 
 # Additional offsets used when converting branch coverage data to HTML
 our $BR_LEN	= 3;
@@ -155,6 +162,12 @@ our $BR_CLOSE	= 5;
 our $BR_SUB = 0;
 our $BR_ADD = 1;
 
+# Error classes which users may specify to ignore during processing
+our $ERROR_SOURCE	= 0;
+our %ERROR_ID = (
+	"source" => $ERROR_SOURCE,
+);
+
 # Data related prototypes
 sub print_usage(*);
 sub gen_html();
@@ -165,7 +178,7 @@ sub info(@);
 sub read_info_file($);
 sub get_info_entry($);
 sub set_info_entry($$$$$$$$$;$$$$$$);
-sub get_prefix(@);
+sub get_prefix($@);
 sub shorten_prefix($);
 sub get_dir_list(@);
 sub get_relative_base_path($);
@@ -181,7 +194,7 @@ sub get_affecting_tests($$$);
 sub combine_info_files($$);
 sub merge_checksums($$$);
 sub combine_info_entries($$$);
-sub apply_prefix($$);
+sub apply_prefix($@);
 sub system_no_output($@);
 sub read_config($);
 sub apply_config($);
@@ -198,6 +211,9 @@ sub combine_brcount($$$);
 sub get_br_found_and_hit($);
 sub warn_handler($);
 sub die_handler($);
+sub parse_ignore_errors(@);
+sub parse_dir_prefix(@);
+sub rate($$;$$$);
 
 
 # HTML related prototypes
@@ -244,7 +260,8 @@ sub gen_png($$$@);
 
 # Global variables & initialization
 our %info_data;		# Hash containing all data from .info file
-our $dir_prefix;	# Prefix to remove from all sub directories
+our @opt_dir_prefix;	# Array of prefixes to remove from all sub directories
+our @dir_prefix;
 our %test_description;	# Hash containing test descriptions if available
 our $date = get_date_string();
 
@@ -259,9 +276,9 @@ our $help;		# Help option flag
 our $version;		# Version option flag
 our $show_details;	# If set, generate detailed directory view
 our $no_prefix;		# If set, do not remove filename prefix
-our $func_coverage = 1;	# If set, generate function coverage statistics
+our $func_coverage;	# If set, generate function coverage statistics
 our $no_func_coverage;	# Disable func_coverage
-our $br_coverage = 1;	# If set, generate branch coverage statistics
+our $br_coverage;	# If set, generate branch coverage statistics
 our $no_br_coverage;	# Disable br_coverage
 our $sort = 1;		# If set, provide directory listings with sorted entries
 our $no_sort;		# Disable sort
@@ -279,15 +296,22 @@ our $html_epilog;	# Actual HTML epilog
 our $html_ext = "html";	# Extension for generated HTML files
 our $html_gzip = 0;	# Compress with gzip
 our $demangle_cpp = 0;	# Demangle C++ function names
+our @opt_ignore_errors;	# Ignore certain error classes during processing
+our @ignore;
+our $opt_config_file;	# User-specified configuration file location
+our %opt_rc;
+our $charset = "UTF-8";	# Default charset for HTML pages
 our @fileview_sortlist;
 our @fileview_sortname = ("", "-sort-l", "-sort-f", "-sort-b");
 our @funcview_sortlist;
 our @rate_name = ("Lo", "Med", "Hi");
 our @rate_png = ("ruby.png", "amber.png", "emerald.png");
+our $lcov_func_coverage = 1;
+our $lcov_branch_coverage = 0;
+our $rc_desc_html = 0;	# lcovrc: genhtml_desc_html
 
 our $cwd = `pwd`;	# Current working directory
 chomp($cwd);
-our $tool_dir = dirname($0);	# Directory where genhtml tool is installed
 
 
 #
@@ -297,17 +321,29 @@ our $tool_dir = dirname($0);	# Directory where genhtml tool is installed
 $SIG{__WARN__} = \&warn_handler;
 $SIG{__DIE__} = \&die_handler;
 
-# Prettify version string
-$lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
+# Check command line for a configuration file name
+Getopt::Long::Configure("pass_through", "no_auto_abbrev");
+GetOptions("config-file=s" => \$opt_config_file,
+	   "rc=s%" => \%opt_rc);
+Getopt::Long::Configure("default");
 
-# Add current working directory if $tool_dir is not already an absolute path
-if (! ($tool_dir =~ /^\/(.*)$/))
 {
-	$tool_dir = "$cwd/$tool_dir";
+	# Remove spaces around rc options
+	my %new_opt_rc;
+
+	while (my ($key, $value) = each(%opt_rc)) {
+		$key =~ s/^\s+|\s+$//g;
+		$value =~ s/^\s+|\s+$//g;
+
+		$new_opt_rc{$key} = $value;
+	}
+	%opt_rc = %new_opt_rc;
 }
 
 # Read configuration file if available
-if (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
+if (defined($opt_config_file)) {
+	$config = read_config($opt_config_file);
+} elsif (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
 {
 	$config = read_config($ENV{"HOME"}."/.lcovrc");
 }
@@ -316,9 +352,9 @@ elsif (-r "/etc/lcovrc")
 	$config = read_config("/etc/lcovrc");
 }
 
-if ($config)
+if ($config || %opt_rc)
 {
-	# Copy configuration file values to variables
+	# Copy configuration file and --rc values to variables
 	apply_config({
 		"genhtml_css_file"		=> \$css_filename,
 		"genhtml_hi_limit"		=> \$hi_limit,
@@ -337,6 +373,7 @@ if ($config)
 		"genhtml_html_epilog"		=> \$html_epilog_file,
 		"genhtml_html_extension"	=> \$html_ext,
 		"genhtml_html_gzip"		=> \$html_gzip,
+		"genhtml_precision"		=> \$default_precision,
 		"genhtml_function_hi_limit"	=> \$fn_hi_limit,
 		"genhtml_function_med_limit"	=> \$fn_med_limit,
 		"genhtml_function_coverage"	=> \$func_coverage,
@@ -345,14 +382,20 @@ if ($config)
 		"genhtml_branch_coverage"	=> \$br_coverage,
 		"genhtml_branch_field_width"	=> \$br_field_width,
 		"genhtml_sort"			=> \$sort,
+		"genhtml_charset"		=> \$charset,
+		"genhtml_desc_html"		=> \$rc_desc_html,
+		"lcov_function_coverage"	=> \$lcov_func_coverage,
+		"lcov_branch_coverage"		=> \$lcov_branch_coverage,
 		});
 }
 
-# Copy limit values if not specified
+# Copy related values if not specified
 $fn_hi_limit	= $hi_limit if (!defined($fn_hi_limit));
 $fn_med_limit	= $med_limit if (!defined($fn_med_limit));
 $br_hi_limit	= $hi_limit if (!defined($br_hi_limit));
 $br_med_limit	= $med_limit if (!defined($br_med_limit));
+$func_coverage	= $lcov_func_coverage if (!defined($func_coverage));
+$br_coverage	= $lcov_branch_coverage if (!defined($br_coverage));
 
 # Parse command line options
 if (!GetOptions("output-directory|o=s"	=> \$output_directory,
@@ -361,7 +404,7 @@ if (!GetOptions("output-directory|o=s"	=> \$output_directory,
 		"keep-descriptions|k"	=> \$keep_descriptions,
 		"css-file|c=s"		=> \$css_filename,
 		"baseline-file|b=s"	=> \$base_filename,
-		"prefix|p=s"		=> \$dir_prefix,
+		"prefix|p=s"		=> \@opt_dir_prefix,
 		"num-spaces=i"		=> \$tab_size,
 		"no-prefix"		=> \$no_prefix,
 		"no-sourceview"		=> \$no_sourceview,
@@ -383,6 +426,10 @@ if (!GetOptions("output-directory|o=s"	=> \$output_directory,
 		"sort"			=> \$sort,
 		"no-sort"		=> \$no_sort,
 		"demangle-cpp"		=> \$demangle_cpp,
+		"ignore-errors=s"	=> \@opt_ignore_errors,
+		"config-file=s"		=> \$opt_config_file,
+		"rc=s%"			=> \%opt_rc,
+		"precision=i"		=> \$default_precision,
 		))
 {
 	print(STDERR "Use $tool_name --help to get usage information\n");
@@ -418,6 +465,12 @@ if ($version)
 	exit(0);
 }
 
+# Determine which errors the user wants us to ignore
+parse_ignore_errors(@opt_ignore_errors);
+
+# Split the list of prefixes if needed
+parse_dir_prefix(@opt_dir_prefix);
+
 # Check for info filename
 if (!@info_filenames)
 {
@@ -471,11 +524,11 @@ if ($no_sourceview && defined($frames))
 }
 
 # Issue a warning if --no-prefix is enabled together with --prefix
-if ($no_prefix && defined($dir_prefix))
+if ($no_prefix && @dir_prefix)
 {
 	warn("WARNING: option --prefix disabled because --no-prefix was ".
 	     "specified!\n");
-	$dir_prefix = undef;
+	@dir_prefix = undef;
 }
 
 @fileview_sortlist = ($SORT_FILE);
@@ -503,6 +556,13 @@ if ($demangle_cpp)
 	}
 }
 
+# Make sure precision is within valid range
+if ($default_precision < 1 || $default_precision > 4)
+{
+	die("ERROR: specified precision is out of range (1 to 4)\n");
+}
+
+
 # Make sure output_directory exists, create it if necessary
 if ($output_directory)
 {
@@ -541,6 +601,9 @@ Misc:
   -h, --help                        Print this help, then exit
   -v, --version                     Print version number, then exit
   -q, --quiet                       Do not print progress messages
+      --config-file FILENAME        Specify configuration file location
+      --rc SETTING=VALUE            Override configuration file setting
+      --ignore-errors ERRORS        Continue after ERRORS (source)
 
 Operation:
   -o, --output-directory OUTDIR     Write HTML output to OUTDIR
@@ -567,6 +630,7 @@ HTML output:
       --html-gzip                   Use gzip to compress HTML
       --(no-)sort                   Enable (disable) sorted coverage views
       --demangle-cpp                Demangle C++ function names
+      --precision NUM               Set precision of coverage rate
 
 For more information see: $lcov_url
 END_OF_USAGE
@@ -607,8 +671,7 @@ sub get_overall_line($$$$)
 
 	return "no data found" if (!defined($found) || $found == 0);
 	$name = ($found == 1) ? $name_sn : $name_pl;
-	return sprintf("%.1f%% (%d of %d %s)", $hit * 100 / $found, $hit,
-		       $found, $name);
+	return rate($hit, $found, "% ($hit of $found $name)");
 }
 
 
@@ -636,6 +699,116 @@ sub print_overall_rate($$$$$$$$$)
 		if ($br_do);
 }
 
+sub get_fn_list($)
+{
+	my ($info) = @_;
+	my %fns;
+	my @result;
+
+	foreach my $filename (keys(%{$info})) {
+		my $data = $info->{$filename};
+		my $funcdata = $data->{"func"};
+		my $sumfnccount = $data->{"sumfnc"};
+
+		if (defined($funcdata)) {
+			foreach my $func_name (keys(%{$funcdata})) {
+				$fns{$func_name} = 1;
+			}
+		}
+
+		if (defined($sumfnccount)) {
+			foreach my $func_name (keys(%{$sumfnccount})) {
+				$fns{$func_name} = 1;
+			}
+		}
+	}
+
+	@result = keys(%fns);
+
+	return \@result;
+}
+
+#
+# rename_functions(info, conv)
+#
+# Rename all function names in INFO according to CONV: OLD_NAME -> NEW_NAME.
+# In case two functions demangle to the same name, assume that they are
+# different object code implementations for the same source function.
+#
+
+sub rename_functions($$)
+{
+	my ($info, $conv) = @_;
+
+	foreach my $filename (keys(%{$info})) {
+		my $data = $info->{$filename};
+		my $funcdata;
+		my $testfncdata;
+		my $sumfnccount;
+		my %newfuncdata;
+		my %newsumfnccount;
+		my $f_found;
+		my $f_hit;
+
+		# funcdata: function name -> line number
+		$funcdata = $data->{"func"};
+		foreach my $fn (keys(%{$funcdata})) {
+			my $cn = $conv->{$fn};
+
+			# Abort if two functions on different lines map to the
+			# same demangled name.
+			if (defined($newfuncdata{$cn}) &&
+			    $newfuncdata{$cn} != $funcdata->{$fn}) {
+				die("ERROR: Demangled function name $cn ".
+				    "maps to different lines (".
+				    $newfuncdata{$cn}." vs ".
+				    $funcdata->{$fn}.") in $filename\n");
+			}
+			$newfuncdata{$cn} = $funcdata->{$fn};
+		}
+		$data->{"func"} = \%newfuncdata;
+
+		# testfncdata: test name -> testfnccount
+		# testfnccount: function name -> execution count
+		$testfncdata = $data->{"testfnc"};
+		foreach my $tn (keys(%{$testfncdata})) {
+			my $testfnccount = $testfncdata->{$tn};
+			my %newtestfnccount;
+
+			foreach my $fn (keys(%{$testfnccount})) {
+				my $cn = $conv->{$fn};
+
+				# Add counts for different functions that map
+				# to the same name.
+				$newtestfnccount{$cn} +=
+					$testfnccount->{$fn};
+			}
+			$testfncdata->{$tn} = \%newtestfnccount;
+		}
+
+		# sumfnccount: function name -> execution count
+		$sumfnccount = $data->{"sumfnc"};
+		foreach my $fn (keys(%{$sumfnccount})) {
+			my $cn = $conv->{$fn};
+
+			# Add counts for different functions that map
+			# to the same name.
+			$newsumfnccount{$cn} += $sumfnccount->{$fn};
+		}
+		$data->{"sumfnc"} = \%newsumfnccount;
+
+		# Update function found and hit counts since they may have
+		# changed
+		$f_found = 0;
+		$f_hit = 0;
+		foreach my $fn (keys(%newsumfnccount)) {
+			$f_found++;
+			$f_hit++ if ($newsumfnccount{$fn} > 0);
+		}
+		$data->{"f_found"} = $f_found;
+		$data->{"f_hit"} = $f_hit;
+	}
+}
 
 #
 # gen_html()
@@ -701,14 +874,16 @@ sub gen_html()
 		# User requested that we leave filenames alone
 		info("User asked not to remove filename prefix\n");
 	}
-	elsif (!defined($dir_prefix))
+	elsif (! @dir_prefix)
 	{
 		# Get prefix common to most directories in list
-		$dir_prefix = get_prefix(@dir_list);
+		my $prefix = get_prefix(1, keys(%info_data));
 
-		if ($dir_prefix)
+		if ($prefix)
 		{
-			info("Found common filename prefix \"$dir_prefix\"\n");
+			info("Found common filename prefix \"$prefix\"\n");
+			$dir_prefix[0] = $prefix;
+
 		}
 		else
 		{
@@ -718,10 +893,17 @@ sub gen_html()
 	}
 	else
 	{
-		info("Using user-specified filename prefix \"".
-		     "$dir_prefix\"\n");
+		my $msg = "Using user-specified filename prefix ";
+		for my $i (0 .. $#dir_prefix)
+		{
+				$dir_prefix[$i] =~ s/\/+$//;
+				$msg .= ", " unless 0 == $i;
+				$msg .= "\"" . $dir_prefix[$i] . "\"";
+		}
+		info($msg . "\n");
 	}
 
+
 	# Read in test description file if specified
 	if ($desc_filename)
 	{
@@ -763,11 +945,14 @@ sub gen_html()
 		 $br_found, $br_hit)
 			= process_dir($dir_name);
 
+		# Handle files in root directory gracefully
+		$dir_name = "root" if ($dir_name eq "");
+
 		# Remove prefix if applicable
-		if (!$no_prefix && $dir_prefix)
+		if (!$no_prefix && @dir_prefix)
 		{
-			# Match directory names beginning with $dir_prefix
-			$dir_name = apply_prefix($dir_name, $dir_prefix);
+			# Match directory names beginning with one of @dir_prefix
+			$dir_name = apply_prefix($dir_name,@dir_prefix);
 		}
 
 		# Generate name for directory overview HTML page
@@ -832,13 +1017,13 @@ sub html_create($$)
 
 	if ($html_gzip)
 	{
-		open($handle, "|gzip -c >$filename")
+		open($handle, "|-", "gzip -c >'$filename'")
 			or die("ERROR: cannot open $filename for writing ".
 			       "(gzip)!\n");
 	}
 	else
 	{
-		open($handle, ">$filename")
+		open($handle, ">", $filename)
 			or die("ERROR: cannot open $filename for writing!\n");
 	}
 }
@@ -855,6 +1040,7 @@ sub write_dir_page($$$$$$$$$$$$$$$$$)
 	if (!defined($trunc_dir)) {
 		$trunc_dir = "";
 	}
+	$title .= " - " if ($trunc_dir ne "");
 	write_html_prolog(*HTML_HANDLE, $base_dir, "LCOV - $title$trunc_dir");
 	write_header(*HTML_HANDLE, $view_type, $trunc_dir, $rel_dir,
 		     $overall_found, $overall_hit, $total_fn_found,
@@ -904,8 +1090,8 @@ sub process_dir($)
 	# Remove prefix if applicable
 	if (!$no_prefix)
 	{
-		# Match directory name beginning with $dir_prefix
-		$rel_dir = apply_prefix($rel_dir, $dir_prefix);
+		# Match directory name beginning with one of @dir_prefix
+		$rel_dir = apply_prefix($rel_dir,@dir_prefix);
 	}
 
 	$trunc_dir = $rel_dir;
@@ -916,6 +1102,10 @@ sub process_dir($)
 		$rel_dir = substr($rel_dir, 1);
 	}
 
+	# Handle files in root directory gracefully
+	$rel_dir = "root" if ($rel_dir eq "");
+	$trunc_dir = "root" if ($trunc_dir eq "");
+
 	$base_dir = get_relative_base_path($rel_dir);
 
 	create_sub_dir($rel_dir);
@@ -1083,7 +1273,7 @@ sub write_function_page($$$$$$$$$$$$$$$$$$)
 
 sub process_file($$$)
 {
-	info("Processing file ".apply_prefix($_[2], $dir_prefix)."\n");
+	info("Processing file ".apply_prefix($_[2], @dir_prefix)."\n");
 
 	my $trunc_dir = $_[0];
 	my $rel_dir = $_[1];
@@ -1195,6 +1385,10 @@ sub process_file($$$)
 #        "func"  -> \%funcdata
 #        "found" -> $lines_found (number of instrumented lines found in file)
 #	 "hit"   -> $lines_hit (number of executed lines in file)
+#        "f_found" -> $fn_found (number of instrumented functions found in file)
+#	 "f_hit"   -> $fn_hit (number of executed functions in file)
+#        "b_found" -> $br_found (number of instrumented branches found in file)
+#	 "b_hit"   -> $br_hit (number of executed branches in file)
 #        "check" -> \%checkdata
 #        "testfnc" -> \%testfncdata
 #        "sumfnc"  -> \%sumfnccount
@@ -1251,6 +1445,7 @@ sub read_info_file($)
 	my $line_checksum;		# Checksum of current line
 	my $br_found;
 	my $br_hit;
+	my $notified_about_relative_paths;
 	local *INFO_HANDLE;		# Filehandle for .info file
 
 	info("Reading data file $tracefile\n");
@@ -1281,14 +1476,14 @@ sub read_info_file($)
 				"compressed file $_[0]!\n");
 
 		# Open compressed file
-		open(INFO_HANDLE, "gunzip -c $_[0]|")
+		open(INFO_HANDLE, "-|", "gunzip -c '$_[0]'")
 			or die("ERROR: cannot start gunzip to decompress ".
 			       "file $_[0]!\n");
 	}
 	else
 	{
 		# Open decompressed file
-		open(INFO_HANDLE, $_[0])
+		open(INFO_HANDLE, "<", $_[0])
 			or die("ERROR: cannot read file $_[0]!\n");
 	}
 
@@ -1317,7 +1512,16 @@ sub read_info_file($)
 			{
 				# Filename information found
 				# Retrieve data for new entry
-				$filename = $1;
+				$filename = File::Spec->rel2abs($1, Cwd::cwd());
+
+				if (!File::Spec->file_name_is_absolute($1) &&
+				    !$notified_about_relative_paths)
+				{
+					info("Resolved relative source file ".
+					     "path \"$1\" with CWD to ".
+					     "\"$filename\".\n");
+					$notified_about_relative_paths = 1;
+				}
 
 				$data = $result{$filename};
 				($testdata, $sumcount, $funcdata, $checkdata,
@@ -1379,6 +1583,8 @@ sub read_info_file($)
 
 			/^FN:(\d+),([^,]+)/ && do
 			{
+				last if (!$func_coverage);
+
 				# Function data found, add to structure
 				$funcdata->{$2} = $1;
 
@@ -1397,6 +1603,7 @@ sub read_info_file($)
 
 			/^FNDA:(\d+),([^,]+)/ && do
 			{
+				last if (!$func_coverage);
 				# Function call count found, add to structure
 				# Add summary counts
 				$sumfnccount->{$2} += $1;
@@ -1414,6 +1621,7 @@ sub read_info_file($)
 				my ($line, $block, $branch, $taken) =
 				   ($1, $2, $3, $4);
 
+				last if (!$br_coverage);
 				$sumbrcount->{$line} =
 					br_ivec_push($sumbrcount->{$line},
 						     $block, $branch, $taken);
@@ -1614,8 +1822,8 @@ sub set_info_entry($$$$$$$$$;$$$$$$)
 
 sub add_counts($$)
 {
-	my %data1 = %{$_[0]};	# Hash 1
-	my %data2 = %{$_[1]};	# Hash 2
+	my $data1_ref = $_[0];	# Hash 1
+	my $data2_ref = $_[1];	# Hash 2
 	my %result;		# Resulting hash
 	my $line;		# Current line iteration scalar
 	my $data1_count;	# Count of line in hash1
@@ -1623,10 +1831,10 @@ sub add_counts($$)
 	my $found = 0;		# Total number of lines found
 	my $hit = 0;		# Number of lines with a count > 0
 
-	foreach $line (keys(%data1))
+	foreach $line (keys(%$data1_ref))
 	{
-		$data1_count = $data1{$line};
-		$data2_count = $data2{$line};
+		$data1_count = $data1_ref->{$line};
+		$data2_count = $data2_ref->{$line};
 
 		# Add counts if present in both hashes
 		if (defined($data2_count)) { $data1_count += $data2_count; }
@@ -1638,14 +1846,14 @@ sub add_counts($$)
 		if ($data1_count > 0) { $hit++; }
 	}
 
-	# Add lines unique to data2
-	foreach $line (keys(%data2))
+	# Add lines unique to data2_ref
+	foreach $line (keys(%$data2_ref))
 	{
-		# Skip lines already in data1
-		if (defined($data1{$line})) { next; }
+		# Skip lines already in data1_ref
+		if (defined($data1_ref->{$line})) { next; }
 
-		# Copy count from data2
-		$result{$line} = $data2{$line};
+		# Copy count from data2_ref
+		$result{$line} = $data2_ref->{$line};
 
 		$found++;
 		if ($result{$line} > 0) { $hit++; }
@@ -2110,16 +2318,17 @@ sub combine_info_files($$)
 
 
 #
-# get_prefix(filename_list)
+# get_prefix(min_dir, filename_list)
 #
 # Search FILENAME_LIST for a directory prefix which is common to as many
 # list entries as possible, so that removing this prefix will minimize the
-# sum of the lengths of all resulting shortened filenames.
+# sum of the lengths of all resulting shortened filenames while observing
+# that no filename has less than MIN_DIR parent directories.
 #
 
-sub get_prefix(@)
+sub get_prefix($@)
 {
-	my @filename_list = @_;		# provided list of filenames
+	my ($min_dir, @filename_list) = @_;
 	my %prefix;			# mapping: prefix -> sum of lengths
 	my $current;			# Temporary iteration variable
 
@@ -2128,12 +2337,14 @@ sub get_prefix(@)
 	{
 		# Need explicit assignment to get a copy of $_ so that
 		# shortening the contained prefix does not affect the list
-		$current = shorten_prefix($_);
+		$current = $_;
 		while ($current = shorten_prefix($current))
 		{
+			$current .= "/";
+
 			# Skip rest if the remaining prefix has already been
 			# added to hash
-			if ($prefix{$current}) { last; }
+			if (exists($prefix{$current})) { last; }
 
 			# Initialize with 0
 			$prefix{$current}="0";
@@ -2141,6 +2352,20 @@ sub get_prefix(@)
 
 	}
 
+	# Remove all prefixes that would cause filenames to have less than
+	# the minimum number of parent directories
+	foreach my $filename (@filename_list) {
+		my $dir = dirname($filename);
+
+		for (my $i = 0; $i < $min_dir; $i++) {
+			delete($prefix{$dir."/"});
+			$dir = shorten_prefix($dir);
+		}
+	}
+
+	# Check if any prefix remains
+	return undef if (!%prefix);
+
 	# Calculate sum of lengths for all prefixes
 	foreach $current (keys(%prefix))
 	{
@@ -2169,6 +2394,8 @@ sub get_prefix(@)
 		}
 	}
 
+	$current =~ s/\/$//;
+
 	return($current);
 }
 
@@ -2260,7 +2487,7 @@ sub read_testfile($)
 	my $changed_testname;
 	local *TEST_HANDLE;
 
-	open(TEST_HANDLE, "<".$_[0])
+	open(TEST_HANDLE, "<", $_[0])
 		or die("ERROR: cannot open $_[0]!\n");
 
 	while (<TEST_HANDLE>)
@@ -2281,6 +2508,9 @@ sub read_testfile($)
 		# Match lines beginning with TD:<whitespace(s)>
 		if (/^TD:\s+(.*?)\s*$/)
 		{
+			if (!defined($test_name)) {
+				die("ERROR: Found test description without prior test name in $_[0]:$.\n");
+			}
 			# Check for empty line
 			if ($1)
 			{
@@ -2348,10 +2578,15 @@ sub get_date_string()
 	my $year;
 	my $month;
 	my $day;
+	my $hour;
+	my $min;
+	my $sec;
 
-	($year, $month, $day) = (localtime())[5, 4, 3];
+	($year, $month, $day, $hour, $min, $sec) =
+		(localtime())[5, 4, 3, 2, 1, 0];
 
-	return sprintf("%d-%02d-%02d", $year+1900, $month+1, $day);
+	return sprintf("%d-%02d-%02d %02d:%02d:%02d", $year+1900, $month+1,
+		       $day, $hour, $min, $sec);
 }
 
 
@@ -2408,8 +2643,10 @@ sub write_description_file($$$$$$$)
 
 	foreach $test_name (sort(keys(%description)))
 	{
-		write_test_table_entry(*HTML_HANDLE, $test_name,
-				       escape_html($description{$test_name}));
+		my $desc = $description{$test_name};
+
+		$desc = escape_html($desc) if (!$rc_desc_html);
+		write_test_table_entry(*HTML_HANDLE, $test_name, $desc);
 	}
 
 	write_test_table_epilog(*HTML_HANDLE);
@@ -2531,7 +2768,7 @@ sub write_png_files()
 		 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82] if ($sort);
 	foreach (keys(%data))
 	{
-		open(PNG_HANDLE, ">".$_)
+		open(PNG_HANDLE, ">", $_)
 			or die("ERROR: cannot create $_!\n");
 		binmode(PNG_HANDLE);
 		print(PNG_HANDLE map(chr,@{$data{$_}}));
@@ -2549,7 +2786,7 @@ sub write_htaccess_file()
 	local *HTACCESS_HANDLE;
 	my $htaccess_data;
 
-	open(*HTACCESS_HANDLE, ">.htaccess")
+	open(*HTACCESS_HANDLE, ">", ".htaccess")
 		or die("ERROR: cannot open .htaccess for writing!\n");
 
 	$htaccess_data = (<<"END_OF_HTACCESS")
@@ -2582,7 +2819,7 @@ sub write_css_file()
 		return;
 	}
 
-	open(CSS_HANDLE, ">gcov.css")
+	open(CSS_HANDLE, ">", "gcov.css")
 		or die ("ERROR: cannot open gcov.css for writing!\n");
 
 
@@ -3132,6 +3369,7 @@ END_OF_CSS
 
 sub get_bar_graph_code($$$)
 {
+	my ($base_dir, $found, $hit) = @_;
 	my $rate;
 	my $alt;
 	my $width;
@@ -3142,13 +3380,12 @@ sub get_bar_graph_code($$$)
 	# Check number of instrumented lines
 	if ($_[1] == 0) { return ""; }
 
-	$rate		= $_[2] * 100 / $_[1];
-	$alt		= sprintf("%.1f", $rate)."%";
-	$width		= sprintf("%.0f", $rate);
-	$remainder	= sprintf("%d", 100-$width);
+	$alt		= rate($hit, $found, "%");
+	$width		= rate($hit, $found, undef, 0);
+	$remainder	= 100 - $width;
 
 	# Decide which .png file to use
-	$png_name = $rate_png[classify_rate($_[1], $_[2], $med_limit,
+	$png_name = $rate_png[classify_rate($found, $hit, $med_limit,
 					    $hi_limit)];
 
 	if ($width == 0)
@@ -3197,7 +3434,7 @@ sub classify_rate($$$$)
 	if ($found == 0) {
 		return 2;
 	}
-	$rate = $hit * 100 / $found;
+	$rate = rate($hit, $found);
 	if ($rate < $med) {
 		return 0;
 	} elsif ($rate < $hi) {
@@ -3415,12 +3652,13 @@ sub write_file_table_entry(*$$$@)
 	my ($handle, $base_dir, $filename, $page_link, @entries) = @_;
 	my $file_code;
 	my $entry;
+	my $esc_filename = escape_html($filename);
 
 	# Add link to source if provided
 	if (defined($page_link) && $page_link ne "") {
-		$file_code = "<a href=\"$page_link\">$filename</a>";
+		$file_code = "<a href=\"$page_link\">$esc_filename</a>";
 	} else {
-		$file_code = $filename;
+		$file_code = $esc_filename;
 	}
 
 	# First column: filename
@@ -3450,7 +3688,7 @@ END_OF_HTML
 			$rate = "-";
 			$class = "Hi";
 		} else {
-			$rate = sprintf("%.1f&nbsp;%%", $hit * 100 / $found);
+			$rate = rate($hit, $found, "&nbsp;%");
 			$class = $rate_name[classify_rate($found, $hit,
 					    $med, $hi)];
 		}
@@ -3490,11 +3728,8 @@ END_OF_HTML
 	# Test data
 	foreach $entry (@entries) {
 		my ($found, $hit) = @{$entry};
-		my $rate = "-";
+		my $rate = rate($hit, $found, "&nbsp;%");
 
-		if ($found > 0) {
-			$rate = sprintf("%.1f&nbsp;%%", $hit * 100 / $found);
-		}
 		write_html($handle, <<END_OF_HTML);
 	      <td class="testPer">$rate</td>
 	      <td class="testNum">$hit&nbsp;/&nbsp;$found</td>
@@ -4010,7 +4245,7 @@ sub write_frameset(*$$$)
 	<html lang="en">
 
 	<head>
-	  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+	  <meta http-equiv="Content-Type" content="text/html; charset=$charset">
 	  <title>$_[3]</title>
 	  <link rel="stylesheet" type="text/css" href="$_[1]gcov.css">
 	</head>
@@ -4073,7 +4308,7 @@ sub write_overview(*$$$$)
 
 	<head>
 	  <title>$_[3]</title>
-	  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+	  <meta http-equiv="Content-Type" content="text/html; charset=$charset">
 	  <link rel="stylesheet" type="text/css" href="$_[1]gcov.css">
 	</head>
 
@@ -4120,17 +4355,6 @@ END_OF_HTML
 }
 
 
-# format_rate(found, hit)
-#
-# Return formatted percent string for coverage rate.
-#
-
-sub format_rate($$)
-{	
-	return $_[0] == 0 ? "-" : sprintf("%.1f", $_[1] * 100 / $_[0])." %";
-}
-
-
 sub max($$)
 {
 	my ($a, $b) = @_;
@@ -4172,6 +4396,7 @@ sub write_header(*$$$$$$$$$$)
 	my @row_right;
 	my $num_rows;
 	my $i;
+	my $esc_trunc_name = escape_html($trunc_name);
 
 	$base_name = basename($rel_filename);
 
@@ -4187,12 +4412,14 @@ sub write_header(*$$$$$$$$$$)
 		# Directory overview
 		$base_dir = get_relative_base_path($rel_filename);
 		$view = "<a href=\"$base_dir"."index.$html_ext\">".
-			"$overview_title</a> - $trunc_name";
+			"$overview_title</a> - $esc_trunc_name";
 	}
 	elsif ($type == $HDR_SOURCE || $type == $HDR_FUNC)
 	{
 		# File view
 		my $dir_name = dirname($rel_filename);
+		my $esc_base_name = escape_html($base_name);
+		my $esc_dir_name = escape_html($dir_name);
 
 		$base_dir = get_relative_base_path($dir_name);
 		if ($frames)
@@ -4202,21 +4429,25 @@ sub write_header(*$$$$$$$$$$)
 			$view = "<a href=\"$base_dir"."index.$html_ext\" ".
 				"target=\"_parent\">$overview_title</a> - ".
 				"<a href=\"index.$html_ext\" target=\"_parent\">".
-				"$dir_name</a> - $base_name";
+				"$esc_dir_name</a> - $esc_base_name";
 		}
 		else
 		{
 			$view = "<a href=\"$base_dir"."index.$html_ext\">".
 				"$overview_title</a> - ".
 				"<a href=\"index.$html_ext\">".
-				"$dir_name</a> - $base_name";
+				"$esc_dir_name</a> - $esc_base_name";
 		}
 
 		# Add function suffix
 		if ($func_coverage) {
 			$view .= "<span style=\"font-size: 80%;\">";
 			if ($type == $HDR_SOURCE) {
-				$view .= " (source / <a href=\"$base_name.func.$html_ext\">functions</a>)";
+				if ($sort) {
+					$view .= " (source / <a href=\"$base_name.func-sort-c.$html_ext\">functions</a>)";
+				} else {
+					$view .= " (source / <a href=\"$base_name.func.$html_ext\">functions</a>)";
+				}
 			} elsif ($type == $HDR_FUNC) {
 				$view .= " (<a href=\"$base_name.gcov.$html_ext\">source</a> / functions)";
 			}
@@ -4303,7 +4534,7 @@ END_OF_HTML
 	# Line coverage
 	$style = $rate_name[classify_rate($lines_found, $lines_hit,
 					  $med_limit, $hi_limit)];
-	$rate = format_rate($lines_found, $lines_hit);
+	$rate = rate($lines_hit, $lines_found, " %");
 	push(@row_right, [[undef, "headerItem", "Lines:"],
 			  [undef, "headerCovTableEntry", $lines_hit],
 			  [undef, "headerCovTableEntry", $lines_found],
@@ -4313,7 +4544,7 @@ END_OF_HTML
 	if ($func_coverage) {
 		$style = $rate_name[classify_rate($fn_found, $fn_hit,
 						  $fn_med_limit, $fn_hi_limit)];
-		$rate = format_rate($fn_found, $fn_hit);
+		$rate = rate($fn_hit, $fn_found, " %");
 		push(@row_right, [[undef, "headerItem", "Functions:"],
 				  [undef, "headerCovTableEntry", $fn_hit],
 				  [undef, "headerCovTableEntry", $fn_found],
@@ -4324,7 +4555,7 @@ END_OF_HTML
 	if ($br_coverage) {
 		$style = $rate_name[classify_rate($br_found, $br_hit,
 						  $br_med_limit, $br_hi_limit)];
-		$rate = format_rate($br_found, $br_hit);
+		$rate = rate($br_hit, $br_found, " %");
 		push(@row_right, [[undef, "headerItem", "Branches:"],
 				  [undef, "headerCovTableEntry", $br_hit],
 				  [undef, "headerCovTableEntry", $br_found],
@@ -4795,6 +5026,7 @@ sub br_ivec_get($$)
 
 	# Retrieve data from vector
 	$block	= vec($vec, $offset + $BR_BLOCK, $BR_VEC_WIDTH);
+	$block = -1 if ($block == $BR_VEC_MAX);
 	$branch = vec($vec, $offset + $BR_BRANCH, $BR_VEC_WIDTH);
 	$taken	= vec($vec, $offset + $BR_TAKEN, $BR_VEC_WIDTH);
 
@@ -4820,10 +5052,12 @@ sub br_ivec_push($$$$)
 	my $i;
 
 	$vec = "" if (!defined($vec));
+	$block = $BR_VEC_MAX if $block < 0;
 
 	# Check if branch already exists in vector
 	for ($i = 0; $i < $num; $i++) {
 		my ($v_block, $v_branch, $v_taken) = br_ivec_get($vec, $i);
+		$v_block = $BR_VEC_MAX if $v_block < 0;
 
 		next if ($v_block != $block || $v_branch != $branch);
 
@@ -4958,19 +5192,43 @@ sub write_source($$$$$$$)
 	my $sumbrcount = $_[6];
 	my $datafunc = get_hash_reverse($funcdata);
 	my $add_anchor;
+	my @file;
 
 	if ($_[2])
 	{
 		%count_data = %{$_[2]};
 	}
 
-	open(SOURCE_HANDLE, "<".$source_filename)
-		or die("ERROR: cannot open $source_filename for reading!\n");
+	if (!open(SOURCE_HANDLE, "<", $source_filename)) {
+		my @lines;
+		my $last_line = 0;
+
+		if (!$ignore[$ERROR_SOURCE]) {
+			die("ERROR: cannot read $source_filename\n");
+		}
+
+		# Continue without source file
+		warn("WARNING: cannot read $source_filename!\n");
+
+		@lines = sort( { $a <=> $b }  keys(%count_data));
+		if (@lines) {
+			$last_line = $lines[scalar(@lines) - 1];
+		}
+		return ( ":" ) if ($last_line < 1);
+
+		# Simulate gcov behavior
+		for ($line_number = 1; $line_number <= $last_line;
+		     $line_number++) {
+			push(@file, "/* EOF */");
+		}
+	} else {
+		@file = <SOURCE_HANDLE>;
+	}
 	
 	write_source_prolog(*HTML_HANDLE);
-
-	for ($line_number = 1; <SOURCE_HANDLE> ; $line_number++)
-	{
+	$line_number = 0;
+	foreach (@file) {
+		$line_number++;
 		chomp($_);
 
 		# Also remove CR from line-end
@@ -5055,8 +5313,51 @@ sub funcview_get_sorted($$$)
 	if ($type == 0) {
 		return sort(keys(%{$funcdata}));
 	}
-	return sort({$sumfncdata->{$b} <=> $sumfncdata->{$a}}
-		    keys(%{$sumfncdata}));
+	return sort({
+		$sumfncdata->{$b} == $sumfncdata->{$a} ?
+			$a cmp $b : $sumfncdata->{$a} <=> $sumfncdata->{$b}
+		} keys(%{$sumfncdata}));
+}
+
+sub demangle_list($)
+{
+	my ($list) = @_;
+	my $tmpfile;
+	my $handle;
+	my %demangle;
+	my %versions;
+
+	# Write function names to file
+	($handle, $tmpfile) = tempfile();
+	die("ERROR: could not create temporary file") if (!defined($tmpfile));
+	print($handle join("\n", @$list));
+	close($handle);
+
+	# Build translation hash from c++filt output
+	open($handle, "-|", "c++filt < $tmpfile") or
+		die("ERROR: could not run c++filt: $!\n");
+	foreach my $func (@$list) {
+		my $translated = <$handle>;
+		my $version;
+
+		last if (!defined($translated));
+		chomp($translated);
+
+		$version = ++$versions{$translated};
+		$translated .= ".$version" if ($version > 1);
+		$demangle{$func} = $translated;
+	}
+	close($handle);
+
+	if (scalar(keys(%demangle)) != scalar(@$list)) {
+		die("ERROR: c++filt output not as expected (".
+		    scalar(keys(%demangle))." vs ".scalar(@$list).") lines\n");
+	}
+
+	unlink($tmpfile) or
+		warn("WARNING: could not remove temporary file $tmpfile: $!\n");
+
+	return \%demangle;
 }
 
 #
@@ -5086,6 +5387,7 @@ sub write_function_table(*$$$$$$$$$$)
 	my $func;
 	my $func_code;
 	my $count_code;
+	my $demangle;
 
 	# Get HTML code for headings
 	$func_code = funcview_get_func_code($name, $base, $type);
@@ -5100,7 +5402,12 @@ sub write_function_table(*$$$$$$$$$$)
 	    </tr>
 END_OF_HTML
 	;
-	
+
+	# Get demangle translation hash
+	if ($demangle_cpp) {
+		$demangle = demangle_list([ sort(keys(%{$funcdata})) ]);
+	}
+
 	# Get a sorted table
 	foreach $func (funcview_get_sorted($funcdata, $sumfncdata, $type)) {
 		if (!defined($funcdata->{$func}))
@@ -5113,12 +5420,10 @@ END_OF_HTML
 		my $count = $sumfncdata->{$name};
 		my $countstyle;
 
-		# Demangle C++ function names if requested
-		if ($demangle_cpp) {
-			$name = `c++filt "$name"`;
-			chomp($name);
-		}
-		# Escape any remaining special characters
+		# Replace function name with demangled version if available
+		$name = $demangle->{$name} if (exists($demangle->{$name}));
+
+		# Escape special characters
 		$name = escape_html($name);
 		if ($startline < 1) {
 			$startline = 1;
@@ -5412,22 +5717,25 @@ sub remove_unused_descriptions()
 
 
 #
-# apply_prefix(filename, prefix)
+# apply_prefix(filename, PREFIXES)
 #
-# If FILENAME begins with PREFIX, remove PREFIX from FILENAME and return
-# resulting string, otherwise return FILENAME.
+# If FILENAME begins with PREFIX from PREFIXES, remove PREFIX from FILENAME
+# and return resulting string, otherwise return FILENAME.
 #
 
-sub apply_prefix($$)
+sub apply_prefix($@)
 {
-	my $filename = $_[0];
-	my $prefix = $_[1];
+	my $filename = shift;
+	my @dir_prefix = @_;
 
-	if (defined($prefix) && ($prefix ne ""))
+	if (@dir_prefix)
 	{
-		if ($filename =~ /^\Q$prefix\E\/(.*)$/)
+		foreach my $prefix (@dir_prefix)
 		{
-			return substr($filename, length($prefix) + 1);
+			if ($prefix ne "" && $filename =~ /^\Q$prefix\E\/(.*)$/)
+			{
+				return substr($filename, length($prefix) + 1);
+			}
 		}
 	}
 
@@ -5455,12 +5763,12 @@ sub system_no_output($@)
 	local *OLD_STDOUT;
 
 	# Save old stdout and stderr handles
-	($mode & 1) && open(OLD_STDOUT, ">>&STDOUT");
-	($mode & 2) && open(OLD_STDERR, ">>&STDERR");
+	($mode & 1) && open(OLD_STDOUT, ">>&", "STDOUT");
+	($mode & 2) && open(OLD_STDERR, ">>&", "STDERR");
 
 	# Redirect to /dev/null
-	($mode & 1) && open(STDOUT, ">/dev/null");
-	($mode & 2) && open(STDERR, ">/dev/null");
+	($mode & 1) && open(STDOUT, ">", "/dev/null");
+	($mode & 2) && open(STDERR, ">", "/dev/null");
 
 	system(@_);
 	$result = $?;
@@ -5470,8 +5778,8 @@ sub system_no_output($@)
 	($mode & 2) && close(STDERR);
 
 	# Restore old handles
-	($mode & 1) && open(STDOUT, ">>&OLD_STDOUT");
-	($mode & 2) && open(STDERR, ">>&OLD_STDERR");
+	($mode & 1) && open(STDOUT, ">>&", "OLD_STDOUT");
+	($mode & 2) && open(STDERR, ">>&", "OLD_STDERR");
 
 	return $result;
 }
@@ -5492,7 +5800,7 @@ sub read_config($)
 	my $value;
 	local *HANDLE;
 
-	if (!open(HANDLE, "<$filename"))
+	if (!open(HANDLE, "<", $filename))
 	{
 		warn("WARNING: cannot read configuration file $filename\n");
 		return undef;
@@ -5531,8 +5839,8 @@ sub read_config($)
 #   key_string => var_ref
 #
 # where KEY_STRING is a keyword and VAR_REF is a reference to an associated
-# variable. If the global configuration hash CONFIG contains a value for
-# keyword KEY_STRING, VAR_REF will be assigned the value for that keyword. 
+# variable. If the global configuration hashes CONFIG or OPT_RC contain a value
+# for keyword KEY_STRING, VAR_REF will be assigned the value for that keyword. 
 #
 
 sub apply_config($)
@@ -5541,8 +5849,9 @@ sub apply_config($)
 
 	foreach (keys(%{$ref}))
 	{
-		if (defined($config->{$_}))
-		{
+		if (defined($opt_rc{$_})) {
+			${$ref->{$_}} = $opt_rc{$_};
+		} elsif (defined($config->{$_})) {
 			${$ref->{$_}} = $config->{$_};
 		}
 	}
@@ -5565,7 +5874,7 @@ sub get_html_prolog($)
 	{
 		local *HANDLE;
 
-		open(HANDLE, "<".$filename)
+		open(HANDLE, "<", $filename)
 			or die("ERROR: cannot open html prolog $filename!\n");
 		while (<HANDLE>)
 		{
@@ -5581,7 +5890,7 @@ sub get_html_prolog($)
 <html lang="en">
 
 <head>
-  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+  <meta http-equiv="Content-Type" content="text/html; charset=$charset">
   <title>\@pagetitle\@</title>
   <link rel="stylesheet" type="text/css" href="\@basedir\@gcov.css">
 </head>
@@ -5611,7 +5920,7 @@ sub get_html_epilog($)
 	{
 		local *HANDLE;
 
-		open(HANDLE, "<".$filename)
+		open(HANDLE, "<", $filename)
 			or die("ERROR: cannot open html epilog $filename!\n");
 		while (<HANDLE>)
 		{
@@ -5646,3 +5955,96 @@ sub die_handler($)
 
 	die("$tool_name: $msg");
 }
+
+#
+# parse_ignore_errors(@ignore_errors)
+#
+# Parse user input about which errors to ignore.
+#
+
+sub parse_ignore_errors(@)
+{
+	my (@ignore_errors) = @_;
+	my @items;
+	my $item;
+
+	return if (!@ignore_errors);
+
+	foreach $item (@ignore_errors) {
+		$item =~ s/\s//g;
+		if ($item =~ /,/) {
+			# Split and add comma-separated parameters
+			push(@items, split(/,/, $item));
+		} else {
+			# Add single parameter
+			push(@items, $item);
+		}
+	}
+	foreach $item (@items) {
+		my $item_id = $ERROR_ID{lc($item)};
+
+		if (!defined($item_id)) {
+			die("ERROR: unknown argument for --ignore-errors: ".
+			    "$item\n");
+		}
+		$ignore[$item_id] = 1;
+	}
+}
+
+#
+# parse_dir_prefix(@dir_prefix)
+#
+# Parse user input about the prefix list
+#
+
+sub parse_dir_prefix(@)
+{
+	my (@opt_dir_prefix) = @_;
+	my $item;
+
+	return if (!@opt_dir_prefix);
+
+	foreach $item (@opt_dir_prefix) {
+		if ($item =~ /,/) {
+			# Split and add comma-separated parameters
+			push(@dir_prefix, split(/,/, $item));
+		} else {
+			# Add single parameter
+			push(@dir_prefix, $item);
+		}
+	}
+}
+
+#
+# rate(hit, found[, suffix, precision, width])
+#
+# Return the coverage rate [0..100] for HIT and FOUND values. 0 is only
+# returned when HIT is 0. 100 is only returned when HIT equals FOUND.
+# PRECISION specifies the precision of the result. SUFFIX defines a
+# string that is appended to the result if FOUND is non-zero. Spaces
+# are added to the start of the resulting string until it is at least WIDTH
+# characters wide.
+#
+
+sub rate($$;$$$)
+{
+        my ($hit, $found, $suffix, $precision, $width) = @_;
+        my $rate; 
+
+	# Assign defaults if necessary
+	$precision	= $default_precision if (!defined($precision));
+	$suffix		= ""	if (!defined($suffix));
+	$width		= 0	if (!defined($width));
+        
+        return sprintf("%*s", $width, "-") if (!defined($found) || $found == 0);
+        $rate = sprintf("%.*f", $precision, $hit * 100 / $found);
+
+	# Adjust rates if necessary
+        if ($rate == 0 && $hit > 0) {
+		$rate = sprintf("%.*f", $precision, 1 / 10 ** $precision);
+        } elsif ($rate == 100 && $hit != $found) {
+		$rate = sprintf("%.*f", $precision, 100 - 1 / 10 ** $precision);
+	}
+
+	return sprintf("%*s", $width, $rate.$suffix);
+}
diff --git a/3rdParty/LCov/geninfo b/3rdParty/LCov/geninfo
index dcb1a67..7c4e6cc 100755
--- a/3rdParty/LCov/geninfo
+++ b/3rdParty/LCov/geninfo
@@ -1,6 +1,6 @@
 #!/usr/bin/perl -w
 #
-#   Copyright (c) International Business Machines  Corp., 2002,2010
+#   Copyright (c) International Business Machines  Corp., 2002,2012
 #
 #   This program is free software;  you can redistribute it and/or modify
 #   it under the terms of the GNU General Public License as published by
@@ -52,17 +52,23 @@
 use strict;
 use File::Basename; 
 use File::Spec::Functions qw /abs2rel catdir file_name_is_absolute splitdir
-			      splitpath/;
+			      splitpath catpath/;
 use Getopt::Long;
 use Digest::MD5 qw(md5_base64);
-
+use Cwd qw/abs_path/;
+if( $^O eq "msys" )
+{
+	require File::Spec::Win32;
+}
 
 # Constants
-our $lcov_version	= 'LCOV version 1.9';
+our $tool_dir		= abs_path(dirname($0));
+our $lcov_version	= "LCOV version 1.12";
 our $lcov_url		= "http://ltp.sourceforge.net/coverage/lcov.php";
 our $gcov_tool		= "gcov";
 our $tool_name		= basename($0);
 
+our $GCOV_VERSION_4_7_0	= 0x40700;
 our $GCOV_VERSION_3_4_0	= 0x30400;
 our $GCOV_VERSION_3_3_0	= 0x30300;
 our $GCNO_FUNCTION_TAG	= 0x01000000;
@@ -70,15 +76,68 @@ our $GCNO_LINES_TAG	= 0x01450000;
 our $GCNO_FILE_MAGIC	= 0x67636e6f;
 our $BBG_FILE_MAGIC	= 0x67626267;
 
-our $COMPAT_HAMMER	= "hammer";
-
+# Error classes which users may specify to ignore during processing
 our $ERROR_GCOV		= 0;
 our $ERROR_SOURCE	= 1;
 our $ERROR_GRAPH	= 2;
+our %ERROR_ID = (
+	"gcov" => $ERROR_GCOV,
+	"source" => $ERROR_SOURCE,
+	"graph" => $ERROR_GRAPH,
+);
 
 our $EXCL_START = "LCOV_EXCL_START";
 our $EXCL_STOP = "LCOV_EXCL_STOP";
-our $EXCL_LINE = "LCOV_EXCL_LINE";
+
+# Marker to exclude branch coverage but keep function and line coveage
+our $EXCL_BR_START = "LCOV_EXCL_BR_START";
+our $EXCL_BR_STOP = "LCOV_EXCL_BR_STOP";
+
+# Compatibility mode values
+our $COMPAT_VALUE_OFF	= 0;
+our $COMPAT_VALUE_ON	= 1;
+our $COMPAT_VALUE_AUTO	= 2;
+
+# Compatibility mode value names
+our %COMPAT_NAME_TO_VALUE = (
+	"off"	=> $COMPAT_VALUE_OFF,
+	"on"	=> $COMPAT_VALUE_ON,
+	"auto"	=> $COMPAT_VALUE_AUTO,
+);
+
+# Compatiblity modes
+our $COMPAT_MODE_LIBTOOL	= 1 << 0;
+our $COMPAT_MODE_HAMMER		= 1 << 1;
+our $COMPAT_MODE_SPLIT_CRC	= 1 << 2;
+
+# Compatibility mode names
+our %COMPAT_NAME_TO_MODE = (
+	"libtool"	=> $COMPAT_MODE_LIBTOOL,
+	"hammer"	=> $COMPAT_MODE_HAMMER,
+	"split_crc"	=> $COMPAT_MODE_SPLIT_CRC,
+	"android_4_4_0"	=> $COMPAT_MODE_SPLIT_CRC,
+);
+
+# Map modes to names
+our %COMPAT_MODE_TO_NAME = (
+	$COMPAT_MODE_LIBTOOL	=> "libtool",
+	$COMPAT_MODE_HAMMER	=> "hammer",
+	$COMPAT_MODE_SPLIT_CRC	=> "split_crc",
+);
+
+# Compatibility mode default values
+our %COMPAT_MODE_DEFAULTS = (
+	$COMPAT_MODE_LIBTOOL	=> $COMPAT_VALUE_ON,
+	$COMPAT_MODE_HAMMER	=> $COMPAT_VALUE_AUTO,
+	$COMPAT_MODE_SPLIT_CRC	=> $COMPAT_VALUE_AUTO,
+);
+
+# Compatibility mode auto-detection routines
+sub compat_hammer_autodetect();
+our %COMPAT_MODE_AUTO = (
+	$COMPAT_MODE_HAMMER	=> \&compat_hammer_autodetect,
+	$COMPAT_MODE_SPLIT_CRC	=> 1,	# will be done later
+);
 
 our $BR_LINE		= 0;
 our $BR_BLOCK		= 1;
@@ -86,8 +145,9 @@ our $BR_BRANCH		= 2;
 our $BR_TAKEN		= 3;
 our $BR_VEC_ENTRIES	= 4;
 our $BR_VEC_WIDTH	= 32;
+our $BR_VEC_MAX		= vec(pack('b*', 1 x $BR_VEC_WIDTH), 0, $BR_VEC_WIDTH);
 
-our $UNNAMED_BLOCK	= 9999;
+our $UNNAMED_BLOCK	= -1;
 
 # Prototypes
 sub print_usage(*);
@@ -112,8 +172,9 @@ sub warn_handler($);
 sub die_handler($);
 sub graph_error($$);
 sub graph_expect($);
-sub graph_read(*$;$);
+sub graph_read(*$;$$);
 sub graph_skip(*$;$);
+sub uniq(@);
 sub sort_uniq(@);
 sub sort_uniq_lex(@);
 sub graph_cleanup($);
@@ -123,18 +184,19 @@ sub graph_add_order($$$);
 sub read_bb_word(*;$);
 sub read_bb_value(*;$);
 sub read_bb_string(*$);
-sub read_bb($$);
+sub read_bb($);
 sub read_bbg_word(*;$);
 sub read_bbg_value(*;$);
 sub read_bbg_string(*);
-sub read_bbg_lines_record(*$$$$$$);
-sub read_bbg($$);
-sub read_gcno_word(*;$);
-sub read_gcno_value(*$;$);
+sub read_bbg_lines_record(*$$$$$);
+sub read_bbg($);
+sub read_gcno_word(*;$$);
+sub read_gcno_value(*$;$$);
 sub read_gcno_string(*$);
-sub read_gcno_lines_record(*$$$$$$$);
+sub read_gcno_lines_record(*$$$$$$);
+sub determine_gcno_split_crc($$$);
 sub read_gcno_function_record(*$$$$);
-sub read_gcno($$);
+sub read_gcno($);
 sub get_gcov_capabilities();
 sub get_overall_line($$$$);
 sub print_overall_rate($$$$$$$$$);
@@ -142,10 +204,17 @@ sub br_gvec_len($);
 sub br_gvec_get($$);
 sub debug($);
 sub int_handler();
+sub parse_ignore_errors(@);
+sub is_external($);
+sub compat_name($);
+sub parse_compat_modes($);
+sub is_compat($);
+sub is_compat_auto($);
 
 
 # Global variables
 our $gcov_version;
+our $gcov_version_string;
 our $graph_file_extension;
 our $data_file_extension;
 our @data_directory;
@@ -158,12 +227,13 @@ our $version;
 our $follow;
 our $checksum;
 our $no_checksum;
-our $compat_libtool;
-our $no_compat_libtool;
+our $opt_compat_libtool;
+our $opt_no_compat_libtool;
+our $rc_adjust_src_path;# Regexp specifying parts to remove from source path
+our $adjust_src_pattern;
+our $adjust_src_replace;
 our $adjust_testname;
 our $config;		# Configuration file contents
-our $compatibility;	# Compatibility version flag - used to indicate
-			# non-standard GCOV data format versions
 our @ignore_errors;	# List of errors to ignore (parameter)
 our @ignore;		# List of errors to ignore (array)
 our $initial;
@@ -171,9 +241,23 @@ our $no_recursion = 0;
 our $maxdepth;
 our $no_markers = 0;
 our $opt_derive_func_data = 0;
+our $opt_external = 1;
+our $opt_no_external;
 our $debug = 0;
 our $gcov_caps;
 our @gcov_options;
+our @internal_dirs;
+our $opt_config_file;
+our $opt_gcov_all_blocks = 1;
+our $opt_compat;
+our %opt_rc;
+our %compat_value;
+our $gcno_split_crc;
+our $func_coverage = 1;
+our $br_coverage = 0;
+our $rc_auto_base = 1;
+our $excl_line = "LCOV_EXCL_LINE";
+our $excl_br_line = "LCOV_EXCL_BR_LINE";
 
 our $cwd = `pwd`;
 chomp($cwd);
@@ -188,14 +272,32 @@ $SIG{"INT"} = \&int_handler;
 $SIG{__WARN__} = \&warn_handler;
 $SIG{__DIE__} = \&die_handler;
 
-# Prettify version string
-$lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
+# Set LC_ALL so that gcov output will be in a unified format
+$ENV{"LC_ALL"} = "C";
 
-# Set LANG so that gcov output will be in a unified format
-$ENV{"LANG"} = "C";
+# Check command line for a configuration file name
+Getopt::Long::Configure("pass_through", "no_auto_abbrev");
+GetOptions("config-file=s" => \$opt_config_file,
+	   "rc=s%" => \%opt_rc);
+Getopt::Long::Configure("default");
+
+{
+	# Remove spaces around rc options
+	my %new_opt_rc;
+
+	while (my ($key, $value) = each(%opt_rc)) {
+		$key =~ s/^\s+|\s+$//g;
+		$value =~ s/^\s+|\s+$//g;
+
+		$new_opt_rc{$key} = $value;
+	}
+	%opt_rc = %new_opt_rc;
+}
 
 # Read configuration file if available
-if (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
+if (defined($opt_config_file)) {
+	$config = read_config($opt_config_file);
+} elsif (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
 {
 	$config = read_config($ENV{"HOME"}."/.lcovrc");
 }
@@ -204,15 +306,25 @@ elsif (-r "/etc/lcovrc")
 	$config = read_config("/etc/lcovrc");
 }
 
-if ($config)
+if ($config || %opt_rc)
 {
-	# Copy configuration file values to variables
+	# Copy configuration file and --rc values to variables
 	apply_config({
 		"geninfo_gcov_tool"		=> \$gcov_tool,
 		"geninfo_adjust_testname"	=> \$adjust_testname,
 		"geninfo_checksum"		=> \$checksum,
 		"geninfo_no_checksum"		=> \$no_checksum, # deprecated
-		"geninfo_compat_libtool"	=> \$compat_libtool});
+		"geninfo_compat_libtool"	=> \$opt_compat_libtool,
+		"geninfo_external"		=> \$opt_external,
+		"geninfo_gcov_all_blocks"	=> \$opt_gcov_all_blocks,
+		"geninfo_compat"		=> \$opt_compat,
+		"geninfo_adjust_src_path"	=> \$rc_adjust_src_path,
+		"geninfo_auto_base"		=> \$rc_auto_base,
+		"lcov_function_coverage"	=> \$func_coverage,
+		"lcov_branch_coverage"		=> \$br_coverage,
+		"lcov_excl_line"		=> \$excl_line,
+		"lcov_excl_br_line"		=> \$excl_br_line,
+	});
 
 	# Merge options
 	if (defined($no_checksum))
@@ -220,6 +332,34 @@ if ($config)
 		$checksum = ($no_checksum ? 0 : 1);
 		$no_checksum = undef;
 	}
+
+	# Check regexp
+	if (defined($rc_adjust_src_path)) {
+		my ($pattern, $replace) = split(/\s*=>\s*/,
+						$rc_adjust_src_path);
+		local $SIG{__DIE__};
+		eval '$adjust_src_pattern = qr>'.$pattern.'>;';
+		if (!defined($adjust_src_pattern)) {
+			my $msg = $@;
+
+			chomp($msg);
+			$msg =~ s/at \(eval.*$//;
+			warn("WARNING: invalid pattern in ".
+			     "geninfo_adjust_src_path: $msg\n");
+		} elsif (!defined($replace)) {
+			# If no replacement is specified, simply remove pattern
+			$adjust_src_replace = "";
+		} else {
+			$adjust_src_replace = $replace;
+		}
+	}
+	for my $regexp (($excl_line, $excl_br_line)) {
+		eval 'qr/'.$regexp.'/';
+		my $error = $@;
+		chomp($error);
+		$error =~ s/at \(eval.*$//;
+		die("ERROR: invalid exclude pattern: $error") if $error;
+	}
 }
 
 # Parse command line options
@@ -232,8 +372,8 @@ if (!GetOptions("test-name|t=s" => \$test_name,
 		"quiet|q" => \$quiet,
 		"help|h|?" => \$help,
 		"follow|f" => \$follow,
-		"compat-libtool" => \$compat_libtool,
-		"no-compat-libtool" => \$no_compat_libtool,
+		"compat-libtool" => \$opt_compat_libtool,
+		"no-compat-libtool" => \$opt_no_compat_libtool,
 		"gcov-tool=s" => \$gcov_tool,
 		"ignore-errors=s" => \@ignore_errors,
 		"initial|i" => \$initial,
@@ -241,6 +381,11 @@ if (!GetOptions("test-name|t=s" => \$test_name,
 		"no-markers" => \$no_markers,
 		"derive-func-data" => \$opt_derive_func_data,
 		"debug" => \$debug,
+		"external" => \$opt_external,
+		"no-external" => \$opt_no_external,
+		"compat=s" => \$opt_compat,
+		"config-file=s" => \$opt_config_file,
+		"rc=s%" => \%opt_rc,
 		))
 {
 	print(STDERR "Use $tool_name --help to get usage information\n");
@@ -255,10 +400,15 @@ else
 		$no_checksum = undef;
 	}
 
-	if (defined($no_compat_libtool))
+	if (defined($opt_no_compat_libtool))
 	{
-		$compat_libtool = ($no_compat_libtool ? 0 : 1);
-		$no_compat_libtool = undef;
+		$opt_compat_libtool = ($opt_no_compat_libtool ? 0 : 1);
+		$opt_no_compat_libtool = undef;
+	}
+
+	if (defined($opt_no_external)) {
+		$opt_external = 0;
+		$opt_no_external = undef;
 	}
 }
 
@@ -278,6 +428,30 @@ if ($version)
 	exit(0);
 }
 
+# Check gcov tool
+if (system_no_output(3, $gcov_tool, "--help") == -1)
+{
+	die("ERROR: need tool $gcov_tool!\n");
+}
+
+($gcov_version, $gcov_version_string) = get_gcov_version();
+
+# Determine gcov options
+$gcov_caps = get_gcov_capabilities();
+push(@gcov_options, "-b") if ($gcov_caps->{'branch-probabilities'} &&
+			      ($br_coverage || $func_coverage));
+push(@gcov_options, "-c") if ($gcov_caps->{'branch-counts'} &&
+			      $br_coverage);
+push(@gcov_options, "-a") if ($gcov_caps->{'all-blocks'} &&
+			      $opt_gcov_all_blocks && $br_coverage);
+push(@gcov_options, "-p") if ($gcov_caps->{'preserve-paths'});
+
+# Determine compatibility modes
+parse_compat_modes($opt_compat);
+
+# Determine which errors the user wants us to ignore
+parse_ignore_errors(@ignore_errors);
+
 # Make sure test names only contain valid characters
 if ($test_name =~ s/\W/_/g)
 {
@@ -319,17 +493,6 @@ else
 	$checksum = 0;
 }
 
-# Determine libtool compatibility mode
-if (defined($compat_libtool))
-{
-	$compat_libtool = ($compat_libtool? 1 : 0);
-}
-else
-{
-	# Default is on
-	$compat_libtool = 1;
-}
-
 # Determine max depth for recursion
 if ($no_recursion)
 {
@@ -358,42 +521,9 @@ else
 	}
 }
 
-if (@ignore_errors)
-{
-	my @expanded;
-	my $error;
-
-	# Expand comma-separated entries
-	foreach (@ignore_errors) {
-		if (/,/)
-		{
-			push(@expanded, split(",", $_));
-		}
-		else
-		{
-			push(@expanded, $_);
-		}
-	}
-
-	foreach (@expanded)
-	{
-		/^gcov$/ && do { $ignore[$ERROR_GCOV] = 1; next; } ;
-		/^source$/ && do { $ignore[$ERROR_SOURCE] = 1; next; };
-		/^graph$/ && do { $ignore[$ERROR_GRAPH] = 1; next; };
-		die("ERROR: unknown argument for --ignore-errors: $_\n");
-	}
-}
-
-if (system_no_output(3, $gcov_tool, "--help") == -1)
-{
-	die("ERROR: need tool $gcov_tool!\n");
-}
-
-$gcov_version = get_gcov_version();
-
 if ($gcov_version < $GCOV_VERSION_3_4_0)
 {
-	if (defined($compatibility) && $compatibility eq $COMPAT_HAMMER)
+	if (is_compat($COMPAT_MODE_HAMMER))
 	{
 		$data_file_extension = ".da";
 		$graph_file_extension = ".bbg";
@@ -410,20 +540,13 @@ else
 	$graph_file_extension = ".gcno";
 }	
 
-# Determine gcov options
-$gcov_caps = get_gcov_capabilities();
-push(@gcov_options, "-b") if ($gcov_caps->{'branch-probabilities'});
-push(@gcov_options, "-c") if ($gcov_caps->{'branch-counts'});
-push(@gcov_options, "-a") if ($gcov_caps->{'all-blocks'});
-push(@gcov_options, "-p") if ($gcov_caps->{'preserve-paths'});
-
 # Check output filename
 if (defined($output_filename) && ($output_filename ne "-"))
 {
 	# Initially create output filename, data is appended
 	# for each data file processed
 	local *DUMMY_HANDLE;
-	open(DUMMY_HANDLE, ">$output_filename")
+	open(DUMMY_HANDLE, ">", $output_filename)
 		or die("ERROR: cannot create $output_filename!\n");
 	close(DUMMY_HANDLE);
 
@@ -435,12 +558,18 @@ if (defined($output_filename) && ($output_filename ne "-"))
 	}
 }
 
+# Build list of directories to identify external files
+foreach my $entry(@data_directory, $base_directory) {
+	next if (!defined($entry));
+	push(@internal_dirs, solve_relative_path($cwd, $entry));
+}
+
 # Do something
 foreach my $entry (@data_directory) {
 	gen_info($entry);
 }
 
-if ($initial) {
+if ($initial && $br_coverage) {
 	warn("Note: --initial does not generate branch coverage ".
 	     "data\n");
 }
@@ -480,9 +609,12 @@ sequentially.
       --gcov-tool TOOL              Specify gcov tool location
       --ignore-errors ERROR         Continue after ERROR (gcov, source, graph)
       --no-recursion                Exclude subdirectories from processing
-      --function-coverage           Capture function call counts
       --no-markers                  Ignore exclusion markers in source code
       --derive-func-data            Generate function data from line data
+      --(no-)external               Include (ignore) data for external files
+      --config-file FILENAME        Specify configuration file location
+      --rc SETTING=VALUE            Override configuration file setting
+      --compat MODE=on|off|auto     Set compat MODE (libtool, hammer, split_crc)
 
 For more information see: $lcov_url
 END_OF_USAGE
@@ -578,10 +710,13 @@ sub gen_info($)
 	{
 		info("Scanning $directory for $ext files ...\n");
 
-		@file_list = `find "$directory" $maxdepth $follow -name \\*$ext -type f 2>/dev/null`;
+		@file_list = `find "$directory" $maxdepth $follow -name \\*$ext -type f -o -name \\*$ext -type l 2>/dev/null`;
 		chomp(@file_list);
-		@file_list or
-			die("ERROR: no $ext files found in $directory!\n");
+		if (!@file_list) {
+			warn("WARNING: no $ext files found in $directory - ".
+			     "skipping!\n");
+			return;
+		}
 		$prefix = get_common_prefix(1, @file_list);
 		info("Found %d %s files in %s\n", $#file_list+1, $type,
 		     $directory);
@@ -604,6 +739,25 @@ sub gen_info($)
 }
 
 
+#
+# derive_data(contentdata, funcdata, bbdata)
+#
+# Calculate function coverage data by combining line coverage data and the
+# list of lines belonging to a function.
+#
+# contentdata: [ instr1, count1, source1, instr2, count2, source2, ... ]
+# instr<n>: Instrumentation flag for line n
+# count<n>: Execution count for line n
+# source<n>: Source code for line n
+#
+# funcdata: [ count1, func1, count2, func2, ... ]
+# count<n>: Execution count for function number n
+# func<n>: Function name for function number n
+#
+# bbdata: function_name -> [ line1, line2, ... ]
+# line<n>: Line number belonging to the corresponding function
+#
+
 sub derive_data($$$)
 {
 	my ($contentdata, $funcdata, $bbdata) = @_;
@@ -633,6 +787,7 @@ sub derive_data($$$)
 	foreach $fn (keys(%{$bbdata})) {
 		my $line_data = $bbdata->{$fn};
 		my $line;
+		my $fninstr = 0;
 
 		if ($fn eq "") {
 			next;
@@ -640,13 +795,17 @@ sub derive_data($$$)
 		# Find the lowest line count for this function
 		$count = 0;
 		foreach $line (@$line_data) {
+			my $linstr = $gcov_content[ ( $line - 1 ) * 3 + 0 ];
 			my $lcount = $gcov_content[ ( $line - 1 ) * 3 + 1 ];
 
+			next if (!$linstr);
+			$fninstr = 1;
 			if (($lcount > 0) &&
 			    (($count == 0) || ($lcount < $count))) {
 				$count = $lcount;
 			}
 		}
+		next if (!$fninstr);
 		$fn_count{$fn} = $count;
 	}
 
@@ -733,7 +892,6 @@ sub process_dafile($$)
 	my $source;		# gcov source header information
 	my $object;		# gcov object header information
 	my @matches;		# List of absolute paths matching filename
-	my @unprocessed;	# List of unprocessed source code files
 	my $base_dir;		# Base directory for current file
 	my @tmp_links;		# Temporary links to be cleaned up
 	my @result;
@@ -749,11 +907,10 @@ sub process_dafile($$)
 	# Get directory and basename of data file
 	($da_dir, $da_basename) = split_filename($da_filename);
 
-	# avoid files from .libs dirs 	 
-	if ($compat_libtool && $da_dir =~ m/(.*)\/\.libs$/) {
-		$source_dir = $1;
-	} else {
-		$source_dir = $da_dir;
+	$source_dir = $da_dir;
+	if (is_compat($COMPAT_MODE_LIBTOOL)) {
+		# Avoid files from .libs dirs 	 
+		$source_dir =~ s/\.libs$//;
 	}
 
 	if (-z $da_filename)
@@ -808,20 +965,27 @@ sub process_dafile($$)
 	# information about functions and their source code positions.
 	if ($gcov_version < $GCOV_VERSION_3_4_0)
 	{
-		if (defined($compatibility) && $compatibility eq $COMPAT_HAMMER)
+		if (is_compat($COMPAT_MODE_HAMMER))
 		{
-			($instr, $graph) = read_bbg($bb_filename, $base_dir);
+			($instr, $graph) = read_bbg($bb_filename);
 		}
 		else
 		{
-			($instr, $graph) = read_bb($bb_filename, $base_dir);
+			($instr, $graph) = read_bb($bb_filename);
 		}
 	} 
 	else
 	{
-		($instr, $graph) = read_gcno($bb_filename, $base_dir);
+		($instr, $graph) = read_gcno($bb_filename);
 	} 
 
+	# Try to find base directory automatically if requested by user
+	if ($rc_auto_base) {
+		$base_dir = find_base_from_graph($base_dir, $instr, $graph);
+	}
+
+	($instr, $graph) = adjust_graph_filenames($base_dir, $instr, $graph);
+
 	# Set $object_dir to real location of object files. This may differ
 	# from $da_dir if the graph file is just a link to the "real" object
 	# file location.
@@ -850,6 +1014,7 @@ sub process_dafile($$)
 	}
 
 	# Change to directory containing data files and apply GCOV
+	debug("chdir($base_dir)\n");
         chdir($base_dir);
 
 	if ($da_renamed)
@@ -905,7 +1070,7 @@ sub process_dafile($$)
 		else
 		{
 			# Append to output file
-			open(INFO_HANDLE, ">>$output_filename")
+			open(INFO_HANDLE, ">>", $output_filename)
 				or die("ERROR: cannot write to ".
 				       "$output_filename!\n");
 		}
@@ -913,7 +1078,7 @@ sub process_dafile($$)
 	else
 	{
 		# Open .info file for output
-		open(INFO_HANDLE, ">$da_filename.info")
+		open(INFO_HANDLE, ">", "$da_filename.info")
 			or die("ERROR: cannot create $da_filename.info!\n");
 	}
 
@@ -922,23 +1087,34 @@ sub process_dafile($$)
 
 	# Traverse the list of generated .gcov files and combine them into a
 	# single .info file
-	@unprocessed = keys(%{$instr});
 	foreach $gcov_file (sort(@gcov_list))
 	{
 		my $i;
 		my $num;
 
+		# Skip gcov file for gcc built-in code
+		next if ($gcov_file eq "<built-in>.gcov");
+
 		($source, $object) = read_gcov_header($gcov_file);
 
-		if (defined($source))
-		{
-			$source = solve_relative_path($base_dir, $source);
+		if (!defined($source)) {
+			# Derive source file name from gcov file name if
+			# header format could not be parsed
+			$source = $gcov_file;
+			$source =~ s/\.gcov$//;
+		}
+
+		$source = solve_relative_path($base_dir, $source);
+
+		if (defined($adjust_src_pattern)) {
+			# Apply transformation as specified by user
+			$source =~ s/$adjust_src_pattern/$adjust_src_replace/g;
 		}
 
 		# gcov will happily create output even if there's no source code
 		# available - this interferes with checksum creation so we need
 		# to pull the emergency brake here.
-		if (defined($source) && ! -r $source && $checksum)
+		if (! -r $source && $checksum)
 		{
 			if ($ignore[$ERROR_SOURCE])
 			{
@@ -949,8 +1125,7 @@ sub process_dafile($$)
 			die("ERROR: could not read source file $source\n");
 		}
 
-		@matches = match_filename(defined($source) ? $source :
-					  $gcov_file, keys(%{$instr}));
+		@matches = match_filename($source, keys(%{$instr}));
 
 		# Skip files that are not mentioned in the graph file
 		if (!@matches)
@@ -994,13 +1169,13 @@ sub process_dafile($$)
 						\@matches, \@gcov_content);
 		}
 
-		# Remove processed file from list
-		for ($index = scalar(@unprocessed) - 1; $index >= 0; $index--)
-		{
-			if ($unprocessed[$index] eq $source_filename)
-			{
-				splice(@unprocessed, $index, 1);
-				last;
+		# Skip external files if requested
+		if (!$opt_external) {
+			if (is_external($source_filename)) {
+				info("  ignoring data for external file ".
+				     "$source_filename\n");
+				unlink($gcov_file);
+				next;
 			}
 		}
 
@@ -1090,6 +1265,7 @@ sub process_dafile($$)
 			my ($line, $block, $branch, $taken) =
 				br_gvec_get($gcov_branches, $i);
 
+			$block = $BR_VEC_MAX if ($block < 0);
 			print(INFO_HANDLE "BRDA:$line,$block,$branch,$taken\n");
 			$br_found++;
 			$br_hit++ if ($taken ne '-' && $taken > 0);
@@ -1138,16 +1314,6 @@ sub process_dafile($$)
 		unlink($gcov_file);
 	}
 
-	# Check for files which show up in the graph file but were never
-	# processed
-	if (@unprocessed && @gcov_list)
-	{
-		foreach (@unprocessed)
-		{
-			warn("WARNING: no data found for $_\n");
-		}
-	}
-
 	if (!($output_filename && ($output_filename eq "-")))
 	{
 		close(INFO_HANDLE);
@@ -1168,8 +1334,40 @@ sub solve_relative_path($$)
 {
 	my $path = $_[0];
 	my $dir = $_[1];
+	my $volume;
+	my $directories;
+	my $filename;
+	my @dirs;			# holds path elements
 	my $result;
 
+	# Convert from Windows path to msys path
+	if( $^O eq "msys" )
+	{
+		# search for a windows drive letter at the beginning
+		($volume, $directories, $filename) = File::Spec::Win32->splitpath( $dir );
+		if( $volume ne '' )
+		{
+			my $uppercase_volume;
+			# transform c/d\../e/f\g to Windows style c\d\..\e\f\g
+			$dir = File::Spec::Win32->canonpath( $dir );
+			# use Win32 module to retrieve path components
+			# $uppercase_volume is not used any further
+			( $uppercase_volume, $directories, $filename ) = File::Spec::Win32->splitpath( $dir );
+			@dirs = File::Spec::Win32->splitdir( $directories );
+			
+			# prepend volume, since in msys C: is always mounted to /c
+			$volume =~ s|^([a-zA-Z]+):|/\L$1\E|;
+			unshift( @dirs, $volume );
+			
+			# transform to Unix style '/' path
+			$directories = File::Spec->catdir( @dirs );
+			$dir = File::Spec->catpath( '', $directories, $filename );
+		} else {
+			# eliminate '\' path separators
+			$dir = File::Spec->canonpath( $dir );
+		}
+	}
+
 	$result = $dir;
 	# Prepend path if not absolute
 	if ($dir =~ /^[^\/]/)
@@ -1182,6 +1380,10 @@ sub solve_relative_path($$)
 
 	# Remove .
 	$result =~ s/\/\.\//\//g;
+	$result =~ s/\/\.$/\//g;
+
+	# Remove trailing /
+	$result =~ s/\/$//g;
 
 	# Solve ..
 	while ($result =~ s/\/[^\/]+\/\.\.\//\//)
@@ -1266,7 +1468,7 @@ sub solve_ambiguous_match($$$)
 	{
 
 		# Compare file contents
-		open(SOURCE, $filename)
+		open(SOURCE, "<", $filename)
 			or die("ERROR: cannot read $filename!\n");
 
 		$no_match = 0;
@@ -1336,7 +1538,7 @@ sub read_gcov_header($)
 	my $object;
 	local *INPUT;
 
-	if (!open(INPUT, $_[0]))
+	if (!open(INPUT, "<", $_[0]))
 	{
 		if ($ignore_errors[$ERROR_GCOV])
 		{
@@ -1408,6 +1610,7 @@ sub br_gvec_get($$)
 	# Retrieve data from vector
 	$line	= vec($vec, $offset + $BR_LINE, $BR_VEC_WIDTH);
 	$block	= vec($vec, $offset + $BR_BLOCK, $BR_VEC_WIDTH);
+	$block = -1 if ($block == $BR_VEC_MAX);
 	$branch = vec($vec, $offset + $BR_BRANCH, $BR_VEC_WIDTH);
 	$taken	= vec($vec, $offset + $BR_TAKEN, $BR_VEC_WIDTH);
 
@@ -1435,6 +1638,7 @@ sub br_gvec_push($$$$$)
 
 	$vec = "" if (!defined($vec));
 	$offset = br_gvec_len($vec) * $BR_VEC_ENTRIES;
+	$block = $BR_VEC_MAX if $block < 0;
 
 	# Encode taken value into an integer
 	if ($taken eq "-") {
@@ -1484,11 +1688,13 @@ sub read_gcov_file($)
 	my $number;
 	my $exclude_flag = 0;
 	my $exclude_line = 0;
+	my $exclude_br_flag = 0;
+	my $exclude_branch = 0;
 	my $last_block = $UNNAMED_BLOCK;
 	my $last_line = 0;
 	local *INPUT;
 
-	if (!open(INPUT, $filename)) {
+	if (!open(INPUT, "<", $filename)) {
 		if ($ignore_errors[$ERROR_GCOV])
 		{
 			warn("WARNING: cannot read $filename!\n");
@@ -1508,11 +1714,15 @@ sub read_gcov_file($)
 			s/\015$//;
 
 			if (/^branch\s+(\d+)\s+taken\s+=\s+(\d+)/) {
+				next if (!$br_coverage);
 				next if ($exclude_line);
+				next if ($exclude_branch);
 				$branches = br_gvec_push($branches, $last_line,
 						$last_block, $1, $2);
 			} elsif (/^branch\s+(\d+)\s+never\s+executed/) {
+				next if (!$br_coverage);
 				next if ($exclude_line);
+				next if ($exclude_branch);
 				$branches = br_gvec_push($branches, $last_line,
 						$last_block, $1, '-');
 			}
@@ -1530,12 +1740,25 @@ sub read_gcov_file($)
 					} elsif (/$EXCL_START/) {
 						$exclude_flag = 1;
 					}
-					if (/$EXCL_LINE/ || $exclude_flag) {
+					if (/$excl_line/ || $exclude_flag) {
 						$exclude_line = 1;
 					} else {
 						$exclude_line = 0;
 					}
 				}
+				# Check for exclusion markers (branch exclude)
+				if (!$no_markers) {
+					if (/$EXCL_BR_STOP/) {
+						$exclude_br_flag = 0;
+					} elsif (/$EXCL_BR_START/) {
+						$exclude_br_flag = 1;
+					}
+					if (/$excl_br_line/ || $exclude_br_flag) {
+						$exclude_branch = 1;
+					} else {
+						$exclude_branch = 0;
+					}
+				}
 				# Source code execution data
 				if (/^\t\t(.*)$/)
 				{
@@ -1579,16 +1802,21 @@ sub read_gcov_file($)
 				$last_line = $2;
 				$last_block = $3;
 			} elsif (/^branch\s+(\d+)\s+taken\s+(\d+)/) {
+				next if (!$br_coverage);
 				next if ($exclude_line);
+				next if ($exclude_branch);
 				$branches = br_gvec_push($branches, $last_line,
 						$last_block, $1, $2);
 			} elsif (/^branch\s+(\d+)\s+never\s+executed/) {
+				next if (!$br_coverage);
 				next if ($exclude_line);
+				next if ($exclude_branch);
 				$branches = br_gvec_push($branches, $last_line,
 						$last_block, $1, '-');
 			}
-			elsif (/^function\s+(\S+)\s+called\s+(\d+)/)
+			elsif (/^function\s+(.+)\s+called\s+(\d+)\s+/)
 			{
+				next if (!$func_coverage);
 				if ($exclude_line) {
 					next;
 				}
@@ -1611,12 +1839,26 @@ sub read_gcov_file($)
 					} elsif (/$EXCL_START/) {
 						$exclude_flag = 1;
 					}
-					if (/$EXCL_LINE/ || $exclude_flag) {
+					if (/$excl_line/ || $exclude_flag) {
 						$exclude_line = 1;
 					} else {
 						$exclude_line = 0;
 					}
 				}
+				# Check for exclusion markers (branch exclude)
+				if (!$no_markers) {
+					if (/$EXCL_BR_STOP/) {
+						$exclude_br_flag = 0;
+					} elsif (/$EXCL_BR_START/) {
+						$exclude_br_flag = 1;
+					}
+					if (/$excl_br_line/ || $exclude_br_flag) {
+						$exclude_branch = 1;
+					} else {
+						$exclude_branch = 0;
+					}
+				}
+
 				# <exec count>:<line number>:<source code>
 				if ($line eq "0")
 				{
@@ -1636,7 +1878,7 @@ sub read_gcov_file($)
 						push(@result, 0);
 					} else {
 						# Check for zero count
-						if ($count eq "#####") {
+						if ($count =~ /^[#=]/) {
 							$count = 0;
 						}
 						push(@result, 1);
@@ -1649,7 +1891,7 @@ sub read_gcov_file($)
 	}
 
 	close(INPUT);
-	if ($exclude_flag) {
+	if ($exclude_flag || $exclude_br_flag) {
 		warn("WARNING: unterminated exclusion section in $filename\n");
 	}
 	return(\@result, $branches, \@functions);
@@ -1667,12 +1909,29 @@ sub get_gcov_version()
 	local *HANDLE;
 	my $version_string;
 	my $result;
+	my $pipe_next_line;
 
-	open(GCOV_PIPE, "$gcov_tool -v |")
+	open(GCOV_PIPE, "-|", "$gcov_tool --version")
 		or die("ERROR: cannot retrieve gcov version!\n");
 	$version_string = <GCOV_PIPE>;
+	# LLVM gcov keeps version information on the second line.
+	# For example, gcov --version yields:
+	# LLVM (http://llvm.org/):
+	#   LLVM version 3.4svn
+
+	$pipe_next_line = <GCOV_PIPE>;	
+	# In case version information is on first line.
+	# For example, with Xcode 7.0 gcov --version yields:
+	# Apple LLVM 7.0.0 (clang-700.0.65)
+
+	$version_string = $pipe_next_line if ($pipe_next_line && $version_string =~ /LLVM/);
 	close(GCOV_PIPE);
 
+	# Remove version information in parenthesis to cope with the following:
+	# - gcov (GCC) 4.4.7 20120313 (Red Hat 4.4.7-3)
+	# - gcov (crosstool-NG 1.18.0) 4.7.2
+	$version_string =~ s/\([^\)]*\)//g;
+
 	$result = 0;
 	if ($version_string =~ /(\d+)\.(\d+)(\.(\d+))?/)
 	{
@@ -1687,13 +1946,22 @@ sub get_gcov_version()
 			$result = $1 << 16 | $2 << 8;
 		}
 	}
-        if ($version_string =~ /suse/i && $result == 0x30303 ||
-            $version_string =~ /mandrake/i && $result == 0x30302)
+	if ($version_string =~ /LLVM/)
 	{
-		info("Using compatibility mode for GCC 3.3 (hammer)\n");
-		$compatibility = $COMPAT_HAMMER;
+		# Map LLVM versions to the version of GCC gcov which
+		# they emulate
+		if ($result >= 0x030400)
+		{
+			info("Found LLVM gcov version 3.4, which emulates gcov version 4.2\n");
+			$result = 0x040200;
+		}
+		else
+		{
+			warn("This version of LLVM's gcov is unknown.  Assuming it emulates GCC gcov version 4.2.\n");
+			$result = 0x040200;
+		}
 	}
-	return $result;
+	return ($result, $version_string);
 }
 
 
@@ -1756,13 +2024,14 @@ sub system_no_output($@)
 	local *OLD_STDOUT;
 
 	# Save old stdout and stderr handles
-	($mode & 1) && open(OLD_STDOUT, ">>&STDOUT");
-	($mode & 2) && open(OLD_STDERR, ">>&STDERR");
+	($mode & 1) && open(OLD_STDOUT, ">>&", "STDOUT");
+	($mode & 2) && open(OLD_STDERR, ">>&", "STDERR");
 
 	# Redirect to /dev/null
-	($mode & 1) && open(STDOUT, ">/dev/null");
-	($mode & 2) && open(STDERR, ">/dev/null");
+	($mode & 1) && open(STDOUT, ">", "/dev/null");
+	($mode & 2) && open(STDERR, ">", "/dev/null");
  
+	debug("system(".join(' ', @_).")\n");
 	system(@_);
 	$result = $?;
 
@@ -1771,8 +2040,8 @@ sub system_no_output($@)
 	($mode & 2) && close(STDERR);
 
 	# Restore old handles
-	($mode & 1) && open(STDOUT, ">>&OLD_STDOUT");
-	($mode & 2) && open(STDERR, ">>&OLD_STDERR");
+	($mode & 1) && open(STDOUT, ">>&", "OLD_STDOUT");
+	($mode & 2) && open(STDERR, ">>&", "OLD_STDERR");
  
 	return $result;
 }
@@ -1793,7 +2062,7 @@ sub read_config($)
 	my $value;
 	local *HANDLE;
 
-	if (!open(HANDLE, "<$filename"))
+	if (!open(HANDLE, "<", $filename))
 	{
 		warn("WARNING: cannot read configuration file $filename\n");
 		return undef;
@@ -1832,8 +2101,8 @@ sub read_config($)
 #   key_string => var_ref
 #
 # where KEY_STRING is a keyword and VAR_REF is a reference to an associated
-# variable. If the global configuration hash CONFIG contains a value for
-# keyword KEY_STRING, VAR_REF will be assigned the value for that keyword. 
+# variable. If the global configuration hashes CONFIG or OPT_RC contain a value
+# for keyword KEY_STRING, VAR_REF will be assigned the value for that keyword. 
 #
 
 sub apply_config($)
@@ -1842,8 +2111,9 @@ sub apply_config($)
 
 	foreach (keys(%{$ref}))
 	{
-		if (defined($config->{$_}))
-		{
+		if (defined($opt_rc{$_})) {
+			${$ref->{$_}} = $opt_rc{$_};
+		} elsif (defined($config->{$_})) {
 			${$ref->{$_}} = $config->{$_};
 		}
 	}
@@ -1865,7 +2135,7 @@ sub get_exclusion_data($)
 	my $flag = 0;
 	local *HANDLE;
 
-	if (!open(HANDLE, "<$filename")) {
+	if (!open(HANDLE, "<", $filename)) {
 		warn("WARNING: could not open $filename\n");
 		return undef;
 	}
@@ -1875,7 +2145,7 @@ sub get_exclusion_data($)
 		} elsif (/$EXCL_START/) {
 			$flag = 1;
 		}
-		if (/$EXCL_LINE/ || $flag) {
+		if (/$excl_line/ || $flag) {
 			$list{$.} = 1;
 		}
 	}
@@ -1989,12 +2259,7 @@ sub apply_exclusion_data($$)
 		}
 
 		# Store modified list
-		if (scalar(@new_data) > 0) {
-			$instr->{$filename} = \@new_data;
-		} else {
-			# All of this file was excluded
-			delete($instr->{$filename});
-		}
+		$instr->{$filename} = \@new_data;
 	}
 
 	return ($instr, $graph);
@@ -2023,11 +2288,10 @@ sub process_graphfile($$)
 	# Get directory and basename of data file
 	($graph_dir, $graph_basename) = split_filename($graph_filename);
 
-	# avoid files from .libs dirs 	 
-	if ($compat_libtool && $graph_dir =~ m/(.*)\/\.libs$/) {
-		$source_dir = $1;
-	} else {
-		$source_dir = $graph_dir;
+	$source_dir = $graph_dir;
+	if (is_compat($COMPAT_MODE_LIBTOOL)) {
+		# Avoid files from .libs dirs 	 
+		$source_dir =~ s/\.libs$//;
 	}
 
 	# Construct base_dir for current file
@@ -2040,22 +2304,36 @@ sub process_graphfile($$)
 		$base_dir = $source_dir;
 	}
 
+	# Ignore empty graph file (e.g. source file with no statement)
+	if (-z $graph_filename)
+	{
+		warn("WARNING: empty $graph_filename (skipped)\n");
+		return;
+	}
+
 	if ($gcov_version < $GCOV_VERSION_3_4_0)
 	{
-		if (defined($compatibility) && $compatibility eq $COMPAT_HAMMER)
+		if (is_compat($COMPAT_MODE_HAMMER))
 		{
-			($instr, $graph) = read_bbg($graph_filename, $base_dir);
+			($instr, $graph) = read_bbg($graph_filename);
 		}
 		else
 		{
-			($instr, $graph) = read_bb($graph_filename, $base_dir);
+			($instr, $graph) = read_bb($graph_filename);
 		}
 	} 
 	else
 	{
-		($instr, $graph) = read_gcno($graph_filename, $base_dir);
+		($instr, $graph) = read_gcno($graph_filename);
 	}
 
+	# Try to find base directory automatically if requested by user
+	if ($rc_auto_base) {
+		$base_dir = find_base_from_graph($base_dir, $instr, $graph);
+	}
+
+	($instr, $graph) = adjust_graph_filenames($base_dir, $instr, $graph);
+
 	if (!$no_markers) {
 		# Apply exclusion marker data to graph file data
 		($instr, $graph) = apply_exclusion_data($instr, $graph);
@@ -2071,7 +2349,7 @@ sub process_graphfile($$)
 		else
 		{
 			# Append to output file
-			open(INFO_HANDLE, ">>$output_filename")
+			open(INFO_HANDLE, ">>", $output_filename)
 				or die("ERROR: cannot write to ".
 				       "$output_filename!\n");
 		}
@@ -2079,7 +2357,7 @@ sub process_graphfile($$)
 	else
 	{
 		# Open .info file for output
-		open(INFO_HANDLE, ">$graph_filename.info")
+		open(INFO_HANDLE, ">", "$graph_filename.info")
 			or die("ERROR: cannot create $graph_filename.info!\n");
 	}
 
@@ -2091,9 +2369,18 @@ sub process_graphfile($$)
 		my $line;
 		my $linedata;
 
+		# Skip external files if requested
+		if (!$opt_external) {
+			if (is_external($filename)) {
+				info("  ignoring data for external file ".
+				     "$filename\n");
+				next;
+			}
+		}
+
 		print(INFO_HANDLE "SF:$filename\n");
 
-		if (defined($funcdata)) {
+		if (defined($funcdata) && $func_coverage) {
 			my @functions = sort {$funcdata->{$a}->[0] <=>
 					      $funcdata->{$b}->[0]}
 					     keys(%{$funcdata});
@@ -2194,26 +2481,36 @@ sub graph_expect($)
 }
 
 #
-# graph_read(handle, bytes[, description])
+# graph_read(handle, bytes[, description, peek])
 #
 # Read and return the specified number of bytes from handle. Return undef
-# if the number of bytes could not be read.
+# if the number of bytes could not be read. If PEEK is non-zero, reset
+# file position after read.
 #
 
-sub graph_read(*$;$)
+sub graph_read(*$;$$)
 {
-	my ($handle, $length, $desc) = @_;
+	my ($handle, $length, $desc, $peek) = @_;
 	my $data;
 	my $result;
+	my $pos;
 
 	graph_expect($desc);
+	if ($peek) {
+		$pos = tell($handle);
+		if ($pos == -1) {
+			warn("Could not get current file position: $!\n");
+			return undef;
+		}
+	}
 	$result = read($handle, $data, $length);
 	if ($debug) {
+		my $op = $peek ? "peek" : "read";
 		my $ascii = "";
 		my $hex = "";
 		my $i;
 
-		print(STDERR "DEBUG: read($length)=$result: ");
+		print(STDERR "DEBUG: $op($length)=$result: ");
 		for ($i = 0; $i < length($data); $i++) {
 			my $c = substr($data, $i, 1);;
 			my $n = ord($c);
@@ -2228,6 +2525,12 @@ sub graph_read(*$;$)
 		print(STDERR "$hex |$ascii|");
 		print(STDERR "\n");
 	}
+	if ($peek) {
+		if (!seek($handle, $pos, 0)) {
+			warn("Could not set file position: $!\n");
+			return undef;
+		}
+	}
 	if ($result != $length) {
 		return undef;
 	}
@@ -2252,6 +2555,27 @@ sub graph_skip(*$;$)
 }
 
 #
+# uniq(list)
+#
+# Return list without duplicate entries.
+#
+
+sub uniq(@)
+{
+	my (@list) = @_;
+	my @new_list;
+	my %known;
+
+	foreach my $item (@list) {
+		next if ($known{$item});
+		$known{$item} = 1;
+		push(@new_list, $item);
+	}
+
+	return @new_list;
+}
+
+#
 # sort_uniq(list)
 #
 # Return list in numerically ascending order and without duplicate entries.
@@ -2286,6 +2610,133 @@ sub sort_uniq_lex(@)
 }
 
 #
+# parent_dir(dir)
+#
+# Return parent directory for DIR. DIR must not contain relative path
+# components.
+#
+
+sub parent_dir($)
+{
+	my ($dir) = @_;
+	my ($v, $d, $f) = splitpath($dir, 1);
+	my @dirs = splitdir($d);
+
+	pop(@dirs);
+
+	return catpath($v, catdir(@dirs), $f);
+}
+
+#
+# find_base_from_graph(base_dir, instr, graph)
+#
+# Try to determine the base directory of the graph file specified by INSTR
+# and GRAPH. The base directory is the base for all relative filenames in
+# the graph file. It is defined by the current working directory at time
+# of compiling the source file.
+#
+# This function implements a heuristic which relies on the following
+# assumptions:
+# - all files used for compilation are still present at their location
+# - the base directory is either BASE_DIR or one of its parent directories
+# - files by the same name are not present in multiple parent directories
+#
+
+sub find_base_from_graph($$$)
+{
+	my ($base_dir, $instr, $graph) = @_;
+	my $old_base;
+	my $best_miss;
+	my $best_base;
+	my %rel_files;
+
+	# Determine list of relative paths
+	foreach my $filename (keys(%{$instr}), keys(%{$graph})) {
+		next if (file_name_is_absolute($filename));
+
+		$rel_files{$filename} = 1;
+	}
+
+	# Early exit if there are no relative paths
+	return $base_dir if (!%rel_files);
+
+	do {
+		my $miss = 0;
+
+		foreach my $filename (keys(%rel_files)) {
+			if (!-e solve_relative_path($base_dir, $filename)) {
+				$miss++;
+			}
+		}
+
+		debug("base_dir=$base_dir miss=$miss\n");
+
+		# Exit if we find an exact match with no misses
+		return $base_dir if ($miss == 0);
+
+		# No exact match, aim for the one with the least source file
+		# misses
+		if (!defined($best_base) || $miss < $best_miss) {
+			$best_base = $base_dir;
+			$best_miss = $miss;
+		}
+
+		# Repeat until there's no more parent directory
+		$old_base = $base_dir;
+		$base_dir = parent_dir($base_dir);
+	} while ($old_base ne $base_dir);
+
+	return $best_base;
+}
+
+#
+# adjust_graph_filenames(base_dir, instr, graph)
+#
+# Make relative paths in INSTR and GRAPH absolute and apply
+# geninfo_adjust_src_path setting to graph file data.
+#
+
+sub adjust_graph_filenames($$$)
+{
+	my ($base_dir, $instr, $graph) = @_;
+
+	foreach my $filename (keys(%{$instr})) {
+		my $old_filename = $filename;
+
+		# Convert to absolute canonical form
+		$filename = solve_relative_path($base_dir, $filename);
+
+		# Apply adjustment
+		if (defined($adjust_src_pattern)) {
+			$filename =~ s/$adjust_src_pattern/$adjust_src_replace/g;
+		}
+
+		if ($filename ne $old_filename) {
+			$instr->{$filename} = delete($instr->{$old_filename});
+		}
+	}
+
+	foreach my $filename (keys(%{$graph})) {
+		my $old_filename = $filename;
+
+		# Make absolute
+		# Convert to absolute canonical form
+		$filename = solve_relative_path($base_dir, $filename);
+
+		# Apply adjustment
+		if (defined($adjust_src_pattern)) {
+			$filename =~ s/$adjust_src_pattern/$adjust_src_replace/g;
+		}
+
+		if ($filename ne $old_filename) {
+			$graph->{$filename} = delete($graph->{$old_filename});
+		}
+	}
+
+	return ($instr, $graph);
+}
+
+#
 # graph_cleanup(graph)
 #
 # Remove entries for functions with no lines. Remove duplicate line numbers.
@@ -2310,7 +2761,7 @@ sub graph_cleanup($)
 				next;
 			}
 			# Normalize list
-			$per_file->{$function} = [ sort_uniq(@$lines) ];
+			$per_file->{$function} = [ uniq(@$lines) ];
 		}
 		if (scalar(keys(%{$per_file})) == 0) {
 			# Remove empty file
@@ -2454,6 +2905,7 @@ sub graph_add_order($$$)
 	push(@$list, $filename);
 	$fileorder->{$function} = $list;
 }
+
 #
 # read_bb_word(handle[, description])
 #
@@ -2513,7 +2965,7 @@ sub read_bb_string(*$)
 }
 
 #
-# read_bb(filename, base_dir)
+# read_bb(filename)
 #
 # Read the contents of the specified .bb file and return (instr, graph), where:
 #
@@ -2524,14 +2976,12 @@ sub read_bb_string(*$)
 #   file_data : function name -> line_data
 #   line_data : [ line1, line2, ... ]
 #
-# Relative filenames are converted to absolute form using base_dir as
-# base directory. See the gcov info pages of gcc 2.95 for a description of
-# the .bb file format.
+# See the gcov info pages of gcc 2.95 for a description of the .bb file format.
 #
 
-sub read_bb($$)
+sub read_bb($)
 {
-	my ($bb_filename, $base) = @_;
+	my ($bb_filename) = @_;
 	my $minus_one = 0x80000001;
 	my $minus_two = 0x80000002;
 	my $value;
@@ -2543,7 +2993,7 @@ sub read_bb($$)
 	my $graph;
 	local *HANDLE;
 
-	open(HANDLE, "<$bb_filename") or goto open_error;
+	open(HANDLE, "<", $bb_filename) or goto open_error;
 	binmode(HANDLE);
 	while (!eof(HANDLE)) {
 		$value = read_bb_value(*HANDLE, "data word");
@@ -2553,10 +3003,6 @@ sub read_bb($$)
 			graph_expect("filename");
 			$filename = read_bb_string(*HANDLE, $minus_one);
 			goto incomplete if (!defined($filename));
-			if ($filename ne "") {
-				$filename = solve_relative_path($base,
-								$filename);
-			}
 		} elsif ($value == $minus_two) {
 			# Function name
 			graph_expect("function name");
@@ -2647,16 +3093,15 @@ sub read_bbg_string(*)
 
 #
 # read_bbg_lines_record(handle, bbg_filename, bb, fileorder, filename,
-#                       function, base)
+#                       function)
 #
 # Read a bbg format lines record from handle and add the relevant data to
 # bb and fileorder. Return filename on success, undef on error.
 #
 
-sub read_bbg_lines_record(*$$$$$$)
+sub read_bbg_lines_record(*$$$$$)
 {
-	my ($handle, $bbg_filename, $bb, $fileorder, $filename, $function,
-	    $base) = @_;
+	my ($handle, $bbg_filename, $bb, $fileorder, $filename, $function) = @_;
 	my $string;
 	my $lineno;
 
@@ -2676,7 +3121,10 @@ sub read_bbg_lines_record(*$$$$$$)
 			if ($string eq "") {
 				return $filename;
 			}
-			$filename = solve_relative_path($base, $string);
+			$filename = $string;
+			if (!exists($bb->{$function}->{$filename})) {
+				$bb->{$function}->{$filename} = [];
+			}
 			next;
 		}
 		# Got an actual line number
@@ -2691,21 +3139,20 @@ sub read_bbg_lines_record(*$$$$$$)
 }
 
 #
-# read_bbg(filename, base_dir)
+# read_bbg(filename)
 #
 # Read the contents of the specified .bbg file and return the following mapping:
 #   graph:     filename -> file_data
 #   file_data: function name -> line_data
 #   line_data: [ line1, line2, ... ]
 #
-# Relative filenames are converted to absolute form using base_dir as
-# base directory. See the gcov-io.h file in the SLES 9 gcc 3.3.3 source code
-# for a description of the .bbg format.
+# See the gcov-io.h file in the SLES 9 gcc 3.3.3 source code for a description
+# of the .bbg format.
 #
 
-sub read_bbg($$)
+sub read_bbg($)
 {
-	my ($bbg_filename, $base) = @_;
+	my ($bbg_filename) = @_;
 	my $file_magic = 0x67626267;
 	my $tag_function = 0x01000000;
 	my $tag_lines = 0x01450000;
@@ -2720,7 +3167,7 @@ sub read_bbg($$)
 	my $graph;
 	local *HANDLE;
 
-	open(HANDLE, "<$bbg_filename") or goto open_error;
+	open(HANDLE, "<", $bbg_filename) or goto open_error;
 	binmode(HANDLE);
 	# Read magic
 	$word = read_bbg_value(*HANDLE, "file magic");
@@ -2752,7 +3199,7 @@ sub read_bbg($$)
 			# Read lines record
 			$filename = read_bbg_lines_record(HANDLE, $bbg_filename,
 					  $bb, $fileorder, $filename,
-					  $function, $base);
+					  $function);
 			goto incomplete if (!defined($filename));
 		} else {
 			# Skip record contents
@@ -2778,31 +3225,33 @@ magic_error:
 }
 
 #
-# read_gcno_word(handle[, description])
+# read_gcno_word(handle[, description, peek])
 #
 # Read and return a word in .gcno format.
 #
 
-sub read_gcno_word(*;$)
+sub read_gcno_word(*;$$)
 {
-	my ($handle, $desc) = @_;
+	my ($handle, $desc, $peek) = @_;
 
-	return graph_read($handle, 4, $desc);
+	return graph_read($handle, 4, $desc, $peek);
 }
 
 #
-# read_gcno_value(handle, big_endian[, description])
+# read_gcno_value(handle, big_endian[, description, peek])
 #
 # Read a word in .gcno format from handle and return its integer value
-# according to the specified endianness.
+# according to the specified endianness. If PEEK is non-zero, reset file
+# position after read.
 #
 
-sub read_gcno_value(*$;$)
+sub read_gcno_value(*$;$$)
 {
-	my ($handle, $big_endian, $desc) = @_;
+	my ($handle, $big_endian, $desc, $peek) = @_;
 	my $word;
+	my $pos;
 
-	$word = read_gcno_word($handle, $desc);
+	$word = read_gcno_word($handle, $desc, $peek);
 	return undef if (!defined($word));
 	if ($big_endian) {
 		return unpack("N", $word);
@@ -2841,16 +3290,16 @@ sub read_gcno_string(*$)
 
 #
 # read_gcno_lines_record(handle, gcno_filename, bb, fileorder, filename,
-#                        function, base, big_endian)
+#                        function, big_endian)
 #
 # Read a gcno format lines record from handle and add the relevant data to
 # bb and fileorder. Return filename on success, undef on error.
 #
 
-sub read_gcno_lines_record(*$$$$$$$)
+sub read_gcno_lines_record(*$$$$$$)
 {
 	my ($handle, $gcno_filename, $bb, $fileorder, $filename, $function,
-	    $base, $big_endian) = @_;
+	    $big_endian) = @_;
 	my $string;
 	my $lineno;
 
@@ -2870,7 +3319,10 @@ sub read_gcno_lines_record(*$$$$$$$)
 			if ($string eq "") {
 				return $filename;
 			}
-			$filename = solve_relative_path($base, $string);
+			$filename = $string;
+			if (!exists($bb->{$function}->{$filename})) {
+				$bb->{$function}->{$filename} = [];
+			}
 			next;
 		}
 		# Got an actual line number
@@ -2886,7 +3338,52 @@ sub read_gcno_lines_record(*$$$$$$$)
 }
 
 #
-# read_gcno_function_record(handle, graph, base, big_endian)
+# determine_gcno_split_crc(handle, big_endian, rec_length)
+#
+# Determine if HANDLE refers to a .gcno file with a split checksum function
+# record format. Return non-zero in case of split checksum format, zero
+# otherwise, undef in case of read error.
+#
+
+sub determine_gcno_split_crc($$$)
+{
+	my ($handle, $big_endian, $rec_length) = @_;
+	my $strlen;
+	my $overlong_string;
+
+	return 1 if ($gcov_version >= $GCOV_VERSION_4_7_0);
+	return 1 if (is_compat($COMPAT_MODE_SPLIT_CRC));
+
+	# Heuristic:
+	# Decide format based on contents of next word in record:
+	# - pre-gcc 4.7
+	#   This is the function name length / 4 which should be
+	#   less than the remaining record length
+	# - gcc 4.7
+	#   This is a checksum, likely with high-order bits set,
+	#   resulting in a large number
+	$strlen = read_gcno_value($handle, $big_endian, undef, 1);
+	return undef if (!defined($strlen));
+	$overlong_string = 1 if ($strlen * 4 >= $rec_length - 12);
+
+	if ($overlong_string) {
+		if (is_compat_auto($COMPAT_MODE_SPLIT_CRC)) {
+			info("Auto-detected compatibility mode for split ".
+			     "checksum .gcno file format\n");
+
+			return 1;
+		} else {
+			# Sanity check
+			warn("Found overlong string in function record: ".
+			     "try '--compat split_crc'\n");
+		}
+	}
+
+	return 0;
+}
+
+#
+# read_gcno_function_record(handle, graph, big_endian, rec_length)
 #
 # Read a gcno format function record from handle and add the relevant data
 # to graph. Return (filename, function) on success, undef on error. 
@@ -2894,7 +3391,7 @@ sub read_gcno_lines_record(*$$$$$$$)
 
 sub read_gcno_function_record(*$$$$)
 {
-	my ($handle, $bb, $fileorder, $base, $big_endian) = @_;
+	my ($handle, $bb, $fileorder, $big_endian, $rec_length) = @_;
 	my $filename;
 	my $function;
 	my $lineno;
@@ -2903,6 +3400,14 @@ sub read_gcno_function_record(*$$$$)
 	graph_expect("function record");
 	# Skip ident and checksum
 	graph_skip($handle, 8, "function ident and checksum") or return undef;
+	# Determine if this is a function record with split checksums
+	if (!defined($gcno_split_crc)) {
+		$gcno_split_crc = determine_gcno_split_crc($handle, $big_endian,
+							   $rec_length);
+		return undef if (!defined($gcno_split_crc));
+	}
+	# Skip cfg checksum word in case of split checksums
+	graph_skip($handle, 4, "function cfg checksum") if ($gcno_split_crc);
 	# Read function name
 	graph_expect("function name");
 	$function = read_gcno_string($handle, $big_endian);
@@ -2911,7 +3416,6 @@ sub read_gcno_function_record(*$$$$)
 	graph_expect("filename");
 	$filename = read_gcno_string($handle, $big_endian);
 	return undef if (!defined($filename));
-	$filename = solve_relative_path($base, $filename);
 	# Read first line number
 	$lineno = read_gcno_value($handle, $big_endian, "initial line number");
 	return undef if (!defined($lineno));
@@ -2923,7 +3427,7 @@ sub read_gcno_function_record(*$$$$)
 }
 
 #
-# read_gcno(filename, base_dir)
+# read_gcno(filename)
 #
 # Read the contents of the specified .gcno file and return the following
 # mapping:
@@ -2931,14 +3435,13 @@ sub read_gcno_function_record(*$$$$)
 #   file_data: function name -> line_data
 #   line_data: [ line1, line2, ... ]
 #
-# Relative filenames are converted to absolute form using base_dir as
-# base directory. See the gcov-io.h file in the gcc 3.3 source code
-# for a description of the .gcno format.
+# See the gcov-io.h file in the gcc 3.3 source code for a description of
+# the .gcno format.
 #
 
-sub read_gcno($$)
+sub read_gcno($)
 {
-	my ($gcno_filename, $base) = @_;
+	my ($gcno_filename) = @_;
 	my $file_magic = 0x67636e6f;
 	my $tag_function = 0x01000000;
 	my $tag_lines = 0x01450000;
@@ -2952,9 +3455,11 @@ sub read_gcno($$)
 	my $fileorder = {};
 	my $instr;
 	my $graph;
+	my $filelength;
 	local *HANDLE;
 
-	open(HANDLE, "<$gcno_filename") or goto open_error;
+	open(HANDLE, "<", $gcno_filename) or goto open_error;
+	$filelength = (stat(HANDLE))[7];
 	binmode(HANDLE);
 	# Read magic
 	$word = read_gcno_word(*HANDLE, "file magic");
@@ -2986,16 +3491,25 @@ sub read_gcno($$)
 		$next_pos = tell(HANDLE);
 		goto tell_error if ($next_pos == -1);
 		$next_pos += $length;
+		# Catch garbage at the end of a gcno file
+		if ($next_pos > $filelength) {
+			debug("Overlong record: file_length=$filelength ".
+			      "rec_length=$length\n");
+			warn("WARNING: $gcno_filename: Overlong record at end ".
+			     "of file!\n");
+			last;
+		}
 		# Process record
 		if ($tag == $tag_function) {
 			($filename, $function) = read_gcno_function_record(
-				*HANDLE, $bb, $fileorder, $base, $big_endian);
+				*HANDLE, $bb, $fileorder, $big_endian,
+				$length);
 			goto incomplete if (!defined($function));
 		} elsif ($tag == $tag_lines) {
 			# Read lines record
 			$filename = read_gcno_lines_record(*HANDLE,
 					$gcno_filename, $bb, $fileorder,
-					$filename, $function, $base,
+					$filename, $function,
 					$big_endian);
 			goto incomplete if (!defined($filename));
 		} else {
@@ -3053,16 +3567,236 @@ sub get_gcov_capabilities()
 {
 	my $help = `$gcov_tool --help`;
 	my %capabilities;
+	my %short_option_translations = (
+		'a' => 'all-blocks',
+		'b' => 'branch-probabilities',
+		'c' => 'branch-counts',
+		'f' => 'function-summaries',
+		'h' => 'help',
+		'l' => 'long-file-names',
+		'n' => 'no-output',
+		'o' => 'object-directory',
+		'p' => 'preserve-paths',
+		'u' => 'unconditional-branches',
+		'v' => 'version',
+	);
 
 	foreach (split(/\n/, $help)) {
-		next if (!/--(\S+)/);
-		next if ($1 eq 'help');
-		next if ($1 eq 'version');
-		next if ($1 eq 'object-directory');
+		my $capability;
+		if (/--(\S+)/) {
+			$capability = $1;
+		} else {
+			# If the line provides a short option, translate it.
+			next if (!/^\s*-(\S)\s/);
+			$capability = $short_option_translations{$1};
+			next if not defined($capability);
+		}
+		next if ($capability eq 'help');
+		next if ($capability eq 'version');
+		next if ($capability eq 'object-directory');
 
-		$capabilities{$1} = 1;
-		debug("gcov has capability '$1'\n");
+		$capabilities{$capability} = 1;
+		debug("gcov has capability '$capability'\n");
 	}
 
 	return \%capabilities;
 }
+
+#
+# parse_ignore_errors(@ignore_errors)
+#
+# Parse user input about which errors to ignore.
+#
+
+sub parse_ignore_errors(@)
+{
+	my (@ignore_errors) = @_;
+	my @items;
+	my $item;
+
+	return if (!@ignore_errors);
+
+	foreach $item (@ignore_errors) {
+		$item =~ s/\s//g;
+		if ($item =~ /,/) {
+			# Split and add comma-separated parameters
+			push(@items, split(/,/, $item));
+		} else {
+			# Add single parameter
+			push(@items, $item);
+		}
+	}
+	foreach $item (@items) {
+		my $item_id = $ERROR_ID{lc($item)};
+
+		if (!defined($item_id)) {
+			die("ERROR: unknown argument for --ignore-errors: ".
+			    "$item\n");
+		}
+		$ignore[$item_id] = 1;
+	}
+}
+
+#
+# is_external(filename)
+#
+# Determine if a file is located outside of the specified data directories.
+#
+
+sub is_external($)
+{
+	my ($filename) = @_;
+	my $dir;
+
+	foreach $dir (@internal_dirs) {
+		return 0 if ($filename =~ /^\Q$dir\/\E/);
+	}
+	return 1;
+}
+
+#
+# compat_name(mode)
+#
+# Return the name of compatibility mode MODE.
+#
+
+sub compat_name($)
+{
+	my ($mode) = @_;
+	my $name = $COMPAT_MODE_TO_NAME{$mode};
+
+	return $name if (defined($name));
+
+	return "<unknown>";
+}
+
+#
+# parse_compat_modes(opt)
+#
+# Determine compatibility mode settings.
+#
+
+sub parse_compat_modes($)
+{
+	my ($opt) = @_;
+	my @opt_list;
+	my %specified;
+
+	# Initialize with defaults
+	%compat_value = %COMPAT_MODE_DEFAULTS;
+
+	# Add old style specifications
+	if (defined($opt_compat_libtool)) {
+		$compat_value{$COMPAT_MODE_LIBTOOL} =
+			$opt_compat_libtool ? $COMPAT_VALUE_ON
+					    : $COMPAT_VALUE_OFF;
+	}
+
+	# Parse settings
+	if (defined($opt)) {
+		@opt_list = split(/\s*,\s*/, $opt);
+	}
+	foreach my $directive (@opt_list) {
+		my ($mode, $value);
+
+		# Either
+		#   mode=off|on|auto or
+		#   mode (implies on)
+		if ($directive !~ /^(\w+)=(\w+)$/ &&
+		    $directive !~ /^(\w+)$/) {
+			die("ERROR: Unknown compatibility mode specification: ".
+			    "$directive!\n");
+		}
+		# Determine mode
+		$mode = $COMPAT_NAME_TO_MODE{lc($1)};
+		if (!defined($mode)) {
+			die("ERROR: Unknown compatibility mode '$1'!\n");
+		}
+		$specified{$mode} = 1;
+		# Determine value
+		if (defined($2)) {
+			$value = $COMPAT_NAME_TO_VALUE{lc($2)};
+			if (!defined($value)) {
+				die("ERROR: Unknown compatibility mode ".
+				    "value '$2'!\n");
+			}
+		} else {
+			$value = $COMPAT_VALUE_ON;
+		}
+		$compat_value{$mode} = $value;
+	}
+	# Perform auto-detection
+	foreach my $mode (sort(keys(%compat_value))) {
+		my $value = $compat_value{$mode};
+		my $is_autodetect = "";
+		my $name = compat_name($mode);
+
+		if ($value == $COMPAT_VALUE_AUTO) {
+			my $autodetect = $COMPAT_MODE_AUTO{$mode};
+
+			if (!defined($autodetect)) {
+				die("ERROR: No auto-detection for ".
+				    "mode '$name' available!\n");
+			}
+
+			if (ref($autodetect) eq "CODE") {
+				$value = &$autodetect();
+				$compat_value{$mode} = $value;
+				$is_autodetect = " (auto-detected)";
+			}
+		}
+
+		if ($specified{$mode}) {
+			if ($value == $COMPAT_VALUE_ON) {
+				info("Enabling compatibility mode ".
+				     "'$name'$is_autodetect\n");
+			} elsif ($value == $COMPAT_VALUE_OFF) {
+				info("Disabling compatibility mode ".
+				     "'$name'$is_autodetect\n");
+			} else {
+				info("Using delayed auto-detection for ".
+				     "compatibility mode ".
+				     "'$name'\n");
+			}
+		}
+	}
+}
+
+sub compat_hammer_autodetect()
+{
+        if ($gcov_version_string =~ /suse/i && $gcov_version == 0x30303 ||
+            $gcov_version_string =~ /mandrake/i && $gcov_version == 0x30302)
+	{
+		info("Auto-detected compatibility mode for GCC 3.3 (hammer)\n");
+		return $COMPAT_VALUE_ON;
+	}
+	return $COMPAT_VALUE_OFF;
+}
+
+#
+# is_compat(mode)
+#
+# Return non-zero if compatibility mode MODE is enabled.
+#
+
+sub is_compat($)
+{
+	my ($mode) = @_;
+
+	return 1 if ($compat_value{$mode} == $COMPAT_VALUE_ON);
+	return 0;
+}
+
+#
+# is_compat_auto(mode)
+#
+# Return non-zero if compatibility mode MODE is set to auto-detect.
+#
+
+sub is_compat_auto($)
+{
+	my ($mode) = @_;
+
+	return 1 if ($compat_value{$mode} == $COMPAT_VALUE_AUTO);
+	return 0;
+}
diff --git a/3rdParty/LCov/genpng b/3rdParty/LCov/genpng
index 7fe9dfe..55e013e 100755
--- a/3rdParty/LCov/genpng
+++ b/3rdParty/LCov/genpng
@@ -22,7 +22,7 @@
 #   This script creates an overview PNG image of a source code file by
 #   representing each source code character by a single pixel.
 #
-#   Note that the PERL module GD.pm is required for this script to work.
+#   Note that the Perl module GD.pm is required for this script to work.
 #   It may be obtained from http://www.cpan.org
 #
 # History:
@@ -32,10 +32,12 @@
 use strict;
 use File::Basename; 
 use Getopt::Long;
+use Cwd qw/abs_path/;
 
 
 # Constants
-our $lcov_version	= 'LCOV version 1.9';
+our $tool_dir		= abs_path(dirname($0));
+our $lcov_version	= "LCOV version 1.12";
 our $lcov_url		= "http://ltp.sourceforge.net/coverage/lcov.php";
 our $tool_name		= basename($0);
 
@@ -53,9 +55,6 @@ sub genpng_die_handler($);
 # Code entry point
 #
 
-# Prettify version string
-$lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
-
 # Check whether required module GD.pm is installed
 if (check_and_load_module("GD"))
 {
@@ -182,7 +181,7 @@ sub genpng_process_file($$$$)
 	local *HANDLE;
 	my @source;
 
-	open(HANDLE, "<$filename")
+	open(HANDLE, "<", $filename)
 		or die("ERROR: cannot open $filename!\n");
 
 	# Check for .gcov filename extension
@@ -238,7 +237,7 @@ sub gen_png($$$@)
 	my $overview_width = shift(@_);	# Imagewidth for image
 	my $tab_size = shift(@_);	# Replacement string for tab signs
 	my @source = @_;	# Source code as passed via argument 2
-	my $height = scalar(@source);	# Height as define by source size
+	my $height;		# Height as define by source size
 	my $overview;		# Source code overview image data
 	my $col_plain_back;	# Color for overview background
 	my $col_plain_text;	# Color for uninstrumented text
@@ -261,6 +260,11 @@ sub gen_png($$$@)
 	my $replacement;	# Replacement string for tabulator chars
 	local *PNG_HANDLE;	# Handle for output PNG file
 
+	# Handle empty source files
+	if (!@source) {
+		@source = ( "" );
+	}
+	$height = scalar(@source);
 	# Create image
 	$overview = new GD::Image($overview_width, $height)
 		or die("ERROR: cannot allocate overview image!\n");
@@ -362,7 +366,7 @@ sub gen_png($$$@)
 	}
 
 	# Write PNG file
-	open (PNG_HANDLE, ">$filename")
+	open (PNG_HANDLE, ">", $filename)
 		or die("ERROR: cannot write png file $filename!\n");
 	binmode(*PNG_HANDLE);
 	print(PNG_HANDLE $overview->png());
diff --git a/3rdParty/LCov/lcov b/3rdParty/LCov/lcov
index 4e392ff..7760ba2 100755
--- a/3rdParty/LCov/lcov
+++ b/3rdParty/LCov/lcov
@@ -1,6 +1,6 @@
 #!/usr/bin/perl -w
 #
-#   Copyright (c) International Business Machines  Corp., 2002,2010
+#   Copyright (c) International Business Machines  Corp., 2002,2012
 #
 #   This program is free software;  you can redistribute it and/or modify
 #   it under the terms of the GNU General Public License as published by
@@ -71,7 +71,8 @@ use Cwd qw /abs_path getcwd/;
 
 
 # Global constants
-our $lcov_version	= 'LCOV version 1.9';
+our $tool_dir		= abs_path(dirname($0));
+our $lcov_version	= "LCOV version 1.12";
 our $lcov_url		= "http://ltp.sourceforge.net/coverage/lcov.php";
 our $tool_name		= basename($0);
 
@@ -93,6 +94,7 @@ our $BR_BRANCH		= 1;
 our $BR_TAKEN		= 2;
 our $BR_VEC_ENTRIES	= 3;
 our $BR_VEC_WIDTH	= 32;
+our $BR_VEC_MAX		= vec(pack('b*', 1 x $BR_VEC_WIDTH), 0, $BR_VEC_WIDTH);
 
 # Branch data combination types
 our $BR_SUB = 0;
@@ -139,6 +141,8 @@ sub lcov_geninfo(@);
 sub create_package($$$;$);
 sub get_func_found_and_hit($);
 sub br_ivec_get($$);
+sub summary();
+sub rate($$;$$$);
 
 # Global variables & initialization
 our @directory;		# Specifies where to get coverage data from
@@ -168,7 +172,7 @@ our $no_checksum;	# If set, don't calculate a checksum for each line
 our $compat_libtool;	# If set, indicates that libtool mode is to be enabled
 our $no_compat_libtool;	# If set, indicates that libtool mode is to be disabled
 our $gcov_tool;
-our $ignore_errors;
+our @opt_ignore_errors;
 our $initial;
 our $no_recursion = 0;
 our $to_package;
@@ -177,7 +181,6 @@ our $maxdepth;
 our $no_markers;
 our $config;		# Configuration file contents
 chomp($cwd);
-our $tool_dir = dirname($0);	# Directory where genhtml tool is installed
 our @temp_dirs;
 our $gcov_gkv;		# gcov kernel support version found on machine
 our $opt_derive_func_data;
@@ -186,12 +189,20 @@ our $opt_list_full_path;
 our $opt_no_list_full_path;
 our $opt_list_width = 80;
 our $opt_list_truncate_max = 20;
+our $opt_external;
+our $opt_no_external;
+our $opt_config_file;
+our %opt_rc;
+our @opt_summary;
+our $opt_compat;
 our $ln_overall_found;
 our $ln_overall_hit;
 our $fn_overall_found;
 our $fn_overall_hit;
 our $br_overall_found;
 our $br_overall_hit;
+our $func_coverage = 1;
+our $br_coverage = 0;
 
 
 #
@@ -203,17 +214,29 @@ $SIG{__DIE__} = \&die_handler;
 $SIG{'INT'} = \&abort_handler;
 $SIG{'QUIT'} = \&abort_handler;
 
-# Prettify version string
-$lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
+# Check command line for a configuration file name
+Getopt::Long::Configure("pass_through", "no_auto_abbrev");
+GetOptions("config-file=s" => \$opt_config_file,
+	   "rc=s%" => \%opt_rc);
+Getopt::Long::Configure("default");
 
-# Add current working directory if $tool_dir is not already an absolute path
-if (! ($tool_dir =~ /^\/(.*)$/))
 {
-	$tool_dir = "$cwd/$tool_dir";
+	# Remove spaces around rc options
+	my %new_opt_rc;
+
+	while (my ($key, $value) = each(%opt_rc)) {
+		$key =~ s/^\s+|\s+$//g;
+		$value =~ s/^\s+|\s+$//g;
+
+		$new_opt_rc{$key} = $value;
+	}
+	%opt_rc = %new_opt_rc;
 }
 
 # Read configuration file if available
-if (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
+if (defined($opt_config_file)) {
+	$config = read_config($opt_config_file);
+} elsif (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
 {
 	$config = read_config($ENV{"HOME"}."/.lcovrc");
 }
@@ -222,15 +245,17 @@ elsif (-r "/etc/lcovrc")
 	$config = read_config("/etc/lcovrc");
 }
 
-if ($config)
+if ($config || %opt_rc)
 {
-	# Copy configuration file values to variables
+	# Copy configuration file and --rc values to variables
 	apply_config({
 		"lcov_gcov_dir"		=> \$gcov_dir,
 		"lcov_tmp_dir"		=> \$tmp_dir,
 		"lcov_list_full_path"	=> \$opt_list_full_path,
 		"lcov_list_width"	=> \$opt_list_width,
 		"lcov_list_truncate_max"=> \$opt_list_truncate_max,
+		"lcov_branch_coverage"	=> \$br_coverage,
+		"lcov_function_coverage"=> \$func_coverage,
 	});
 }
 
@@ -259,7 +284,7 @@ if (!GetOptions("directory|d|di=s" => \@directory,
 		"compat-libtool" => \$compat_libtool,
 		"no-compat-libtool" => \$no_compat_libtool,
 		"gcov-tool=s" => \$gcov_tool,
-		"ignore-errors=s" => \$ignore_errors,
+		"ignore-errors=s" => \@opt_ignore_errors,
 		"initial|i" => \$initial,
 		"no-recursion" => \$no_recursion,
 		"to-package=s" => \$to_package,
@@ -269,6 +294,12 @@ if (!GetOptions("directory|d|di=s" => \@directory,
 		"debug" => \$opt_debug,
 		"list-full-path" => \$opt_list_full_path,
 		"no-list-full-path" => \$opt_no_list_full_path,
+		"external" => \$opt_external,
+		"no-external" => \$opt_no_external,
+		"summary=s" => \@opt_summary,
+		"compat=s" => \$opt_compat,
+		"config-file=s" => \$opt_config_file,
+		"rc=s%" => \%opt_rc,
 		))
 {
 	print(STDERR "Use $tool_name --help to get usage information\n");
@@ -294,6 +325,11 @@ else
 		$opt_list_full_path = ($opt_no_list_full_path ? 0 : 1);
 		$opt_no_list_full_path = undef;
 	}
+
+	if (defined($opt_no_external)) {
+		$opt_external = 0;
+		$opt_no_external = undef;
+	}
 }
 
 # Check for help option
@@ -341,7 +377,7 @@ else
 check_options();
 
 # Only --extract, --remove and --diff allow unnamed parameters
-if (@ARGV && !($extract || $remove || $diff))
+if (@ARGV && !($extract || $remove || $diff || @opt_summary))
 {
 	die("Extra parameter found: '".join(" ", @ARGV)."'\n".
 	    "Use $tool_name --help to get usage information\n");
@@ -429,6 +465,12 @@ elsif ($diff)
 	 $fn_overall_found, $fn_overall_hit,
 	 $br_overall_found, $br_overall_hit) = diff();
 }
+elsif (@opt_summary)
+{
+	($ln_overall_found, $ln_overall_hit,
+	 $fn_overall_found, $fn_overall_hit,
+	 $br_overall_found, $br_overall_hit) = summary();
+}
 
 temp_cleanup();
 
@@ -471,6 +513,7 @@ Operation:
   -r, --remove FILE PATTERN       Remove files matching PATTERN from FILE
   -l, --list FILE                 List contents of tracefile FILE
       --diff FILE DIFF            Transform tracefile FILE according to DIFF
+      --summary FILE              Show summary coverage data for tracefiles
 
 Options:
   -i, --initial                   Capture initial zero coverage data
@@ -493,6 +536,10 @@ Options:
       --no-markers                Ignore exclusion markers in source code
       --derive-func-data          Generate function data from line data
       --list-full-path            Print full path during a list operation
+      --(no-)external             Include (ignore) data for external files
+      --config-file FILENAME      Specify configuration file location
+      --rc SETTING=VALUE          Override configuration file setting
+      --compat MODE=on|off|auto   Set compat MODE (libtool, hammer, split_crc)
 
 For more information see: $lcov_url
 END_OF_USAGE
@@ -518,17 +565,18 @@ sub check_options()
 	$remove && $i++;
 	$list && $i++;
 	$diff && $i++;
+	@opt_summary && $i++;
 	
 	if ($i == 0)
 	{
-		die("Need one of the options -z, -c, -a, -e, -r, -l or ".
-		    "--diff\n".
+		die("Need one of options -z, -c, -a, -e, -r, -l, ".
+		    "--diff or --summary\n".
 		    "Use $tool_name --help to get usage information\n");
 	}
 	elsif ($i > 1)
 	{
-		die("ERROR: only one of -z, -c, -a, -e, -r, -l or ".
-		    "--diff allowed!\n".
+		die("ERROR: only one of -z, -c, -a, -e, -r, -l, ".
+		    "--diff or --summary allowed!\n".
 		    "Use $tool_name --help to get usage information\n");
 	}
 }
@@ -551,7 +599,7 @@ sub userspace_reset()
 	{
 		info("Deleting all .da files in $current_dir".
 		     ($no_recursion?"\n":" and subdirectories\n"));
-		@file_list = `find "$current_dir" $maxdepth $follow -name \\*\\.da -o -name \\*\\.gcda -type f 2>/dev/null`;
+		@file_list = `find "$current_dir" $maxdepth $follow -name \\*\\.da -type f -o -name \\*\\.gcda -type f 2>/dev/null`;
 		chomp(@file_list);
 		foreach (@file_list)
 		{
@@ -613,7 +661,7 @@ sub kernel_reset()
 	} else {
 		die("ERROR: no reset control found in $gcov_dir\n");
 	}
-	open(HANDLE, ">$reset_file") or
+	open(HANDLE, ">", $reset_file) or
 		die("ERROR: cannot write to $reset_file!\n");
 	print(HANDLE "0");
 	close(HANDLE);
@@ -635,10 +683,10 @@ sub lcov_copy_single($$)
 	local $/;
 	local *HANDLE;
 
-	open(HANDLE, "<$from") or die("ERROR: cannot read $from: $!\n");
+	open(HANDLE, "<", $from) or die("ERROR: cannot read $from: $!\n");
 	$content = <HANDLE>;
 	close(HANDLE);
-	open(HANDLE, ">$to") or die("ERROR: cannot write $from: $!\n");
+	open(HANDLE, ">", $to) or die("ERROR: cannot write $from: $!\n");
 	if (defined($content)) {
 		print(HANDLE $content);
 	}
@@ -798,9 +846,11 @@ sub lcov_geninfo(@)
 	{
 		@param = (@param, "--gcov-tool", $gcov_tool);
 	}
-	if ($ignore_errors)
-	{
-		@param = (@param, "--ignore-errors", $ignore_errors);
+	foreach (@opt_ignore_errors) {
+		@param = (@param, "--ignore-errors", $_);
+	}
+	if ($no_recursion) {
+		@param = (@param, "--no-recursion");
 	}
 	if ($initial)
 	{
@@ -818,6 +868,26 @@ sub lcov_geninfo(@)
 	{
 		@param = (@param, "--debug");
 	}
+	if (defined($opt_external) && $opt_external)
+	{
+		@param = (@param, "--external");
+	}
+	if (defined($opt_external) && !$opt_external)
+	{
+		@param = (@param, "--no-external");
+	}
+	if (defined($opt_compat)) {
+		@param = (@param, "--compat", $opt_compat);
+	}
+	if (%opt_rc) {
+		foreach my $key (keys(%opt_rc)) {
+			@param = (@param, "--rc", "$key=".$opt_rc{$key});
+		}
+	}
+	if (defined($opt_config_file)) {
+		@param = (@param, "--config-file", $opt_config_file);
+	}
+
 	system(@param) and exit($? >> 8);
 }
 
@@ -834,7 +904,7 @@ sub read_file($)
 	local $\;
 	local *HANDLE;
 
-	open(HANDLE, "<$filename") || return undef;
+	open(HANDLE, "<", $filename) || return undef;
 	$content = <HANDLE>;
 	close(HANDLE);
 
@@ -860,17 +930,21 @@ sub get_package($)
 	local *HANDLE;
 
 	info("Reading package $file:\n");
-	info("  data directory .......: $dir\n");
 	$file = abs_path($file);
 	chdir($dir);
-	open(HANDLE, "tar xvfz $file 2>/dev/null|")
+	open(HANDLE, "-|", "tar xvfz '$file' 2>/dev/null")
 		or die("ERROR: could not process package $file\n");
+	$count = 0;
 	while (<HANDLE>) {
 		if (/\.da$/ || /\.gcda$/) {
 			$count++;
 		}
 	}
 	close(HANDLE);
+	if ($count == 0) {
+		die("ERROR: no data file found in package $file\n");
+	}
+	info("  data directory .......: $dir\n");
 	$build = read_file("$dir/$pkg_build_file");
 	if (defined($build)) {
 		info("  build directory ......: $build\n");
@@ -904,7 +978,7 @@ sub write_file($$)
 	my ($filename, $content) = @_;
 	local *HANDLE;
 
-	open(HANDLE, ">$filename") || return 0;
+	open(HANDLE, ">", $filename) || return 0;
 	print(HANDLE $content);
 	close(HANDLE) || return 0;
 
@@ -922,7 +996,7 @@ sub count_package_data($)
 	local *HANDLE;
 	my $count = 0;
 
-	open(HANDLE, "tar tfz $filename|") or return undef;
+	open(HANDLE, "-|", "tar tfz '$filename'") or return undef;
 	while (<HANDLE>) {
 		if (/\.da$/ || /\.gcda$/) {
 			$count++;
@@ -944,6 +1018,10 @@ sub create_package($$$;$)
 	my ($file, $dir, $build, $gkv) = @_;
 	my $cwd = getcwd();
 
+	# Check for availability of tar tool first
+	system("tar --help > /dev/null")
+		and die("ERROR: tar command not available\n");
+
 	# Print information about the package
 	info("Creating package $file:\n");
 	info("  data directory .......: $dir\n");
@@ -972,6 +1050,7 @@ sub create_package($$$;$)
 	chdir($dir);
 	system("tar cfz $file .")
 		and die("ERROR: could not create package $file\n");
+	chdir($cwd);
 
 	# Remove temporary files
 	unlink("$dir/$pkg_build_file");
@@ -985,7 +1064,6 @@ sub create_package($$$;$)
 			info("  data files ...........: $count\n");
 		}
 	}
-	chdir($cwd);
 }
 
 sub find_link_fn($$$)
@@ -1208,6 +1286,113 @@ sub kernel_capture()
 }
 
 #
+# link_data_cb(datadir, rel, graphdir)
+#
+# Create symbolic link in GRAPDIR/REL pointing to DATADIR/REL.
+#
+
+sub link_data_cb($$$)
+{
+	my ($datadir, $rel, $graphdir) = @_;
+	my $absfrom = catfile($datadir, $rel);
+	my $absto = catfile($graphdir, $rel);
+	my $base;
+	my $dir;
+
+	if (-e $absto) {
+		die("ERROR: could not create symlink at $absto: ".
+		    "File already exists!\n");
+	}
+	if (-l $absto) {
+		# Broken link - possibly from an interrupted earlier run
+		unlink($absto);
+	}
+
+	# Check for graph file
+	$base = $absto;
+	$base =~ s/\.(gcda|da)$//;
+	if (! -e $base.".gcno" && ! -e $base.".bbg" && ! -e $base.".bb") {
+		die("ERROR: No graph file found for $absfrom in ".
+		    dirname($base)."!\n");
+	}
+
+	symlink($absfrom, $absto) or
+		die("ERROR: could not create symlink at $absto: $!\n");
+}
+
+#
+# unlink_data_cb(datadir, rel, graphdir)
+#
+# Remove symbolic link from GRAPHDIR/REL to DATADIR/REL.
+#
+
+sub unlink_data_cb($$$)
+{
+	my ($datadir, $rel, $graphdir) = @_;
+	my $absfrom = catfile($datadir, $rel);
+	my $absto = catfile($graphdir, $rel);
+	my $target;
+
+	return if (!-l $absto);
+	$target = readlink($absto);
+	return if (!defined($target) || $target ne $absfrom);
+
+	unlink($absto) or
+		warn("WARNING: could not remove symlink $absto: $!\n");
+}
+
+#
+# link_data(datadir, graphdir, create)
+#
+# If CREATE is non-zero, create symbolic links in GRAPHDIR for data files
+# found in DATADIR. Otherwise remove link in GRAPHDIR.
+#
+
+sub link_data($$$)
+{
+	my ($datadir, $graphdir, $create) = @_;
+
+	$datadir = abs_path($datadir);
+	$graphdir = abs_path($graphdir);
+	if ($create) {
+		lcov_find($datadir, \&link_data_cb, $graphdir, '\.gcda$',
+			  '\.da$');
+	} else {
+		lcov_find($datadir, \&unlink_data_cb, $graphdir, '\.gcda$',
+			  '\.da$');
+	}
+}
+
+#
+# find_graph_cb(datadir, rel, count_ref)
+#
+# Count number of files found.
+#
+
+sub find_graph_cb($$$)
+{
+	my ($dir, $rel, $count_ref) = @_;
+
+	($$count_ref)++;
+}
+
+#
+# find_graph(dir)
+#
+# Search DIR for a graph file. Return non-zero if one was found, zero otherwise.
+#
+
+sub find_graph($)
+{
+	my ($dir) = @_;
+	my $count = 0;
+
+	lcov_find($dir, \&find_graph_cb, \$count, '\.gcno$', '\.bb$', '\.bbg$');
+
+	return $count > 0 ? 1 : 0;
+}
+
+#
 # package_capture()
 #
 # Capture coverage data from a package of unprocessed coverage data files
@@ -1242,7 +1427,16 @@ sub package_capture()
 	} else {
 		# Build directory needs to be passed to geninfo
 		$base_directory = $build;
-		lcov_geninfo($dir);
+		if (find_graph($dir)) {
+			# Package contains graph files - collect from there
+			lcov_geninfo($dir);
+		} else {
+			# No graph files found, link data files next to
+			# graph files
+			link_data($dir, $base_directory, 1);
+			lcov_geninfo($base_directory);
+			link_data($dir, $base_directory, 0);
+		}
 	}
 }
 
@@ -1397,10 +1591,12 @@ sub br_ivec_push($$$$)
 	my $i;
 
 	$vec = "" if (!defined($vec));
+	$block = $BR_VEC_MAX if $block < 0;
 
 	# Check if branch already exists in vector
 	for ($i = 0; $i < $num; $i++) {
 		my ($v_block, $v_branch, $v_taken) = br_ivec_get($vec, $i);
+		$v_block = $BR_VEC_MAX if $v_block < 0;
 
 		next if ($v_block != $block || $v_branch != $branch);
 
@@ -1437,6 +1633,7 @@ sub br_ivec_get($$)
 
 	# Retrieve data from vector
 	$block	= vec($vec, $offset + $BR_BLOCK, $BR_VEC_WIDTH);
+	$block = -1 if ($block == $BR_VEC_MAX);
 	$branch = vec($vec, $offset + $BR_BRANCH, $BR_VEC_WIDTH);
 	$taken	= vec($vec, $offset + $BR_TAKEN, $BR_VEC_WIDTH);
 
@@ -1492,6 +1689,10 @@ sub get_br_found_and_hit($)
 #        "func"  -> \%funcdata
 #        "found" -> $lines_found (number of instrumented lines found in file)
 #	 "hit"   -> $lines_hit (number of executed lines in file)
+#        "f_found" -> $fn_found (number of instrumented functions found in file)
+#	 "f_hit"   -> $fn_hit (number of executed functions in file)
+#        "b_found" -> $br_found (number of instrumented branches found in file)
+#	 "b_hit"   -> $br_hit (number of executed branches in file)
 #        "check" -> \%checkdata
 #        "testfnc" -> \%testfncdata
 #        "sumfnc"  -> \%sumfnccount
@@ -1576,14 +1777,14 @@ sub read_info_file($)
 				"compressed file $_[0]!\n");
 
 		# Open compressed file
-		open(INFO_HANDLE, "gunzip -c $_[0]|")
+		open(INFO_HANDLE, "-|", "gunzip -c '$_[0]'")
 			or die("ERROR: cannot start gunzip to decompress ".
 			       "file $_[0]!\n");
 	}
 	else
 	{
 		# Open decompressed file
-		open(INFO_HANDLE, $_[0])
+		open(INFO_HANDLE, "<", $_[0])
 			or die("ERROR: cannot read file $_[0]!\n");
 	}
 
@@ -1674,6 +1875,8 @@ sub read_info_file($)
 
 			/^FN:(\d+),([^,]+)/ && do
 			{
+				last if (!$func_coverage);
+
 				# Function data found, add to structure
 				$funcdata->{$2} = $1;
 
@@ -1692,6 +1895,8 @@ sub read_info_file($)
 
 			/^FNDA:(\d+),([^,]+)/ && do
 			{
+				last if (!$func_coverage);
+
 				# Function call count found, add to structure
 				# Add summary counts
 				$sumfnccount->{$2} += $1;
@@ -1709,6 +1914,7 @@ sub read_info_file($)
 				my ($line, $block, $branch, $taken) =
 				   ($1, $2, $3, $4);
 
+				last if (!$br_coverage);
 				$sumbrcount->{$line} =
 					br_ivec_push($sumbrcount->{$line},
 						     $block, $branch, $taken);
@@ -1912,8 +2118,8 @@ sub set_info_entry($$$$$$$$$;$$$$$$)
 
 sub add_counts($$)
 {
-	my %data1 = %{$_[0]};	# Hash 1
-	my %data2 = %{$_[1]};	# Hash 2
+	my $data1_ref = $_[0];	# Hash 1
+	my $data2_ref = $_[1];	# Hash 2
 	my %result;		# Resulting hash
 	my $line;		# Current line iteration scalar
 	my $data1_count;	# Count of line in hash1
@@ -1921,10 +2127,10 @@ sub add_counts($$)
 	my $found = 0;		# Total number of lines found
 	my $hit = 0;		# Number of lines with a count > 0
 
-	foreach $line (keys(%data1))
+	foreach $line (keys(%$data1_ref))
 	{
-		$data1_count = $data1{$line};
-		$data2_count = $data2{$line};
+		$data1_count = $data1_ref->{$line};
+		$data2_count = $data2_ref->{$line};
 
 		# Add counts if present in both hashes
 		if (defined($data2_count)) { $data1_count += $data2_count; }
@@ -1936,14 +2142,14 @@ sub add_counts($$)
 		if ($data1_count > 0) { $hit++; }
 	}
 
-	# Add lines unique to data2
-	foreach $line (keys(%data2))
+	# Add lines unique to data2_ref
+	foreach $line (keys(%$data2_ref))
 	{
-		# Skip lines already in data1
-		if (defined($data1{$line})) { next; }
+		# Skip lines already in data1_ref
+		if (defined($data1_ref->{$line})) { next; }
 
-		# Copy count from data2
-		$result{$line} = $data2{$line};
+		# Copy count from data2_ref
+		$result{$line} = $data2_ref->{$line};
 
 		$found++;
 		if ($result{$line} > 0) { $hit++; }
@@ -2439,7 +2645,7 @@ sub add_traces()
 	if ($to_file)
 	{
 		info("Writing data to $output_filename\n");
-		open(INFO_HANDLE, ">$output_filename")
+		open(INFO_HANDLE, ">", $output_filename)
 			or die("ERROR: cannot write to $output_filename!\n");
 		@result = write_info_file(*INFO_HANDLE, $total_trace);
 		close(*INFO_HANDLE);
@@ -2548,6 +2754,7 @@ sub write_info_file(*$)
 					my ($block, $branch, $taken) =
 						br_ivec_get($brdata, $i);
 
+					$block = $BR_VEC_MAX if ($block < 0);
 					print(INFO_HANDLE "BRDA:$line,$block,".
 					      "$branch,$taken\n");
 					$br_found++;
@@ -2589,7 +2796,7 @@ sub write_info_file(*$)
 #
 # transform_pattern(pattern)
 #
-# Transform shell wildcard expression to equivalent PERL regular expression.
+# Transform shell wildcard expression to equivalent Perl regular expression.
 # Return transformed pattern.
 #
 
@@ -2669,7 +2876,7 @@ sub extract()
 	{
 		info("Extracted $extracted files\n");
 		info("Writing data to $output_filename\n");
-		open(INFO_HANDLE, ">$output_filename")
+		open(INFO_HANDLE, ">", $output_filename)
 			or die("ERROR: cannot write to $output_filename!\n");
 		@result = write_info_file(*INFO_HANDLE, $data);
 		close(*INFO_HANDLE);
@@ -2725,7 +2932,7 @@ sub remove()
 	{
 		info("Deleted $removed files\n");
 		info("Writing data to $output_filename\n");
-		open(INFO_HANDLE, ">$output_filename")
+		open(INFO_HANDLE, ">", $output_filename)
 			or die("ERROR: cannot write to $output_filename!\n");
 		@result = write_info_file(*INFO_HANDLE, $data);
 		close(*INFO_HANDLE);
@@ -2836,13 +3043,13 @@ sub shorten_number($$)
 	return '#';
 }
 
-sub shorten_rate($$)
+sub shorten_rate($$$)
 {
-	my ($rate, $width) = @_;
-	my $result = sprintf("%*.1f%%", $width - 3, $rate);
+	my ($hit, $found, $width) = @_;
+	my $result = rate($hit, $found, "%", 1, $width);
 
 	return $result if (length($result) <= $width);
-	$result = sprintf("%*d%%", $width - 1, $rate);
+	$result = rate($hit, $found, "%", 0, $width);
 	return $result if (length($result) <= $width);
 	return "#";
 }
@@ -3060,26 +3267,11 @@ sub list()
 		$br_total_hit += $br_hit;
 
 		# Determine line coverage rate for this file
-		if ($found == 0) {
-			$rate = "-";
-		} else {
-			$rate = shorten_rate(100 * $hit / $found,
-					     $fwidth[$F_LN_RATE]);
-		}
+		$rate = shorten_rate($hit, $found, $fwidth[$F_LN_RATE]);
 		# Determine function coverage rate for this file
-		if (!defined($fn_found) || $fn_found == 0) {
-			$fnrate = "-";
-		} else {
-			$fnrate = shorten_rate(100 * $fn_hit / $fn_found,
-					     $fwidth[$F_FN_RATE]);
-		}
+		$fnrate = shorten_rate($fn_hit, $fn_found, $fwidth[$F_FN_RATE]);
 		# Determine branch coverage rate for this file
-		if (!defined($br_found) || $br_found == 0) {
-			$brrate = "-";
-		} else {
-			$brrate = shorten_rate(100 * $br_hit / $br_found,
-					     $fwidth[$F_BR_RATE]);
-		}
+		$brrate = shorten_rate($br_hit, $br_found, $fwidth[$F_BR_RATE]);
 
 		# Assemble line parameters
 		push(@file_data, $print_filename);
@@ -3095,26 +3287,13 @@ sub list()
 	}
 
 	# Determine total line coverage rate
-	if ($total_found == 0) {
-		$rate = "-";
-	} else {
-		$rate = shorten_rate(100 * $total_hit / $total_found,
-				     $fwidth[$F_LN_RATE]);
-	}
+	$rate = shorten_rate($total_hit, $total_found, $fwidth[$F_LN_RATE]);
 	# Determine total function coverage rate
-	if ($fn_total_found == 0) {
-		$fnrate = "-";
-	} else {
-		$fnrate = shorten_rate(100 * $fn_total_hit / $fn_total_found,
-				       $fwidth[$F_FN_RATE]);
-	}
+	$fnrate = shorten_rate($fn_total_hit, $fn_total_found,
+			       $fwidth[$F_FN_RATE]);
 	# Determine total branch coverage rate
-	if ($br_total_found == 0) {
-		$brrate = "-";
-	} else {
-		$brrate = shorten_rate(100 * $br_total_hit / $br_total_found,
-				       $fwidth[$F_BR_RATE]);
-	}
+	$brrate = shorten_rate($br_total_hit, $br_total_found,
+			       $fwidth[$F_BR_RATE]);
 
 	# Print separator
 	print(("="x$barlen)."\n");
@@ -3253,14 +3432,14 @@ sub read_diff($)
 				"compressed file $diff_file!\n");
 
 		# Open compressed file
-		open(HANDLE, "gunzip -c $diff_file|")
+		open(HANDLE, "-|", "gunzip -c '$diff_file'")
 			or die("ERROR: cannot start gunzip to decompress ".
 			       "file $_[0]!\n");
 	}
 	else
 	{
 		# Open decompressed file
-		open(HANDLE, $diff_file)
+		open(HANDLE, "<", $diff_file)
 			or die("ERROR: cannot read file $_[0]!\n");
 	}
 
@@ -3668,10 +3847,10 @@ sub adjust_fncdata($$$)
 
 	# Remove count data in testfncdata for functions which are no longer
 	# in funcdata
-	foreach $testname (%{$testfncdata}) {
+	foreach $testname (keys(%{$testfncdata})) {
 		my $fnccount = $testfncdata->{$testname};
 
-		foreach $func (%{$fnccount}) {
+		foreach $func (keys(%{$fnccount})) {
 			if (!defined($funcdata->{$func})) {
 				delete($fnccount->{$func});
 			}
@@ -3679,7 +3858,7 @@ sub adjust_fncdata($$$)
 	}
 	# Remove count data in sumfnccount for functions which are no longer
 	# in funcdata
-	foreach $func (%{$sumfnccount}) {
+	foreach $func (keys(%{$sumfnccount})) {
 		if (!defined($funcdata->{$func})) {
 			delete($sumfnccount->{$func});
 		}
@@ -3886,7 +4065,7 @@ sub diff()
 	if ($to_file)
 	{
 		info("Writing data to $output_filename\n");
-		open(INFO_HANDLE, ">$output_filename")
+		open(INFO_HANDLE, ">", $output_filename)
 			or die("ERROR: cannot write to $output_filename!\n");
 		@result = write_info_file(*INFO_HANDLE, $trace_data);
 		close(*INFO_HANDLE);
@@ -3899,6 +4078,59 @@ sub diff()
 	return @result;
 }
 
+#
+# summary()
+#
+
+sub summary()
+{
+	my $filename;
+	my $current;
+	my $total;
+	my $ln_total_found;
+	my $ln_total_hit;
+	my $fn_total_found;
+	my $fn_total_hit;
+	my $br_total_found;
+	my $br_total_hit;
+
+	# Read and combine trace files
+	foreach $filename (@opt_summary) {
+		$current = read_info_file($filename);
+		if (!defined($total)) {
+			$total = $current;
+		} else {
+			$total = combine_info_files($total, $current);
+		}
+	}
+	# Calculate coverage data
+	foreach $filename (keys(%{$total}))
+	{
+		my $entry = $total->{$filename};
+		my $ln_found;
+		my $ln_hit;
+		my $fn_found;
+		my $fn_hit;
+		my $br_found;
+		my $br_hit;
+
+		(undef, undef, undef, undef, undef, undef, undef, undef,
+			$ln_found, $ln_hit, $fn_found, $fn_hit, $br_found,
+			$br_hit) = get_info_entry($entry);
+
+		# Add to totals
+		$ln_total_found	+= $ln_found;
+		$ln_total_hit	+= $ln_hit;
+		$fn_total_found += $fn_found;
+		$fn_total_hit	+= $fn_hit;
+		$br_total_found += $br_found;
+		$br_total_hit	+= $br_hit;
+	}
+
+
+	return ($ln_total_found, $ln_total_hit, $fn_total_found, $fn_total_hit,
+		$br_total_found, $br_total_hit);
+}
 
 #
 # system_no_output(mode, parameters)
@@ -3920,12 +4152,12 @@ sub system_no_output($@)
 	local *OLD_STDOUT;
 
 	# Save old stdout and stderr handles
-	($mode & 1) && open(OLD_STDOUT, ">>&STDOUT");
-	($mode & 2) && open(OLD_STDERR, ">>&STDERR");
+	($mode & 1) && open(OLD_STDOUT, ">>&", "STDOUT");
+	($mode & 2) && open(OLD_STDERR, ">>&", "STDERR");
 
 	# Redirect to /dev/null
-	($mode & 1) && open(STDOUT, ">/dev/null");
-	($mode & 2) && open(STDERR, ">/dev/null");
+	($mode & 1) && open(STDOUT, ">", "/dev/null");
+	($mode & 2) && open(STDERR, ">", "/dev/null");
  
 	system(@_);
 	$result = $?;
@@ -3935,8 +4167,8 @@ sub system_no_output($@)
 	($mode & 2) && close(STDERR);
 
 	# Restore old handles
-	($mode & 1) && open(STDOUT, ">>&OLD_STDOUT");
-	($mode & 2) && open(STDERR, ">>&OLD_STDERR");
+	($mode & 1) && open(STDOUT, ">>&", "OLD_STDOUT");
+	($mode & 2) && open(STDERR, ">>&", "OLD_STDERR");
  
 	return $result;
 }
@@ -3957,7 +4189,7 @@ sub read_config($)
 	my $value;
 	local *HANDLE;
 
-	if (!open(HANDLE, "<$filename"))
+	if (!open(HANDLE, "<", $filename))
 	{
 		warn("WARNING: cannot read configuration file $filename\n");
 		return undef;
@@ -3996,8 +4228,8 @@ sub read_config($)
 #   key_string => var_ref
 #
 # where KEY_STRING is a keyword and VAR_REF is a reference to an associated
-# variable. If the global configuration hash CONFIG contains a value for
-# keyword KEY_STRING, VAR_REF will be assigned the value for that keyword. 
+# variable. If the global configuration hashes CONFIG or OPT_RC contain a value
+# for keyword KEY_STRING, VAR_REF will be assigned the value for that keyword. 
 #
 
 sub apply_config($)
@@ -4006,8 +4238,9 @@ sub apply_config($)
 
 	foreach (keys(%{$ref}))
 	{
-		if (defined($config->{$_}))
-		{
+		if (defined($opt_rc{$_})) {
+			${$ref->{$_}} = $opt_rc{$_};
+		} elsif (defined($config->{$_})) {
 			${$ref->{$_}} = $config->{$_};
 		}
 	}
@@ -4037,6 +4270,9 @@ sub abort_handler($)
 
 sub temp_cleanup()
 {
+	# Ensure temp directory is not in use by current process
+	chdir("/");
+
 	if (@temp_dirs) {
 		info("Removing temporary directories.\n");
 		foreach (@temp_dirs) {
@@ -4145,8 +4381,8 @@ sub get_overall_line($$$$)
 
 	return "no data found" if (!defined($found) || $found == 0);
 	$name = ($found == 1) ? $name_sn : $name_pl;
-	return sprintf("%.1f%% (%d of %d %s)", $hit * 100 / $found, $hit,
-		       $found, $name);
+
+	return rate($hit, $found, "% ($hit of $found $name)");
 }
 
 
@@ -4162,7 +4398,7 @@ sub print_overall_rate($$$$$$$$$)
 	my ($ln_do, $ln_found, $ln_hit, $fn_do, $fn_found, $fn_hit,
 	    $br_do, $br_found, $br_hit) = @_;
 
-	info("Overall coverage rate:\n");
+	info("Summary coverage rate:\n");
 	info("  lines......: %s\n",
 	     get_overall_line($ln_found, $ln_hit, "line", "lines"))
 		if ($ln_do);
@@ -4173,3 +4409,38 @@ sub print_overall_rate($$$$$$$$$)
 	     get_overall_line($br_found, $br_hit, "branch", "branches"))
 		if ($br_do);
 }
+
+
+#
+# rate(hit, found[, suffix, precision, width])
+#
+# Return the coverage rate [0..100] for HIT and FOUND values. 0 is only
+# returned when HIT is 0. 100 is only returned when HIT equals FOUND.
+# PRECISION specifies the precision of the result. SUFFIX defines a
+# string that is appended to the result if FOUND is non-zero. Spaces
+# are added to the start of the resulting string until it is at least WIDTH
+# characters wide.
+#
+
+sub rate($$;$$$)
+{
+        my ($hit, $found, $suffix, $precision, $width) = @_;
+        my $rate; 
+
+	# Assign defaults if necessary
+        $precision	= 1	if (!defined($precision));
+	$suffix		= ""	if (!defined($suffix));
+	$width		= 0	if (!defined($width));
+        
+        return sprintf("%*s", $width, "-") if (!defined($found) || $found == 0);
+        $rate = sprintf("%.*f", $precision, $hit * 100 / $found);
+
+	# Adjust rates if necessary
+        if ($rate == 0 && $hit > 0) {
+		$rate = sprintf("%.*f", $precision, 1 / 10 ** $precision);
+        } elsif ($rate == 100 && $hit != $found) {
+		$rate = sprintf("%.*f", $precision, 100 - 1 / 10 ** $precision);
+	}
+
+	return sprintf("%*s", $width, $rate.$suffix);
+}
-- 
cgit v0.10.2-6-g49f6