| Index: third_party/lcov-1.9/bin/genhtml | 
| diff --git a/third_party/lcov-1.9/bin/genhtml b/third_party/lcov-1.9/bin/genhtml | 
| deleted file mode 100755 | 
| index d74063a4fdb85e582a2af0fd67f90b88ea6fba23..0000000000000000000000000000000000000000 | 
| --- a/third_party/lcov-1.9/bin/genhtml | 
| +++ /dev/null | 
| @@ -1,5648 +0,0 @@ | 
| -#!/usr/bin/perl -w | 
| -# | 
| -#   Copyright (c) International Business Machines  Corp., 2002,2010 | 
| -# | 
| -#   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 | 
| -#   the Free Software Foundation; either version 2 of the License, or (at | 
| -#   your option) any later version. | 
| -# | 
| -#   This program is distributed in the hope that it will be useful, but | 
| -#   WITHOUT ANY WARRANTY;  without even the implied warranty of | 
| -#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
| -#   General Public License for more details. | 
| -# | 
| -#   You should have received a copy of the GNU General Public License | 
| -#   along with this program;  if not, write to the Free Software | 
| -#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 
| -# | 
| -# | 
| -# genhtml | 
| -# | 
| -#   This script generates HTML output from .info files as created by the | 
| -#   geninfo script. Call it with --help and refer to the genhtml man page | 
| -#   to get information on usage and available options. | 
| -# | 
| -# | 
| -# History: | 
| -#   2002-08-23 created by Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com> | 
| -#                         IBM Lab Boeblingen | 
| -#        based on code by Manoj Iyer <manjo@mail.utexas.edu> and | 
| -#                         Megan Bock <mbock@us.ibm.com> | 
| -#                         IBM Austin | 
| -#   2002-08-27 / Peter Oberparleiter: implemented frame view | 
| -#   2002-08-29 / Peter Oberparleiter: implemented test description filtering | 
| -#                so that by default only descriptions for test cases which | 
| -#                actually hit some source lines are kept | 
| -#   2002-09-05 / Peter Oberparleiter: implemented --no-sourceview | 
| -#   2002-09-05 / Mike Kobler: One of my source file paths includes a "+" in | 
| -#                the directory name.  I found that genhtml.pl died when it | 
| -#                encountered it. I was able to fix the problem by modifying | 
| -#                the string with the escape character before parsing it. | 
| -#   2002-10-26 / Peter Oberparleiter: implemented --num-spaces | 
| -#   2003-04-07 / Peter Oberparleiter: fixed bug which resulted in an error | 
| -#                when trying to combine .info files containing data without | 
| -#                a test name | 
| -#   2003-04-10 / Peter Oberparleiter: extended fix by Mike to also cover | 
| -#                other special characters | 
| -#   2003-04-30 / Peter Oberparleiter: made info write to STDERR, not STDOUT | 
| -#   2003-07-10 / Peter Oberparleiter: added line checksum support | 
| -#   2004-08-09 / Peter Oberparleiter: added configuration file support | 
| -#   2005-03-04 / Cal Pierog: added legend to HTML output, fixed coloring of | 
| -#                "good coverage" background | 
| -#   2006-03-18 / Marcus Boerger: added --custom-intro, --custom-outro and | 
| -#                overwrite --no-prefix if --prefix is present | 
| -#   2006-03-20 / Peter Oberparleiter: changes to custom_* function (rename | 
| -#                to html_prolog/_epilog, minor modifications to implementation), | 
| -#                changed prefix/noprefix handling to be consistent with current | 
| -#                logic | 
| -#   2006-03-20 / Peter Oberparleiter: added --html-extension option | 
| -#   2008-07-14 / Tom Zoerner: added --function-coverage command line option; | 
| -#                added function table to source file page | 
| -#   2008-08-13 / Peter Oberparleiter: modified function coverage | 
| -#                implementation (now enabled per default), | 
| -#                introduced sorting option (enabled per default) | 
| -# | 
| - | 
| -use strict; | 
| -use File::Basename; | 
| -use Getopt::Long; | 
| -use Digest::MD5 qw(md5_base64); | 
| - | 
| - | 
| -# Global constants | 
| -our $title		= "LCOV - code coverage report"; | 
| -our $lcov_version	= 'LCOV version 1.9'; | 
| -our $lcov_url		= "http://ltp.sourceforge.net/coverage/lcov.php"; | 
| -our $tool_name		= basename($0); | 
| - | 
| -# 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 | 
| -# LO:          0  <= rate <  $med_limit   graph color: red | 
| - | 
| -# For line coverage/all coverage types if not specified | 
| -our $hi_limit = 90; | 
| -our $med_limit = 75; | 
| - | 
| -# For function coverage | 
| -our $fn_hi_limit; | 
| -our $fn_med_limit; | 
| - | 
| -# For branch coverage | 
| -our $br_hi_limit; | 
| -our $br_med_limit; | 
| - | 
| -# Width of overview image | 
| -our $overview_width = 80; | 
| - | 
| -# Resolution of overview navigation: this number specifies the maximum | 
| -# difference in lines between the position a user selected from the overview | 
| -# and the position the source code window is scrolled to. | 
| -our $nav_resolution = 4; | 
| - | 
| -# Clicking a line in the overview image should show the source code view at | 
| -# a position a bit further up so that the requested line is not the first | 
| -# line in the window. This number specifies that offset in lines. | 
| -our $nav_offset = 10; | 
| - | 
| -# Clicking on a function name should show the source code at a position a | 
| -# few lines before the first line of code of that function. This number | 
| -# specifies that offset in lines. | 
| -our $func_offset = 2; | 
| - | 
| -our $overview_title = "top level"; | 
| - | 
| -# Width for line coverage information in the source code view | 
| -our $line_field_width = 12; | 
| - | 
| -# Width for branch coverage information in the source code view | 
| -our $br_field_width = 16; | 
| - | 
| -# Internal Constants | 
| - | 
| -# Header types | 
| -our $HDR_DIR		= 0; | 
| -our $HDR_FILE		= 1; | 
| -our $HDR_SOURCE		= 2; | 
| -our $HDR_TESTDESC	= 3; | 
| -our $HDR_FUNC		= 4; | 
| - | 
| -# Sort types | 
| -our $SORT_FILE		= 0; | 
| -our $SORT_LINE		= 1; | 
| -our $SORT_FUNC		= 2; | 
| -our $SORT_BRANCH	= 3; | 
| - | 
| -# Fileview heading types | 
| -our $HEAD_NO_DETAIL	= 1; | 
| -our $HEAD_DETAIL_HIDDEN	= 2; | 
| -our $HEAD_DETAIL_SHOWN	= 3; | 
| - | 
| -# Offsets for storing branch coverage data in vectors | 
| -our $BR_BLOCK		= 0; | 
| -our $BR_BRANCH		= 1; | 
| -our $BR_TAKEN		= 2; | 
| -our $BR_VEC_ENTRIES	= 3; | 
| -our $BR_VEC_WIDTH	= 32; | 
| - | 
| -# Additional offsets used when converting branch coverage data to HTML | 
| -our $BR_LEN	= 3; | 
| -our $BR_OPEN	= 4; | 
| -our $BR_CLOSE	= 5; | 
| - | 
| -# Branch data combination types | 
| -our $BR_SUB = 0; | 
| -our $BR_ADD = 1; | 
| - | 
| -# Data related prototypes | 
| -sub print_usage(*); | 
| -sub gen_html(); | 
| -sub html_create($$); | 
| -sub process_dir($); | 
| -sub process_file($$$); | 
| -sub info(@); | 
| -sub read_info_file($); | 
| -sub get_info_entry($); | 
| -sub set_info_entry($$$$$$$$$;$$$$$$); | 
| -sub get_prefix(@); | 
| -sub shorten_prefix($); | 
| -sub get_dir_list(@); | 
| -sub get_relative_base_path($); | 
| -sub read_testfile($); | 
| -sub get_date_string(); | 
| -sub create_sub_dir($); | 
| -sub subtract_counts($$); | 
| -sub add_counts($$); | 
| -sub apply_baseline($$); | 
| -sub remove_unused_descriptions(); | 
| -sub get_found_and_hit($); | 
| -sub get_affecting_tests($$$); | 
| -sub combine_info_files($$); | 
| -sub merge_checksums($$$); | 
| -sub combine_info_entries($$$); | 
| -sub apply_prefix($$); | 
| -sub system_no_output($@); | 
| -sub read_config($); | 
| -sub apply_config($); | 
| -sub get_html_prolog($); | 
| -sub get_html_epilog($); | 
| -sub write_dir_page($$$$$$$$$$$$$$$$$); | 
| -sub classify_rate($$$$); | 
| -sub br_taken_add($$); | 
| -sub br_taken_sub($$); | 
| -sub br_ivec_len($); | 
| -sub br_ivec_get($$); | 
| -sub br_ivec_push($$$$); | 
| -sub combine_brcount($$$); | 
| -sub get_br_found_and_hit($); | 
| -sub warn_handler($); | 
| -sub die_handler($); | 
| - | 
| - | 
| -# HTML related prototypes | 
| -sub escape_html($); | 
| -sub get_bar_graph_code($$$); | 
| - | 
| -sub write_png_files(); | 
| -sub write_htaccess_file(); | 
| -sub write_css_file(); | 
| -sub write_description_file($$$$$$$); | 
| -sub write_function_table(*$$$$$$$$$$); | 
| - | 
| -sub write_html(*$); | 
| -sub write_html_prolog(*$$); | 
| -sub write_html_epilog(*$;$); | 
| - | 
| -sub write_header(*$$$$$$$$$$); | 
| -sub write_header_prolog(*$); | 
| -sub write_header_line(*@); | 
| -sub write_header_epilog(*$); | 
| - | 
| -sub write_file_table(*$$$$$$$); | 
| -sub write_file_table_prolog(*$@); | 
| -sub write_file_table_entry(*$$$@); | 
| -sub write_file_table_detail_entry(*$@); | 
| -sub write_file_table_epilog(*); | 
| - | 
| -sub write_test_table_prolog(*$); | 
| -sub write_test_table_entry(*$$); | 
| -sub write_test_table_epilog(*); | 
| - | 
| -sub write_source($$$$$$$); | 
| -sub write_source_prolog(*); | 
| -sub write_source_line(*$$$$$$); | 
| -sub write_source_epilog(*); | 
| - | 
| -sub write_frameset(*$$$); | 
| -sub write_overview_line(*$$$); | 
| -sub write_overview(*$$$$); | 
| - | 
| -# External prototype (defined in genpng) | 
| -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 %test_description;	# Hash containing test descriptions if available | 
| -our $date = get_date_string(); | 
| - | 
| -our @info_filenames;	# List of .info files to use as data source | 
| -our $test_title;	# Title for output as written to each page header | 
| -our $output_directory;	# Name of directory in which to store output | 
| -our $base_filename;	# Optional name of file containing baseline data | 
| -our $desc_filename;	# Name of file containing test descriptions | 
| -our $css_filename;	# Optional name of external stylesheet file to use | 
| -our $quiet;		# If set, suppress information messages | 
| -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 $no_func_coverage;	# Disable func_coverage | 
| -our $br_coverage = 1;	# 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 | 
| -our $frames;		# If set, use frames for source code view | 
| -our $keep_descriptions;	# If set, do not remove unused test case descriptions | 
| -our $no_sourceview;	# If set, do not create a source code view for each file | 
| -our $highlight;		# If set, highlight lines covered by converted data only | 
| -our $legend;		# If set, include legend in output | 
| -our $tab_size = 8;	# Number of spaces to use in place of tab | 
| -our $config;		# Configuration file contents | 
| -our $html_prolog_file;	# Custom HTML prolog file (up to and including <body>) | 
| -our $html_epilog_file;	# Custom HTML epilog file (from </body> onwards) | 
| -our $html_prolog;	# Actual HTML prolog | 
| -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 @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 $cwd = `pwd`;	# Current working directory | 
| -chomp($cwd); | 
| -our $tool_dir = dirname($0);	# Directory where genhtml tool is installed | 
| - | 
| - | 
| -# | 
| -# Code entry point | 
| -# | 
| - | 
| -$SIG{__WARN__} = \&warn_handler; | 
| -$SIG{__DIE__} = \&die_handler; | 
| - | 
| -# Prettify version string | 
| -$lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/; | 
| - | 
| -# Add current working directory if $tool_dir is not already an absolute path | 
| -if (! ($tool_dir =~ /^\/(.*)$/)) | 
| -{ | 
| -	$tool_dir = "$cwd/$tool_dir"; | 
| -} | 
| - | 
| -# Read configuration file if available | 
| -if (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc")) | 
| -{ | 
| -	$config = read_config($ENV{"HOME"}."/.lcovrc"); | 
| -} | 
| -elsif (-r "/etc/lcovrc") | 
| -{ | 
| -	$config = read_config("/etc/lcovrc"); | 
| -} | 
| - | 
| -if ($config) | 
| -{ | 
| -	# Copy configuration file values to variables | 
| -	apply_config({ | 
| -		"genhtml_css_file"		=> \$css_filename, | 
| -		"genhtml_hi_limit"		=> \$hi_limit, | 
| -		"genhtml_med_limit"		=> \$med_limit, | 
| -		"genhtml_line_field_width"	=> \$line_field_width, | 
| -		"genhtml_overview_width"	=> \$overview_width, | 
| -		"genhtml_nav_resolution"	=> \$nav_resolution, | 
| -		"genhtml_nav_offset"		=> \$nav_offset, | 
| -		"genhtml_keep_descriptions"	=> \$keep_descriptions, | 
| -		"genhtml_no_prefix"		=> \$no_prefix, | 
| -		"genhtml_no_source"		=> \$no_sourceview, | 
| -		"genhtml_num_spaces"		=> \$tab_size, | 
| -		"genhtml_highlight"		=> \$highlight, | 
| -		"genhtml_legend"		=> \$legend, | 
| -		"genhtml_html_prolog"		=> \$html_prolog_file, | 
| -		"genhtml_html_epilog"		=> \$html_epilog_file, | 
| -		"genhtml_html_extension"	=> \$html_ext, | 
| -		"genhtml_html_gzip"		=> \$html_gzip, | 
| -		"genhtml_function_hi_limit"	=> \$fn_hi_limit, | 
| -		"genhtml_function_med_limit"	=> \$fn_med_limit, | 
| -		"genhtml_function_coverage"	=> \$func_coverage, | 
| -		"genhtml_branch_hi_limit"	=> \$br_hi_limit, | 
| -		"genhtml_branch_med_limit"	=> \$br_med_limit, | 
| -		"genhtml_branch_coverage"	=> \$br_coverage, | 
| -		"genhtml_branch_field_width"	=> \$br_field_width, | 
| -		"genhtml_sort"			=> \$sort, | 
| -		}); | 
| -} | 
| - | 
| -# Copy limit 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)); | 
| - | 
| -# Parse command line options | 
| -if (!GetOptions("output-directory|o=s"	=> \$output_directory, | 
| -		"title|t=s"		=> \$test_title, | 
| -		"description-file|d=s"	=> \$desc_filename, | 
| -		"keep-descriptions|k"	=> \$keep_descriptions, | 
| -		"css-file|c=s"		=> \$css_filename, | 
| -		"baseline-file|b=s"	=> \$base_filename, | 
| -		"prefix|p=s"		=> \$dir_prefix, | 
| -		"num-spaces=i"		=> \$tab_size, | 
| -		"no-prefix"		=> \$no_prefix, | 
| -		"no-sourceview"		=> \$no_sourceview, | 
| -		"show-details|s"	=> \$show_details, | 
| -		"frames|f"		=> \$frames, | 
| -		"highlight"		=> \$highlight, | 
| -		"legend"		=> \$legend, | 
| -		"quiet|q"		=> \$quiet, | 
| -		"help|h|?"		=> \$help, | 
| -		"version|v"		=> \$version, | 
| -		"html-prolog=s"		=> \$html_prolog_file, | 
| -		"html-epilog=s"		=> \$html_epilog_file, | 
| -		"html-extension=s"	=> \$html_ext, | 
| -		"html-gzip"		=> \$html_gzip, | 
| -		"function-coverage"	=> \$func_coverage, | 
| -		"no-function-coverage"	=> \$no_func_coverage, | 
| -		"branch-coverage"	=> \$br_coverage, | 
| -		"no-branch-coverage"	=> \$no_br_coverage, | 
| -		"sort"			=> \$sort, | 
| -		"no-sort"		=> \$no_sort, | 
| -		"demangle-cpp"		=> \$demangle_cpp, | 
| -		)) | 
| -{ | 
| -	print(STDERR "Use $tool_name --help to get usage information\n"); | 
| -	exit(1); | 
| -} else { | 
| -	# Merge options | 
| -	if ($no_func_coverage) { | 
| -		$func_coverage = 0; | 
| -	} | 
| -	if ($no_br_coverage) { | 
| -		$br_coverage = 0; | 
| -	} | 
| - | 
| -	# Merge sort options | 
| -	if ($no_sort) { | 
| -		$sort = 0; | 
| -	} | 
| -} | 
| - | 
| -@info_filenames = @ARGV; | 
| - | 
| -# Check for help option | 
| -if ($help) | 
| -{ | 
| -	print_usage(*STDOUT); | 
| -	exit(0); | 
| -} | 
| - | 
| -# Check for version option | 
| -if ($version) | 
| -{ | 
| -	print("$tool_name: $lcov_version\n"); | 
| -	exit(0); | 
| -} | 
| - | 
| -# Check for info filename | 
| -if (!@info_filenames) | 
| -{ | 
| -	die("No filename specified\n". | 
| -	    "Use $tool_name --help to get usage information\n"); | 
| -} | 
| - | 
| -# Generate a title if none is specified | 
| -if (!$test_title) | 
| -{ | 
| -	if (scalar(@info_filenames) == 1) | 
| -	{ | 
| -		# Only one filename specified, use it as title | 
| -		$test_title = basename($info_filenames[0]); | 
| -	} | 
| -	else | 
| -	{ | 
| -		# More than one filename specified, used default title | 
| -		$test_title = "unnamed"; | 
| -	} | 
| -} | 
| - | 
| -# Make sure css_filename is an absolute path (in case we're changing | 
| -# directories) | 
| -if ($css_filename) | 
| -{ | 
| -	if (!($css_filename =~ /^\/(.*)$/)) | 
| -	{ | 
| -		$css_filename = $cwd."/".$css_filename; | 
| -	} | 
| -} | 
| - | 
| -# Make sure tab_size is within valid range | 
| -if ($tab_size < 1) | 
| -{ | 
| -	print(STDERR "ERROR: invalid number of spaces specified: ". | 
| -		     "$tab_size!\n"); | 
| -	exit(1); | 
| -} | 
| - | 
| -# Get HTML prolog and epilog | 
| -$html_prolog = get_html_prolog($html_prolog_file); | 
| -$html_epilog = get_html_epilog($html_epilog_file); | 
| - | 
| -# Issue a warning if --no-sourceview is enabled together with --frames | 
| -if ($no_sourceview && defined($frames)) | 
| -{ | 
| -	warn("WARNING: option --frames disabled because --no-sourceview ". | 
| -	     "was specified!\n"); | 
| -	$frames = undef; | 
| -} | 
| - | 
| -# Issue a warning if --no-prefix is enabled together with --prefix | 
| -if ($no_prefix && defined($dir_prefix)) | 
| -{ | 
| -	warn("WARNING: option --prefix disabled because --no-prefix was ". | 
| -	     "specified!\n"); | 
| -	$dir_prefix = undef; | 
| -} | 
| - | 
| -@fileview_sortlist = ($SORT_FILE); | 
| -@funcview_sortlist = ($SORT_FILE); | 
| - | 
| -if ($sort) { | 
| -	push(@fileview_sortlist, $SORT_LINE); | 
| -	push(@fileview_sortlist, $SORT_FUNC) if ($func_coverage); | 
| -	push(@fileview_sortlist, $SORT_BRANCH) if ($br_coverage); | 
| -	push(@funcview_sortlist, $SORT_LINE); | 
| -} | 
| - | 
| -if ($frames) | 
| -{ | 
| -	# Include genpng code needed for overview image generation | 
| -	do("$tool_dir/genpng"); | 
| -} | 
| - | 
| -# Ensure that the c++filt tool is available when using --demangle-cpp | 
| -if ($demangle_cpp) | 
| -{ | 
| -	if (system_no_output(3, "c++filt", "--version")) { | 
| -		die("ERROR: could not find c++filt tool needed for ". | 
| -		    "--demangle-cpp\n"); | 
| -	} | 
| -} | 
| - | 
| -# Make sure output_directory exists, create it if necessary | 
| -if ($output_directory) | 
| -{ | 
| -	stat($output_directory); | 
| - | 
| -	if (! -e _) | 
| -	{ | 
| -		create_sub_dir($output_directory); | 
| -	} | 
| -} | 
| - | 
| -# Do something | 
| -gen_html(); | 
| - | 
| -exit(0); | 
| - | 
| - | 
| - | 
| -# | 
| -# print_usage(handle) | 
| -# | 
| -# Print usage information. | 
| -# | 
| - | 
| -sub print_usage(*) | 
| -{ | 
| -	local *HANDLE = $_[0]; | 
| - | 
| -	print(HANDLE <<END_OF_USAGE); | 
| -Usage: $tool_name [OPTIONS] INFOFILE(S) | 
| - | 
| -Create HTML output for coverage data found in INFOFILE. Note that INFOFILE | 
| -may also be a list of filenames. | 
| - | 
| -Misc: | 
| -  -h, --help                        Print this help, then exit | 
| -  -v, --version                     Print version number, then exit | 
| -  -q, --quiet                       Do not print progress messages | 
| - | 
| -Operation: | 
| -  -o, --output-directory OUTDIR     Write HTML output to OUTDIR | 
| -  -s, --show-details                Generate detailed directory view | 
| -  -d, --description-file DESCFILE   Read test case descriptions from DESCFILE | 
| -  -k, --keep-descriptions           Do not remove unused test descriptions | 
| -  -b, --baseline-file BASEFILE      Use BASEFILE as baseline file | 
| -  -p, --prefix PREFIX               Remove PREFIX from all directory names | 
| -      --no-prefix                   Do not remove prefix from directory names | 
| -      --(no-)function-coverage      Enable (disable) function coverage display | 
| -      --(no-)branch-coverage        Enable (disable) branch coverage display | 
| - | 
| -HTML output: | 
| -  -f, --frames                      Use HTML frames for source code view | 
| -  -t, --title TITLE                 Display TITLE in header of all pages | 
| -  -c, --css-file CSSFILE            Use external style sheet file CSSFILE | 
| -      --no-source                   Do not create source code view | 
| -      --num-spaces NUM              Replace tabs with NUM spaces in source view | 
| -      --highlight                   Highlight lines with converted-only data | 
| -      --legend                      Include color legend in HTML output | 
| -      --html-prolog FILE            Use FILE as HTML prolog for generated pages | 
| -      --html-epilog FILE            Use FILE as HTML epilog for generated pages | 
| -      --html-extension EXT          Use EXT as filename extension for pages | 
| -      --html-gzip                   Use gzip to compress HTML | 
| -      --(no-)sort                   Enable (disable) sorted coverage views | 
| -      --demangle-cpp                Demangle C++ function names | 
| - | 
| -For more information see: $lcov_url | 
| -END_OF_USAGE | 
| -	; | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_rate(found, hit) | 
| -# | 
| -# Return a relative value for the specified found&hit values | 
| -# which is used for sorting the corresponding entries in a | 
| -# file list. | 
| -# | 
| - | 
| -sub get_rate($$) | 
| -{ | 
| -	my ($found, $hit) = @_; | 
| - | 
| -	if ($found == 0) { | 
| -		return 10000; | 
| -	} | 
| -	return int($hit * 1000 / $found) * 10 + 2 - (1 / $found); | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_overall_line(found, hit, name_singular, name_plural) | 
| -# | 
| -# Return a string containing overall information for the specified | 
| -# found/hit data. | 
| -# | 
| - | 
| -sub get_overall_line($$$$) | 
| -{ | 
| -	my ($found, $hit, $name_sn, $name_pl) = @_; | 
| -	my $name; | 
| - | 
| -	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); | 
| -} | 
| - | 
| - | 
| -# | 
| -# print_overall_rate(ln_do, ln_found, ln_hit, fn_do, fn_found, fn_hit, br_do | 
| -#                    br_found, br_hit) | 
| -# | 
| -# Print overall coverage rates for the specified coverage types. | 
| -# | 
| - | 
| -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("  lines......: %s\n", | 
| -	     get_overall_line($ln_found, $ln_hit, "line", "lines")) | 
| -		if ($ln_do); | 
| -	info("  functions..: %s\n", | 
| -	     get_overall_line($fn_found, $fn_hit, "function", "functions")) | 
| -		if ($fn_do); | 
| -	info("  branches...: %s\n", | 
| -	     get_overall_line($br_found, $br_hit, "branch", "branches")) | 
| -		if ($br_do); | 
| -} | 
| - | 
| - | 
| -# | 
| -# gen_html() | 
| -# | 
| -# Generate a set of HTML pages from contents of .info file INFO_FILENAME. | 
| -# Files will be written to the current directory. If provided, test case | 
| -# descriptions will be read from .tests file TEST_FILENAME and included | 
| -# in ouput. | 
| -# | 
| -# Die on error. | 
| -# | 
| - | 
| -sub gen_html() | 
| -{ | 
| -	local *HTML_HANDLE; | 
| -	my %overview; | 
| -	my %base_data; | 
| -	my $lines_found; | 
| -	my $lines_hit; | 
| -	my $fn_found; | 
| -	my $fn_hit; | 
| -	my $br_found; | 
| -	my $br_hit; | 
| -	my $overall_found = 0; | 
| -	my $overall_hit = 0; | 
| -	my $total_fn_found = 0; | 
| -	my $total_fn_hit = 0; | 
| -	my $total_br_found = 0; | 
| -	my $total_br_hit = 0; | 
| -	my $dir_name; | 
| -	my $link_name; | 
| -	my @dir_list; | 
| -	my %new_info; | 
| - | 
| -	# Read in all specified .info files | 
| -	foreach (@info_filenames) | 
| -	{ | 
| -		%new_info = %{read_info_file($_)}; | 
| - | 
| -		# Combine %new_info with %info_data | 
| -		%info_data = %{combine_info_files(\%info_data, \%new_info)}; | 
| -	} | 
| - | 
| -	info("Found %d entries.\n", scalar(keys(%info_data))); | 
| - | 
| -	# Read and apply baseline data if specified | 
| -	if ($base_filename) | 
| -	{ | 
| -		# Read baseline file | 
| -		info("Reading baseline file $base_filename\n"); | 
| -		%base_data = %{read_info_file($base_filename)}; | 
| -		info("Found %d entries.\n", scalar(keys(%base_data))); | 
| - | 
| -		# Apply baseline | 
| -		info("Subtracting baseline data.\n"); | 
| -		%info_data = %{apply_baseline(\%info_data, \%base_data)}; | 
| -	} | 
| - | 
| -	@dir_list = get_dir_list(keys(%info_data)); | 
| - | 
| -	if ($no_prefix) | 
| -	{ | 
| -		# User requested that we leave filenames alone | 
| -		info("User asked not to remove filename prefix\n"); | 
| -	} | 
| -	elsif (!defined($dir_prefix)) | 
| -	{ | 
| -		# Get prefix common to most directories in list | 
| -		$dir_prefix = get_prefix(@dir_list); | 
| - | 
| -		if ($dir_prefix) | 
| -		{ | 
| -			info("Found common filename prefix \"$dir_prefix\"\n"); | 
| -		} | 
| -		else | 
| -		{ | 
| -			info("No common filename prefix found!\n"); | 
| -			$no_prefix=1; | 
| -		} | 
| -	} | 
| -	else | 
| -	{ | 
| -		info("Using user-specified filename prefix \"". | 
| -		     "$dir_prefix\"\n"); | 
| -	} | 
| - | 
| -	# Read in test description file if specified | 
| -	if ($desc_filename) | 
| -	{ | 
| -		info("Reading test description file $desc_filename\n"); | 
| -		%test_description = %{read_testfile($desc_filename)}; | 
| - | 
| -		# Remove test descriptions which are not referenced | 
| -		# from %info_data if user didn't tell us otherwise | 
| -		if (!$keep_descriptions) | 
| -		{ | 
| -			remove_unused_descriptions(); | 
| -		} | 
| -	} | 
| - | 
| -	# Change to output directory if specified | 
| -	if ($output_directory) | 
| -	{ | 
| -		chdir($output_directory) | 
| -			or die("ERROR: cannot change to directory ". | 
| -			"$output_directory!\n"); | 
| -	} | 
| - | 
| -	info("Writing .css and .png files.\n"); | 
| -	write_css_file(); | 
| -	write_png_files(); | 
| - | 
| -	if ($html_gzip) | 
| -	{ | 
| -		info("Writing .htaccess file.\n"); | 
| -		write_htaccess_file(); | 
| -	} | 
| - | 
| -	info("Generating output.\n"); | 
| - | 
| -	# Process each subdirectory and collect overview information | 
| -	foreach $dir_name (@dir_list) | 
| -	{ | 
| -		($lines_found, $lines_hit, $fn_found, $fn_hit, | 
| -		 $br_found, $br_hit) | 
| -			= process_dir($dir_name); | 
| - | 
| -		# Remove prefix if applicable | 
| -		if (!$no_prefix && $dir_prefix) | 
| -		{ | 
| -			# Match directory names beginning with $dir_prefix | 
| -			$dir_name = apply_prefix($dir_name, $dir_prefix); | 
| -		} | 
| - | 
| -		# Generate name for directory overview HTML page | 
| -		if ($dir_name =~ /^\/(.*)$/) | 
| -		{ | 
| -			$link_name = substr($dir_name, 1)."/index.$html_ext"; | 
| -		} | 
| -		else | 
| -		{ | 
| -			$link_name = $dir_name."/index.$html_ext"; | 
| -		} | 
| - | 
| -		$overview{$dir_name} = [$lines_found, $lines_hit, $fn_found, | 
| -					$fn_hit, $br_found, $br_hit, $link_name, | 
| -					get_rate($lines_found, $lines_hit), | 
| -					get_rate($fn_found, $fn_hit), | 
| -					get_rate($br_found, $br_hit)]; | 
| -		$overall_found	+= $lines_found; | 
| -		$overall_hit	+= $lines_hit; | 
| -		$total_fn_found	+= $fn_found; | 
| -		$total_fn_hit	+= $fn_hit; | 
| -		$total_br_found	+= $br_found; | 
| -		$total_br_hit	+= $br_hit; | 
| -	} | 
| - | 
| -	# Generate overview page | 
| -	info("Writing directory view page.\n"); | 
| - | 
| -	# Create sorted pages | 
| -	foreach (@fileview_sortlist) { | 
| -		write_dir_page($fileview_sortname[$_], ".", "", $test_title, | 
| -			       undef, $overall_found, $overall_hit, | 
| -			       $total_fn_found, $total_fn_hit, $total_br_found, | 
| -			       $total_br_hit, \%overview, {}, {}, {}, 0, $_); | 
| -	} | 
| - | 
| -	# Check if there are any test case descriptions to write out | 
| -	if (%test_description) | 
| -	{ | 
| -		info("Writing test case description file.\n"); | 
| -		write_description_file( \%test_description, | 
| -					$overall_found, $overall_hit, | 
| -					$total_fn_found, $total_fn_hit, | 
| -					$total_br_found, $total_br_hit); | 
| -	} | 
| - | 
| -	print_overall_rate(1, $overall_found, $overall_hit, | 
| -			   $func_coverage, $total_fn_found, $total_fn_hit, | 
| -			   $br_coverage, $total_br_found, $total_br_hit); | 
| - | 
| -	chdir($cwd); | 
| -} | 
| - | 
| -# | 
| -# html_create(handle, filename) | 
| -# | 
| - | 
| -sub html_create($$) | 
| -{ | 
| -	my $handle = $_[0]; | 
| -	my $filename = $_[1]; | 
| - | 
| -	if ($html_gzip) | 
| -	{ | 
| -		open($handle, "|gzip -c >$filename") | 
| -			or die("ERROR: cannot open $filename for writing ". | 
| -			       "(gzip)!\n"); | 
| -	} | 
| -	else | 
| -	{ | 
| -		open($handle, ">$filename") | 
| -			or die("ERROR: cannot open $filename for writing!\n"); | 
| -	} | 
| -} | 
| - | 
| -sub write_dir_page($$$$$$$$$$$$$$$$$) | 
| -{ | 
| -	my ($name, $rel_dir, $base_dir, $title, $trunc_dir, $overall_found, | 
| -	    $overall_hit, $total_fn_found, $total_fn_hit, $total_br_found, | 
| -	    $total_br_hit, $overview, $testhash, $testfnchash, $testbrhash, | 
| -	    $view_type, $sort_type) = @_; | 
| - | 
| -	# Generate directory overview page including details | 
| -	html_create(*HTML_HANDLE, "$rel_dir/index$name.$html_ext"); | 
| -	if (!defined($trunc_dir)) { | 
| -		$trunc_dir = ""; | 
| -	} | 
| -	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, | 
| -		     $total_fn_hit, $total_br_found, $total_br_hit, $sort_type); | 
| -	write_file_table(*HTML_HANDLE, $base_dir, $overview, $testhash, | 
| -			 $testfnchash, $testbrhash, $view_type, $sort_type); | 
| -	write_html_epilog(*HTML_HANDLE, $base_dir); | 
| -	close(*HTML_HANDLE); | 
| -} | 
| - | 
| - | 
| -# | 
| -# process_dir(dir_name) | 
| -# | 
| - | 
| -sub process_dir($) | 
| -{ | 
| -	my $abs_dir = $_[0]; | 
| -	my $trunc_dir; | 
| -	my $rel_dir = $abs_dir; | 
| -	my $base_dir; | 
| -	my $filename; | 
| -	my %overview; | 
| -	my $lines_found; | 
| -	my $lines_hit; | 
| -	my $fn_found; | 
| -	my $fn_hit; | 
| -	my $br_found; | 
| -	my $br_hit; | 
| -	my $overall_found=0; | 
| -	my $overall_hit=0; | 
| -	my $total_fn_found=0; | 
| -	my $total_fn_hit=0; | 
| -	my $total_br_found = 0; | 
| -	my $total_br_hit = 0; | 
| -	my $base_name; | 
| -	my $extension; | 
| -	my $testdata; | 
| -	my %testhash; | 
| -	my $testfncdata; | 
| -	my %testfnchash; | 
| -	my $testbrdata; | 
| -	my %testbrhash; | 
| -	my @sort_list; | 
| -	local *HTML_HANDLE; | 
| - | 
| -	# Remove prefix if applicable | 
| -	if (!$no_prefix) | 
| -	{ | 
| -		# Match directory name beginning with $dir_prefix | 
| -		$rel_dir = apply_prefix($rel_dir, $dir_prefix); | 
| -	} | 
| - | 
| -	$trunc_dir = $rel_dir; | 
| - | 
| -	# Remove leading / | 
| -	if ($rel_dir =~ /^\/(.*)$/) | 
| -	{ | 
| -		$rel_dir = substr($rel_dir, 1); | 
| -	} | 
| - | 
| -	$base_dir = get_relative_base_path($rel_dir); | 
| - | 
| -	create_sub_dir($rel_dir); | 
| - | 
| -	# Match filenames which specify files in this directory, not including | 
| -	# sub-directories | 
| -	foreach $filename (grep(/^\Q$abs_dir\E\/[^\/]*$/,keys(%info_data))) | 
| -	{ | 
| -		my $page_link; | 
| -		my $func_link; | 
| - | 
| -		($lines_found, $lines_hit, $fn_found, $fn_hit, $br_found, | 
| -		 $br_hit, $testdata, $testfncdata, $testbrdata) = | 
| -			process_file($trunc_dir, $rel_dir, $filename); | 
| - | 
| -		$base_name = basename($filename); | 
| - | 
| -		if ($no_sourceview) { | 
| -			$page_link = ""; | 
| -		} elsif ($frames) { | 
| -			# Link to frameset page | 
| -			$page_link = "$base_name.gcov.frameset.$html_ext"; | 
| -		} else { | 
| -			# Link directory to source code view page | 
| -			$page_link = "$base_name.gcov.$html_ext"; | 
| -		} | 
| -		$overview{$base_name} = [$lines_found, $lines_hit, $fn_found, | 
| -					 $fn_hit, $br_found, $br_hit, | 
| -					 $page_link, | 
| -					 get_rate($lines_found, $lines_hit), | 
| -					 get_rate($fn_found, $fn_hit), | 
| -					 get_rate($br_found, $br_hit)]; | 
| - | 
| -		$testhash{$base_name} = $testdata; | 
| -		$testfnchash{$base_name} = $testfncdata; | 
| -		$testbrhash{$base_name} = $testbrdata; | 
| - | 
| -		$overall_found	+= $lines_found; | 
| -		$overall_hit	+= $lines_hit; | 
| - | 
| -		$total_fn_found += $fn_found; | 
| -		$total_fn_hit   += $fn_hit; | 
| - | 
| -		$total_br_found += $br_found; | 
| -		$total_br_hit   += $br_hit; | 
| -	} | 
| - | 
| -	# Create sorted pages | 
| -	foreach (@fileview_sortlist) { | 
| -		# Generate directory overview page (without details) | 
| -		write_dir_page($fileview_sortname[$_], $rel_dir, $base_dir, | 
| -			       $test_title, $trunc_dir, $overall_found, | 
| -			       $overall_hit, $total_fn_found, $total_fn_hit, | 
| -			       $total_br_found, $total_br_hit, \%overview, {}, | 
| -			       {}, {}, 1, $_); | 
| -		if (!$show_details) { | 
| -			next; | 
| -		} | 
| -		# Generate directory overview page including details | 
| -		write_dir_page("-detail".$fileview_sortname[$_], $rel_dir, | 
| -			       $base_dir, $test_title, $trunc_dir, | 
| -			       $overall_found, $overall_hit, $total_fn_found, | 
| -			       $total_fn_hit, $total_br_found, $total_br_hit, | 
| -			       \%overview, \%testhash, \%testfnchash, | 
| -			       \%testbrhash, 1, $_); | 
| -	} | 
| - | 
| -	# Calculate resulting line counts | 
| -	return ($overall_found, $overall_hit, $total_fn_found, $total_fn_hit, | 
| -		$total_br_found, $total_br_hit); | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_converted_lines(testdata) | 
| -# | 
| -# Return hash of line numbers of those lines which were only covered in | 
| -# converted data sets. | 
| -# | 
| - | 
| -sub get_converted_lines($) | 
| -{ | 
| -	my $testdata = $_[0]; | 
| -	my $testcount; | 
| -	my %converted; | 
| -	my %nonconverted; | 
| -	my $hash; | 
| -	my $testcase; | 
| -	my $line; | 
| -	my %result; | 
| - | 
| - | 
| -	# Get a hash containing line numbers with positive counts both for | 
| -	# converted and original data sets | 
| -	foreach $testcase (keys(%{$testdata})) | 
| -	{ | 
| -		# Check to see if this is a converted data set | 
| -		if ($testcase =~ /,diff$/) | 
| -		{ | 
| -			$hash = \%converted; | 
| -		} | 
| -		else | 
| -		{ | 
| -			$hash = \%nonconverted; | 
| -		} | 
| - | 
| -		$testcount = $testdata->{$testcase}; | 
| -		# Add lines with a positive count to hash | 
| -		foreach $line (keys%{$testcount}) | 
| -		{ | 
| -			if ($testcount->{$line} > 0) | 
| -			{ | 
| -				$hash->{$line} = 1; | 
| -			} | 
| -		} | 
| -	} | 
| - | 
| -	# Combine both hashes to resulting list | 
| -	foreach $line (keys(%converted)) | 
| -	{ | 
| -		if (!defined($nonconverted{$line})) | 
| -		{ | 
| -			$result{$line} = 1; | 
| -		} | 
| -	} | 
| - | 
| -	return \%result; | 
| -} | 
| - | 
| - | 
| -sub write_function_page($$$$$$$$$$$$$$$$$$) | 
| -{ | 
| -	my ($base_dir, $rel_dir, $trunc_dir, $base_name, $title, | 
| -	    $lines_found, $lines_hit, $fn_found, $fn_hit, $br_found, $br_hit, | 
| -	    $sumcount, $funcdata, $sumfnccount, $testfncdata, $sumbrcount, | 
| -	    $testbrdata, $sort_type) = @_; | 
| -	my $pagetitle; | 
| -	my $filename; | 
| - | 
| -	# Generate function table for this file | 
| -	if ($sort_type == 0) { | 
| -		$filename = "$rel_dir/$base_name.func.$html_ext"; | 
| -	} else { | 
| -		$filename = "$rel_dir/$base_name.func-sort-c.$html_ext"; | 
| -	} | 
| -	html_create(*HTML_HANDLE, $filename); | 
| -	$pagetitle = "LCOV - $title - $trunc_dir/$base_name - functions"; | 
| -	write_html_prolog(*HTML_HANDLE, $base_dir, $pagetitle); | 
| -	write_header(*HTML_HANDLE, 4, "$trunc_dir/$base_name", | 
| -		     "$rel_dir/$base_name", $lines_found, $lines_hit, | 
| -		     $fn_found, $fn_hit, $br_found, $br_hit, $sort_type); | 
| -	write_function_table(*HTML_HANDLE, "$base_name.gcov.$html_ext", | 
| -			     $sumcount, $funcdata, | 
| -			     $sumfnccount, $testfncdata, $sumbrcount, | 
| -			     $testbrdata, $base_name, | 
| -			     $base_dir, $sort_type); | 
| -	write_html_epilog(*HTML_HANDLE, $base_dir, 1); | 
| -	close(*HTML_HANDLE); | 
| -} | 
| - | 
| - | 
| -# | 
| -# process_file(trunc_dir, rel_dir, filename) | 
| -# | 
| - | 
| -sub process_file($$$) | 
| -{ | 
| -	info("Processing file ".apply_prefix($_[2], $dir_prefix)."\n"); | 
| - | 
| -	my $trunc_dir = $_[0]; | 
| -	my $rel_dir = $_[1]; | 
| -	my $filename = $_[2]; | 
| -	my $base_name = basename($filename); | 
| -	my $base_dir = get_relative_base_path($rel_dir); | 
| -	my $testdata; | 
| -	my $testcount; | 
| -	my $sumcount; | 
| -	my $funcdata; | 
| -	my $checkdata; | 
| -	my $testfncdata; | 
| -	my $sumfnccount; | 
| -	my $testbrdata; | 
| -	my $sumbrcount; | 
| -	my $lines_found; | 
| -	my $lines_hit; | 
| -	my $fn_found; | 
| -	my $fn_hit; | 
| -	my $br_found; | 
| -	my $br_hit; | 
| -	my $converted; | 
| -	my @source; | 
| -	my $pagetitle; | 
| -	local *HTML_HANDLE; | 
| - | 
| -	($testdata, $sumcount, $funcdata, $checkdata, $testfncdata, | 
| -	 $sumfnccount, $testbrdata, $sumbrcount, $lines_found, $lines_hit, | 
| -	 $fn_found, $fn_hit, $br_found, $br_hit) | 
| -		= get_info_entry($info_data{$filename}); | 
| - | 
| -	# Return after this point in case user asked us not to generate | 
| -	# source code view | 
| -	if ($no_sourceview) | 
| -	{ | 
| -		return ($lines_found, $lines_hit, $fn_found, $fn_hit, | 
| -			$br_found, $br_hit, $testdata, $testfncdata, | 
| -			$testbrdata); | 
| -	} | 
| - | 
| -	$converted = get_converted_lines($testdata); | 
| -	# Generate source code view for this file | 
| -	html_create(*HTML_HANDLE, "$rel_dir/$base_name.gcov.$html_ext"); | 
| -	$pagetitle = "LCOV - $test_title - $trunc_dir/$base_name"; | 
| -	write_html_prolog(*HTML_HANDLE, $base_dir, $pagetitle); | 
| -	write_header(*HTML_HANDLE, 2, "$trunc_dir/$base_name", | 
| -		     "$rel_dir/$base_name", $lines_found, $lines_hit, | 
| -		     $fn_found, $fn_hit, $br_found, $br_hit, 0); | 
| -	@source = write_source(*HTML_HANDLE, $filename, $sumcount, $checkdata, | 
| -			       $converted, $funcdata, $sumbrcount); | 
| - | 
| -	write_html_epilog(*HTML_HANDLE, $base_dir, 1); | 
| -	close(*HTML_HANDLE); | 
| - | 
| -	if ($func_coverage) { | 
| -		# Create function tables | 
| -		foreach (@funcview_sortlist) { | 
| -			write_function_page($base_dir, $rel_dir, $trunc_dir, | 
| -					    $base_name, $test_title, | 
| -					    $lines_found, $lines_hit, | 
| -					    $fn_found, $fn_hit, $br_found, | 
| -					    $br_hit, $sumcount, | 
| -					    $funcdata, $sumfnccount, | 
| -					    $testfncdata, $sumbrcount, | 
| -					    $testbrdata, $_); | 
| -		} | 
| -	} | 
| - | 
| -	# Additional files are needed in case of frame output | 
| -	if (!$frames) | 
| -	{ | 
| -		return ($lines_found, $lines_hit, $fn_found, $fn_hit, | 
| -			$br_found, $br_hit, $testdata, $testfncdata, | 
| -			$testbrdata); | 
| -	} | 
| - | 
| -	# Create overview png file | 
| -	gen_png("$rel_dir/$base_name.gcov.png", $overview_width, $tab_size, | 
| -		@source); | 
| - | 
| -	# Create frameset page | 
| -	html_create(*HTML_HANDLE, | 
| -		    "$rel_dir/$base_name.gcov.frameset.$html_ext"); | 
| -	write_frameset(*HTML_HANDLE, $base_dir, $base_name, $pagetitle); | 
| -	close(*HTML_HANDLE); | 
| - | 
| -	# Write overview frame | 
| -	html_create(*HTML_HANDLE, | 
| -		    "$rel_dir/$base_name.gcov.overview.$html_ext"); | 
| -	write_overview(*HTML_HANDLE, $base_dir, $base_name, $pagetitle, | 
| -		       scalar(@source)); | 
| -	close(*HTML_HANDLE); | 
| - | 
| -	return ($lines_found, $lines_hit, $fn_found, $fn_hit, $br_found, | 
| -		$br_hit, $testdata, $testfncdata, $testbrdata); | 
| -} | 
| - | 
| - | 
| -# | 
| -# read_info_file(info_filename) | 
| -# | 
| -# Read in the contents of the .info file specified by INFO_FILENAME. Data will | 
| -# be returned as a reference to a hash containing the following mappings: | 
| -# | 
| -# %result: for each filename found in file -> \%data | 
| -# | 
| -# %data: "test"  -> \%testdata | 
| -#        "sum"   -> \%sumcount | 
| -#        "func"  -> \%funcdata | 
| -#        "found" -> $lines_found (number of instrumented lines found in file) | 
| -#	 "hit"   -> $lines_hit (number of executed lines in file) | 
| -#        "check" -> \%checkdata | 
| -#        "testfnc" -> \%testfncdata | 
| -#        "sumfnc"  -> \%sumfnccount | 
| -#        "testbr"  -> \%testbrdata | 
| -#        "sumbr"   -> \%sumbrcount | 
| -# | 
| -# %testdata   : name of test affecting this file -> \%testcount | 
| -# %testfncdata: name of test affecting this file -> \%testfnccount | 
| -# %testbrdata:  name of test affecting this file -> \%testbrcount | 
| -# | 
| -# %testcount   : line number   -> execution count for a single test | 
| -# %testfnccount: function name -> execution count for a single test | 
| -# %testbrcount : line number   -> branch coverage data for a single test | 
| -# %sumcount    : line number   -> execution count for all tests | 
| -# %sumfnccount : function name -> execution count for all tests | 
| -# %sumbrcount  : line number   -> branch coverage data for all tests | 
| -# %funcdata    : function name -> line number | 
| -# %checkdata   : line number   -> checksum of source code line | 
| -# $brdata      : vector of items: block, branch, taken | 
| -# | 
| -# Note that .info file sections referring to the same file and test name | 
| -# will automatically be combined by adding all execution counts. | 
| -# | 
| -# Note that if INFO_FILENAME ends with ".gz", it is assumed that the file | 
| -# is compressed using GZIP. If available, GUNZIP will be used to decompress | 
| -# this file. | 
| -# | 
| -# Die on error. | 
| -# | 
| - | 
| -sub read_info_file($) | 
| -{ | 
| -	my $tracefile = $_[0];		# Name of tracefile | 
| -	my %result;			# Resulting hash: file -> data | 
| -	my $data;			# Data handle for current entry | 
| -	my $testdata;			#       "             " | 
| -	my $testcount;			#       "             " | 
| -	my $sumcount;			#       "             " | 
| -	my $funcdata;			#       "             " | 
| -	my $checkdata;			#       "             " | 
| -	my $testfncdata; | 
| -	my $testfnccount; | 
| -	my $sumfnccount; | 
| -	my $testbrdata; | 
| -	my $testbrcount; | 
| -	my $sumbrcount; | 
| -	my $line;			# Current line read from .info file | 
| -	my $testname;			# Current test name | 
| -	my $filename;			# Current filename | 
| -	my $hitcount;			# Count for lines hit | 
| -	my $count;			# Execution count of current line | 
| -	my $negative;			# If set, warn about negative counts | 
| -	my $changed_testname;		# If set, warn about changed testname | 
| -	my $line_checksum;		# Checksum of current line | 
| -	my $br_found; | 
| -	my $br_hit; | 
| -	local *INFO_HANDLE;		# Filehandle for .info file | 
| - | 
| -	info("Reading data file $tracefile\n"); | 
| - | 
| -	# Check if file exists and is readable | 
| -	stat($_[0]); | 
| -	if (!(-r _)) | 
| -	{ | 
| -		die("ERROR: cannot read file $_[0]!\n"); | 
| -	} | 
| - | 
| -	# Check if this is really a plain file | 
| -	if (!(-f _)) | 
| -	{ | 
| -		die("ERROR: not a plain file: $_[0]!\n"); | 
| -	} | 
| - | 
| -	# Check for .gz extension | 
| -	if ($_[0] =~ /\.gz$/) | 
| -	{ | 
| -		# Check for availability of GZIP tool | 
| -		system_no_output(1, "gunzip" ,"-h") | 
| -			and die("ERROR: gunzip command not available!\n"); | 
| - | 
| -		# Check integrity of compressed file | 
| -		system_no_output(1, "gunzip", "-t", $_[0]) | 
| -			and die("ERROR: integrity check failed for ". | 
| -				"compressed file $_[0]!\n"); | 
| - | 
| -		# Open compressed file | 
| -		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]) | 
| -			or die("ERROR: cannot read file $_[0]!\n"); | 
| -	} | 
| - | 
| -	$testname = ""; | 
| -	while (<INFO_HANDLE>) | 
| -	{ | 
| -		chomp($_); | 
| -		$line = $_; | 
| - | 
| -		# Switch statement | 
| -		foreach ($line) | 
| -		{ | 
| -			/^TN:([^,]*)(,diff)?/ && do | 
| -			{ | 
| -				# Test name information found | 
| -				$testname = defined($1) ? $1 : ""; | 
| -				if ($testname =~ s/\W/_/g) | 
| -				{ | 
| -					$changed_testname = 1; | 
| -				} | 
| -				$testname .= $2 if (defined($2)); | 
| -				last; | 
| -			}; | 
| - | 
| -			/^[SK]F:(.*)/ && do | 
| -			{ | 
| -				# Filename information found | 
| -				# Retrieve data for new entry | 
| -				$filename = $1; | 
| - | 
| -				$data = $result{$filename}; | 
| -				($testdata, $sumcount, $funcdata, $checkdata, | 
| -				 $testfncdata, $sumfnccount, $testbrdata, | 
| -				 $sumbrcount) = | 
| -					get_info_entry($data); | 
| - | 
| -				if (defined($testname)) | 
| -				{ | 
| -					$testcount = $testdata->{$testname}; | 
| -					$testfnccount = $testfncdata->{$testname}; | 
| -					$testbrcount = $testbrdata->{$testname}; | 
| -				} | 
| -				else | 
| -				{ | 
| -					$testcount = {}; | 
| -					$testfnccount = {}; | 
| -					$testbrcount = {}; | 
| -				} | 
| -				last; | 
| -			}; | 
| - | 
| -			/^DA:(\d+),(-?\d+)(,[^,\s]+)?/ && do | 
| -			{ | 
| -				# Fix negative counts | 
| -				$count = $2 < 0 ? 0 : $2; | 
| -				if ($2 < 0) | 
| -				{ | 
| -					$negative = 1; | 
| -				} | 
| -				# Execution count found, add to structure | 
| -				# Add summary counts | 
| -				$sumcount->{$1} += $count; | 
| - | 
| -				# Add test-specific counts | 
| -				if (defined($testname)) | 
| -				{ | 
| -					$testcount->{$1} += $count; | 
| -				} | 
| - | 
| -				# Store line checksum if available | 
| -				if (defined($3)) | 
| -				{ | 
| -					$line_checksum = substr($3, 1); | 
| - | 
| -					# Does it match a previous definition | 
| -					if (defined($checkdata->{$1}) && | 
| -					    ($checkdata->{$1} ne | 
| -					     $line_checksum)) | 
| -					{ | 
| -						die("ERROR: checksum mismatch ". | 
| -						    "at $filename:$1\n"); | 
| -					} | 
| - | 
| -					$checkdata->{$1} = $line_checksum; | 
| -				} | 
| -				last; | 
| -			}; | 
| - | 
| -			/^FN:(\d+),([^,]+)/ && do | 
| -			{ | 
| -				# Function data found, add to structure | 
| -				$funcdata->{$2} = $1; | 
| - | 
| -				# Also initialize function call data | 
| -				if (!defined($sumfnccount->{$2})) { | 
| -					$sumfnccount->{$2} = 0; | 
| -				} | 
| -				if (defined($testname)) | 
| -				{ | 
| -					if (!defined($testfnccount->{$2})) { | 
| -						$testfnccount->{$2} = 0; | 
| -					} | 
| -				} | 
| -				last; | 
| -			}; | 
| - | 
| -			/^FNDA:(\d+),([^,]+)/ && do | 
| -			{ | 
| -				# Function call count found, add to structure | 
| -				# Add summary counts | 
| -				$sumfnccount->{$2} += $1; | 
| - | 
| -				# Add test-specific counts | 
| -				if (defined($testname)) | 
| -				{ | 
| -					$testfnccount->{$2} += $1; | 
| -				} | 
| -				last; | 
| -			}; | 
| - | 
| -			/^BRDA:(\d+),(\d+),(\d+),(\d+|-)/ && do { | 
| -				# Branch coverage data found | 
| -				my ($line, $block, $branch, $taken) = | 
| -				   ($1, $2, $3, $4); | 
| - | 
| -				$sumbrcount->{$line} = | 
| -					br_ivec_push($sumbrcount->{$line}, | 
| -						     $block, $branch, $taken); | 
| - | 
| -				# Add test-specific counts | 
| -				if (defined($testname)) { | 
| -					$testbrcount->{$line} = | 
| -						br_ivec_push( | 
| -							$testbrcount->{$line}, | 
| -							$block, $branch, | 
| -							$taken); | 
| -				} | 
| -				last; | 
| -			}; | 
| - | 
| -			/^end_of_record/ && do | 
| -			{ | 
| -				# Found end of section marker | 
| -				if ($filename) | 
| -				{ | 
| -					# Store current section data | 
| -					if (defined($testname)) | 
| -					{ | 
| -						$testdata->{$testname} = | 
| -							$testcount; | 
| -						$testfncdata->{$testname} = | 
| -							$testfnccount; | 
| -						$testbrdata->{$testname} = | 
| -							$testbrcount; | 
| -					} | 
| - | 
| -					set_info_entry($data, $testdata, | 
| -						       $sumcount, $funcdata, | 
| -						       $checkdata, $testfncdata, | 
| -						       $sumfnccount, | 
| -						       $testbrdata, | 
| -						       $sumbrcount); | 
| -					$result{$filename} = $data; | 
| -					last; | 
| -				} | 
| -			}; | 
| - | 
| -			# default | 
| -			last; | 
| -		} | 
| -	} | 
| -	close(INFO_HANDLE); | 
| - | 
| -	# Calculate lines_found and lines_hit for each file | 
| -	foreach $filename (keys(%result)) | 
| -	{ | 
| -		$data = $result{$filename}; | 
| - | 
| -		($testdata, $sumcount, undef, undef, $testfncdata, | 
| -		 $sumfnccount, $testbrdata, $sumbrcount) = | 
| -			get_info_entry($data); | 
| - | 
| -		# Filter out empty files | 
| -		if (scalar(keys(%{$sumcount})) == 0) | 
| -		{ | 
| -			delete($result{$filename}); | 
| -			next; | 
| -		} | 
| -		# Filter out empty test cases | 
| -		foreach $testname (keys(%{$testdata})) | 
| -		{ | 
| -			if (!defined($testdata->{$testname}) || | 
| -			    scalar(keys(%{$testdata->{$testname}})) == 0) | 
| -			{ | 
| -				delete($testdata->{$testname}); | 
| -				delete($testfncdata->{$testname}); | 
| -			} | 
| -		} | 
| - | 
| -		$data->{"found"} = scalar(keys(%{$sumcount})); | 
| -		$hitcount = 0; | 
| - | 
| -		foreach (keys(%{$sumcount})) | 
| -		{ | 
| -			if ($sumcount->{$_} > 0) { $hitcount++; } | 
| -		} | 
| - | 
| -		$data->{"hit"} = $hitcount; | 
| - | 
| -		# Get found/hit values for function call data | 
| -		$data->{"f_found"} = scalar(keys(%{$sumfnccount})); | 
| -		$hitcount = 0; | 
| - | 
| -		foreach (keys(%{$sumfnccount})) { | 
| -			if ($sumfnccount->{$_} > 0) { | 
| -				$hitcount++; | 
| -			} | 
| -		} | 
| -		$data->{"f_hit"} = $hitcount; | 
| - | 
| -		# Get found/hit values for branch data | 
| -		($br_found, $br_hit) = get_br_found_and_hit($sumbrcount); | 
| - | 
| -		$data->{"b_found"} = $br_found; | 
| -		$data->{"b_hit"} = $br_hit; | 
| -	} | 
| - | 
| -	if (scalar(keys(%result)) == 0) | 
| -	{ | 
| -		die("ERROR: no valid records found in tracefile $tracefile\n"); | 
| -	} | 
| -	if ($negative) | 
| -	{ | 
| -		warn("WARNING: negative counts found in tracefile ". | 
| -		     "$tracefile\n"); | 
| -	} | 
| -	if ($changed_testname) | 
| -	{ | 
| -		warn("WARNING: invalid characters removed from testname in ". | 
| -		     "tracefile $tracefile\n"); | 
| -	} | 
| - | 
| -	return(\%result); | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_info_entry(hash_ref) | 
| -# | 
| -# Retrieve data from an entry of the structure generated by read_info_file(). | 
| -# Return a list of references to hashes: | 
| -# (test data hash ref, sum count hash ref, funcdata hash ref, checkdata hash | 
| -#  ref, testfncdata hash ref, sumfnccount hash ref, lines found, lines hit, | 
| -#  functions found, functions hit) | 
| -# | 
| - | 
| -sub get_info_entry($) | 
| -{ | 
| -	my $testdata_ref = $_[0]->{"test"}; | 
| -	my $sumcount_ref = $_[0]->{"sum"}; | 
| -	my $funcdata_ref = $_[0]->{"func"}; | 
| -	my $checkdata_ref = $_[0]->{"check"}; | 
| -	my $testfncdata = $_[0]->{"testfnc"}; | 
| -	my $sumfnccount = $_[0]->{"sumfnc"}; | 
| -	my $testbrdata = $_[0]->{"testbr"}; | 
| -	my $sumbrcount = $_[0]->{"sumbr"}; | 
| -	my $lines_found = $_[0]->{"found"}; | 
| -	my $lines_hit = $_[0]->{"hit"}; | 
| -	my $fn_found = $_[0]->{"f_found"}; | 
| -	my $fn_hit = $_[0]->{"f_hit"}; | 
| -	my $br_found = $_[0]->{"b_found"}; | 
| -	my $br_hit = $_[0]->{"b_hit"}; | 
| - | 
| -	return ($testdata_ref, $sumcount_ref, $funcdata_ref, $checkdata_ref, | 
| -		$testfncdata, $sumfnccount, $testbrdata, $sumbrcount, | 
| -		$lines_found, $lines_hit, $fn_found, $fn_hit, | 
| -		$br_found, $br_hit); | 
| -} | 
| - | 
| - | 
| -# | 
| -# set_info_entry(hash_ref, testdata_ref, sumcount_ref, funcdata_ref, | 
| -#                checkdata_ref, testfncdata_ref, sumfcncount_ref, | 
| -#                testbrdata_ref, sumbrcount_ref[,lines_found, | 
| -#                lines_hit, f_found, f_hit, $b_found, $b_hit]) | 
| -# | 
| -# Update the hash referenced by HASH_REF with the provided data references. | 
| -# | 
| - | 
| -sub set_info_entry($$$$$$$$$;$$$$$$) | 
| -{ | 
| -	my $data_ref = $_[0]; | 
| - | 
| -	$data_ref->{"test"} = $_[1]; | 
| -	$data_ref->{"sum"} = $_[2]; | 
| -	$data_ref->{"func"} = $_[3]; | 
| -	$data_ref->{"check"} = $_[4]; | 
| -	$data_ref->{"testfnc"} = $_[5]; | 
| -	$data_ref->{"sumfnc"} = $_[6]; | 
| -	$data_ref->{"testbr"} = $_[7]; | 
| -	$data_ref->{"sumbr"} = $_[8]; | 
| - | 
| -	if (defined($_[9])) { $data_ref->{"found"} = $_[9]; } | 
| -	if (defined($_[10])) { $data_ref->{"hit"} = $_[10]; } | 
| -	if (defined($_[11])) { $data_ref->{"f_found"} = $_[11]; } | 
| -	if (defined($_[12])) { $data_ref->{"f_hit"} = $_[12]; } | 
| -	if (defined($_[13])) { $data_ref->{"b_found"} = $_[13]; } | 
| -	if (defined($_[14])) { $data_ref->{"b_hit"} = $_[14]; } | 
| -} | 
| - | 
| - | 
| -# | 
| -# add_counts(data1_ref, data2_ref) | 
| -# | 
| -# DATA1_REF and DATA2_REF are references to hashes containing a mapping | 
| -# | 
| -#   line number -> execution count | 
| -# | 
| -# Return a list (RESULT_REF, LINES_FOUND, LINES_HIT) where RESULT_REF | 
| -# is a reference to a hash containing the combined mapping in which | 
| -# execution counts are added. | 
| -# | 
| - | 
| -sub add_counts($$) | 
| -{ | 
| -	my %data1 = %{$_[0]};	# Hash 1 | 
| -	my %data2 = %{$_[1]};	# Hash 2 | 
| -	my %result;		# Resulting hash | 
| -	my $line;		# Current line iteration scalar | 
| -	my $data1_count;	# Count of line in hash1 | 
| -	my $data2_count;	# Count of line in hash2 | 
| -	my $found = 0;		# Total number of lines found | 
| -	my $hit = 0;		# Number of lines with a count > 0 | 
| - | 
| -	foreach $line (keys(%data1)) | 
| -	{ | 
| -		$data1_count = $data1{$line}; | 
| -		$data2_count = $data2{$line}; | 
| - | 
| -		# Add counts if present in both hashes | 
| -		if (defined($data2_count)) { $data1_count += $data2_count; } | 
| - | 
| -		# Store sum in %result | 
| -		$result{$line} = $data1_count; | 
| - | 
| -		$found++; | 
| -		if ($data1_count > 0) { $hit++; } | 
| -	} | 
| - | 
| -	# Add lines unique to data2 | 
| -	foreach $line (keys(%data2)) | 
| -	{ | 
| -		# Skip lines already in data1 | 
| -		if (defined($data1{$line})) { next; } | 
| - | 
| -		# Copy count from data2 | 
| -		$result{$line} = $data2{$line}; | 
| - | 
| -		$found++; | 
| -		if ($result{$line} > 0) { $hit++; } | 
| -	} | 
| - | 
| -	return (\%result, $found, $hit); | 
| -} | 
| - | 
| - | 
| -# | 
| -# merge_checksums(ref1, ref2, filename) | 
| -# | 
| -# REF1 and REF2 are references to hashes containing a mapping | 
| -# | 
| -#   line number -> checksum | 
| -# | 
| -# Merge checksum lists defined in REF1 and REF2 and return reference to | 
| -# resulting hash. Die if a checksum for a line is defined in both hashes | 
| -# but does not match. | 
| -# | 
| - | 
| -sub merge_checksums($$$) | 
| -{ | 
| -	my $ref1 = $_[0]; | 
| -	my $ref2 = $_[1]; | 
| -	my $filename = $_[2]; | 
| -	my %result; | 
| -	my $line; | 
| - | 
| -	foreach $line (keys(%{$ref1})) | 
| -	{ | 
| -		if (defined($ref2->{$line}) && | 
| -		    ($ref1->{$line} ne $ref2->{$line})) | 
| -		{ | 
| -			die("ERROR: checksum mismatch at $filename:$line\n"); | 
| -		} | 
| -		$result{$line} = $ref1->{$line}; | 
| -	} | 
| - | 
| -	foreach $line (keys(%{$ref2})) | 
| -	{ | 
| -		$result{$line} = $ref2->{$line}; | 
| -	} | 
| - | 
| -	return \%result; | 
| -} | 
| - | 
| - | 
| -# | 
| -# merge_func_data(funcdata1, funcdata2, filename) | 
| -# | 
| - | 
| -sub merge_func_data($$$) | 
| -{ | 
| -	my ($funcdata1, $funcdata2, $filename) = @_; | 
| -	my %result; | 
| -	my $func; | 
| - | 
| -	if (defined($funcdata1)) { | 
| -		%result = %{$funcdata1}; | 
| -	} | 
| - | 
| -	foreach $func (keys(%{$funcdata2})) { | 
| -		my $line1 = $result{$func}; | 
| -		my $line2 = $funcdata2->{$func}; | 
| - | 
| -		if (defined($line1) && ($line1 != $line2)) { | 
| -			warn("WARNING: function data mismatch at ". | 
| -			     "$filename:$line2\n"); | 
| -			next; | 
| -		} | 
| -		$result{$func} = $line2; | 
| -	} | 
| - | 
| -	return \%result; | 
| -} | 
| - | 
| - | 
| -# | 
| -# add_fnccount(fnccount1, fnccount2) | 
| -# | 
| -# Add function call count data. Return list (fnccount_added, f_found, f_hit) | 
| -# | 
| - | 
| -sub add_fnccount($$) | 
| -{ | 
| -	my ($fnccount1, $fnccount2) = @_; | 
| -	my %result; | 
| -	my $fn_found; | 
| -	my $fn_hit; | 
| -	my $function; | 
| - | 
| -	if (defined($fnccount1)) { | 
| -		%result = %{$fnccount1}; | 
| -	} | 
| -	foreach $function (keys(%{$fnccount2})) { | 
| -		$result{$function} += $fnccount2->{$function}; | 
| -	} | 
| -	$fn_found = scalar(keys(%result)); | 
| -	$fn_hit = 0; | 
| -	foreach $function (keys(%result)) { | 
| -		if ($result{$function} > 0) { | 
| -			$fn_hit++; | 
| -		} | 
| -	} | 
| - | 
| -	return (\%result, $fn_found, $fn_hit); | 
| -} | 
| - | 
| -# | 
| -# add_testfncdata(testfncdata1, testfncdata2) | 
| -# | 
| -# Add function call count data for several tests. Return reference to | 
| -# added_testfncdata. | 
| -# | 
| - | 
| -sub add_testfncdata($$) | 
| -{ | 
| -	my ($testfncdata1, $testfncdata2) = @_; | 
| -	my %result; | 
| -	my $testname; | 
| - | 
| -	foreach $testname (keys(%{$testfncdata1})) { | 
| -		if (defined($testfncdata2->{$testname})) { | 
| -			my $fnccount; | 
| - | 
| -			# Function call count data for this testname exists | 
| -			# in both data sets: add | 
| -			($fnccount) = add_fnccount( | 
| -				$testfncdata1->{$testname}, | 
| -				$testfncdata2->{$testname}); | 
| -			$result{$testname} = $fnccount; | 
| -			next; | 
| -		} | 
| -		# Function call count data for this testname is unique to | 
| -		# data set 1: copy | 
| -		$result{$testname} = $testfncdata1->{$testname}; | 
| -	} | 
| - | 
| -	# Add count data for testnames unique to data set 2 | 
| -	foreach $testname (keys(%{$testfncdata2})) { | 
| -		if (!defined($result{$testname})) { | 
| -			$result{$testname} = $testfncdata2->{$testname}; | 
| -		} | 
| -	} | 
| -	return \%result; | 
| -} | 
| - | 
| - | 
| -# | 
| -# brcount_to_db(brcount) | 
| -# | 
| -# Convert brcount data to the following format: | 
| -# | 
| -# db:          line number    -> block hash | 
| -# block hash:  block number   -> branch hash | 
| -# branch hash: branch number  -> taken value | 
| -# | 
| - | 
| -sub brcount_to_db($) | 
| -{ | 
| -	my ($brcount) = @_; | 
| -	my $line; | 
| -	my $db; | 
| - | 
| -	# Add branches from first count to database | 
| -	foreach $line (keys(%{$brcount})) { | 
| -		my $brdata = $brcount->{$line}; | 
| -		my $i; | 
| -		my $num = br_ivec_len($brdata); | 
| - | 
| -		for ($i = 0; $i < $num; $i++) { | 
| -			my ($block, $branch, $taken) = br_ivec_get($brdata, $i); | 
| - | 
| -			$db->{$line}->{$block}->{$branch} = $taken; | 
| -		} | 
| -	} | 
| - | 
| -	return $db; | 
| -} | 
| - | 
| - | 
| -# | 
| -# db_to_brcount(db) | 
| -# | 
| -# Convert branch coverage data back to brcount format. | 
| -# | 
| - | 
| -sub db_to_brcount($) | 
| -{ | 
| -	my ($db) = @_; | 
| -	my $line; | 
| -	my $brcount = {}; | 
| -	my $br_found = 0; | 
| -	my $br_hit = 0; | 
| - | 
| -	# Convert database back to brcount format | 
| -	foreach $line (sort({$a <=> $b} keys(%{$db}))) { | 
| -		my $ldata = $db->{$line}; | 
| -		my $brdata; | 
| -		my $block; | 
| - | 
| -		foreach $block (sort({$a <=> $b} keys(%{$ldata}))) { | 
| -			my $bdata = $ldata->{$block}; | 
| -			my $branch; | 
| - | 
| -			foreach $branch (sort({$a <=> $b} keys(%{$bdata}))) { | 
| -				my $taken = $bdata->{$branch}; | 
| - | 
| -				$br_found++; | 
| -				$br_hit++ if ($taken ne "-" && $taken > 0); | 
| -				$brdata = br_ivec_push($brdata, $block, | 
| -						       $branch, $taken); | 
| -			} | 
| -		} | 
| -		$brcount->{$line} = $brdata; | 
| -	} | 
| - | 
| -	return ($brcount, $br_found, $br_hit); | 
| -} | 
| - | 
| - | 
| -# | 
| -# combine_brcount(brcount1, brcount2, type) | 
| -# | 
| -# If add is BR_ADD, add branch coverage data and return list (brcount_added, | 
| -# br_found, br_hit). If add is BR_SUB, subtract the taken values of brcount2 | 
| -# from brcount1 and return (brcount_sub, br_found, br_hit). | 
| -# | 
| - | 
| -sub combine_brcount($$$) | 
| -{ | 
| -	my ($brcount1, $brcount2, $type) = @_; | 
| -	my $line; | 
| -	my $block; | 
| -	my $branch; | 
| -	my $taken; | 
| -	my $db; | 
| -	my $br_found = 0; | 
| -	my $br_hit = 0; | 
| -	my $result; | 
| - | 
| -	# Convert branches from first count to database | 
| -	$db = brcount_to_db($brcount1); | 
| -	# Combine values from database and second count | 
| -	foreach $line (keys(%{$brcount2})) { | 
| -		my $brdata = $brcount2->{$line}; | 
| -		my $num = br_ivec_len($brdata); | 
| -		my $i; | 
| - | 
| -		for ($i = 0; $i < $num; $i++) { | 
| -			($block, $branch, $taken) = br_ivec_get($brdata, $i); | 
| -			my $new_taken = $db->{$line}->{$block}->{$branch}; | 
| - | 
| -			if ($type == $BR_ADD) { | 
| -				$new_taken = br_taken_add($new_taken, $taken); | 
| -			} elsif ($type == $BR_SUB) { | 
| -				$new_taken = br_taken_sub($new_taken, $taken); | 
| -			} | 
| -			$db->{$line}->{$block}->{$branch} = $new_taken | 
| -				if (defined($new_taken)); | 
| -		} | 
| -	} | 
| -	# Convert database back to brcount format | 
| -	($result, $br_found, $br_hit) = db_to_brcount($db); | 
| - | 
| -	return ($result, $br_found, $br_hit); | 
| -} | 
| - | 
| - | 
| -# | 
| -# add_testbrdata(testbrdata1, testbrdata2) | 
| -# | 
| -# Add branch coverage data for several tests. Return reference to | 
| -# added_testbrdata. | 
| -# | 
| - | 
| -sub add_testbrdata($$) | 
| -{ | 
| -	my ($testbrdata1, $testbrdata2) = @_; | 
| -	my %result; | 
| -	my $testname; | 
| - | 
| -	foreach $testname (keys(%{$testbrdata1})) { | 
| -		if (defined($testbrdata2->{$testname})) { | 
| -			my $brcount; | 
| - | 
| -			# Branch coverage data for this testname exists | 
| -			# in both data sets: add | 
| -			($brcount) = combine_brcount($testbrdata1->{$testname}, | 
| -					 $testbrdata2->{$testname}, $BR_ADD); | 
| -			$result{$testname} = $brcount; | 
| -			next; | 
| -		} | 
| -		# Branch coverage data for this testname is unique to | 
| -		# data set 1: copy | 
| -		$result{$testname} = $testbrdata1->{$testname}; | 
| -	} | 
| - | 
| -	# Add count data for testnames unique to data set 2 | 
| -	foreach $testname (keys(%{$testbrdata2})) { | 
| -		if (!defined($result{$testname})) { | 
| -			$result{$testname} = $testbrdata2->{$testname}; | 
| -		} | 
| -	} | 
| -	return \%result; | 
| -} | 
| - | 
| - | 
| -# | 
| -# combine_info_entries(entry_ref1, entry_ref2, filename) | 
| -# | 
| -# Combine .info data entry hashes referenced by ENTRY_REF1 and ENTRY_REF2. | 
| -# Return reference to resulting hash. | 
| -# | 
| - | 
| -sub combine_info_entries($$$) | 
| -{ | 
| -	my $entry1 = $_[0];	# Reference to hash containing first entry | 
| -	my $testdata1; | 
| -	my $sumcount1; | 
| -	my $funcdata1; | 
| -	my $checkdata1; | 
| -	my $testfncdata1; | 
| -	my $sumfnccount1; | 
| -	my $testbrdata1; | 
| -	my $sumbrcount1; | 
| - | 
| -	my $entry2 = $_[1];	# Reference to hash containing second entry | 
| -	my $testdata2; | 
| -	my $sumcount2; | 
| -	my $funcdata2; | 
| -	my $checkdata2; | 
| -	my $testfncdata2; | 
| -	my $sumfnccount2; | 
| -	my $testbrdata2; | 
| -	my $sumbrcount2; | 
| - | 
| -	my %result;		# Hash containing combined entry | 
| -	my %result_testdata; | 
| -	my $result_sumcount = {}; | 
| -	my $result_funcdata; | 
| -	my $result_testfncdata; | 
| -	my $result_sumfnccount; | 
| -	my $result_testbrdata; | 
| -	my $result_sumbrcount; | 
| -	my $lines_found; | 
| -	my $lines_hit; | 
| -	my $fn_found; | 
| -	my $fn_hit; | 
| -	my $br_found; | 
| -	my $br_hit; | 
| - | 
| -	my $testname; | 
| -	my $filename = $_[2]; | 
| - | 
| -	# Retrieve data | 
| -	($testdata1, $sumcount1, $funcdata1, $checkdata1, $testfncdata1, | 
| -	 $sumfnccount1, $testbrdata1, $sumbrcount1) = get_info_entry($entry1); | 
| -	($testdata2, $sumcount2, $funcdata2, $checkdata2, $testfncdata2, | 
| -	 $sumfnccount2, $testbrdata2, $sumbrcount2) = get_info_entry($entry2); | 
| - | 
| -	# Merge checksums | 
| -	$checkdata1 = merge_checksums($checkdata1, $checkdata2, $filename); | 
| - | 
| -	# Combine funcdata | 
| -	$result_funcdata = merge_func_data($funcdata1, $funcdata2, $filename); | 
| - | 
| -	# Combine function call count data | 
| -	$result_testfncdata = add_testfncdata($testfncdata1, $testfncdata2); | 
| -	($result_sumfnccount, $fn_found, $fn_hit) = | 
| -		add_fnccount($sumfnccount1, $sumfnccount2); | 
| - | 
| -	# Combine branch coverage data | 
| -	$result_testbrdata = add_testbrdata($testbrdata1, $testbrdata2); | 
| -	($result_sumbrcount, $br_found, $br_hit) = | 
| -		combine_brcount($sumbrcount1, $sumbrcount2, $BR_ADD); | 
| - | 
| -	# Combine testdata | 
| -	foreach $testname (keys(%{$testdata1})) | 
| -	{ | 
| -		if (defined($testdata2->{$testname})) | 
| -		{ | 
| -			# testname is present in both entries, requires | 
| -			# combination | 
| -			($result_testdata{$testname}) = | 
| -				add_counts($testdata1->{$testname}, | 
| -					   $testdata2->{$testname}); | 
| -		} | 
| -		else | 
| -		{ | 
| -			# testname only present in entry1, add to result | 
| -			$result_testdata{$testname} = $testdata1->{$testname}; | 
| -		} | 
| - | 
| -		# update sum count hash | 
| -		($result_sumcount, $lines_found, $lines_hit) = | 
| -			add_counts($result_sumcount, | 
| -				   $result_testdata{$testname}); | 
| -	} | 
| - | 
| -	foreach $testname (keys(%{$testdata2})) | 
| -	{ | 
| -		# Skip testnames already covered by previous iteration | 
| -		if (defined($testdata1->{$testname})) { next; } | 
| - | 
| -		# testname only present in entry2, add to result hash | 
| -		$result_testdata{$testname} = $testdata2->{$testname}; | 
| - | 
| -		# update sum count hash | 
| -		($result_sumcount, $lines_found, $lines_hit) = | 
| -			add_counts($result_sumcount, | 
| -				   $result_testdata{$testname}); | 
| -	} | 
| - | 
| -	# Calculate resulting sumcount | 
| - | 
| -	# Store result | 
| -	set_info_entry(\%result, \%result_testdata, $result_sumcount, | 
| -		       $result_funcdata, $checkdata1, $result_testfncdata, | 
| -		       $result_sumfnccount, $result_testbrdata, | 
| -		       $result_sumbrcount, $lines_found, $lines_hit, | 
| -		       $fn_found, $fn_hit, $br_found, $br_hit); | 
| - | 
| -	return(\%result); | 
| -} | 
| - | 
| - | 
| -# | 
| -# combine_info_files(info_ref1, info_ref2) | 
| -# | 
| -# Combine .info data in hashes referenced by INFO_REF1 and INFO_REF2. Return | 
| -# reference to resulting hash. | 
| -# | 
| - | 
| -sub combine_info_files($$) | 
| -{ | 
| -	my %hash1 = %{$_[0]}; | 
| -	my %hash2 = %{$_[1]}; | 
| -	my $filename; | 
| - | 
| -	foreach $filename (keys(%hash2)) | 
| -	{ | 
| -		if ($hash1{$filename}) | 
| -		{ | 
| -			# Entry already exists in hash1, combine them | 
| -			$hash1{$filename} = | 
| -				combine_info_entries($hash1{$filename}, | 
| -						     $hash2{$filename}, | 
| -						     $filename); | 
| -		} | 
| -		else | 
| -		{ | 
| -			# Entry is unique in both hashes, simply add to | 
| -			# resulting hash | 
| -			$hash1{$filename} = $hash2{$filename}; | 
| -		} | 
| -	} | 
| - | 
| -	return(\%hash1); | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_prefix(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. | 
| -# | 
| - | 
| -sub get_prefix(@) | 
| -{ | 
| -	my @filename_list = @_;		# provided list of filenames | 
| -	my %prefix;			# mapping: prefix -> sum of lengths | 
| -	my $current;			# Temporary iteration variable | 
| - | 
| -	# Find list of prefixes | 
| -	foreach (@filename_list) | 
| -	{ | 
| -		# Need explicit assignment to get a copy of $_ so that | 
| -		# shortening the contained prefix does not affect the list | 
| -		$current = shorten_prefix($_); | 
| -		while ($current = shorten_prefix($current)) | 
| -		{ | 
| -			# Skip rest if the remaining prefix has already been | 
| -			# added to hash | 
| -			if ($prefix{$current}) { last; } | 
| - | 
| -			# Initialize with 0 | 
| -			$prefix{$current}="0"; | 
| -		} | 
| - | 
| -	} | 
| - | 
| -	# Calculate sum of lengths for all prefixes | 
| -	foreach $current (keys(%prefix)) | 
| -	{ | 
| -		foreach (@filename_list) | 
| -		{ | 
| -			# Add original length | 
| -			$prefix{$current} += length($_); | 
| - | 
| -			# Check whether prefix matches | 
| -			if (substr($_, 0, length($current)) eq $current) | 
| -			{ | 
| -				# Subtract prefix length for this filename | 
| -				$prefix{$current} -= length($current); | 
| -			} | 
| -		} | 
| -	} | 
| - | 
| -	# Find and return prefix with minimal sum | 
| -	$current = (keys(%prefix))[0]; | 
| - | 
| -	foreach (keys(%prefix)) | 
| -	{ | 
| -		if ($prefix{$_} < $prefix{$current}) | 
| -		{ | 
| -			$current = $_; | 
| -		} | 
| -	} | 
| - | 
| -	return($current); | 
| -} | 
| - | 
| - | 
| -# | 
| -# shorten_prefix(prefix) | 
| -# | 
| -# Return PREFIX shortened by last directory component. | 
| -# | 
| - | 
| -sub shorten_prefix($) | 
| -{ | 
| -	my @list = split("/", $_[0]); | 
| - | 
| -	pop(@list); | 
| -	return join("/", @list); | 
| -} | 
| - | 
| - | 
| - | 
| -# | 
| -# get_dir_list(filename_list) | 
| -# | 
| -# Return sorted list of directories for each entry in given FILENAME_LIST. | 
| -# | 
| - | 
| -sub get_dir_list(@) | 
| -{ | 
| -	my %result; | 
| - | 
| -	foreach (@_) | 
| -	{ | 
| -		$result{shorten_prefix($_)} = ""; | 
| -	} | 
| - | 
| -	return(sort(keys(%result))); | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_relative_base_path(subdirectory) | 
| -# | 
| -# Return a relative path string which references the base path when applied | 
| -# in SUBDIRECTORY. | 
| -# | 
| -# Example: get_relative_base_path("fs/mm") -> "../../" | 
| -# | 
| - | 
| -sub get_relative_base_path($) | 
| -{ | 
| -	my $result = ""; | 
| -	my $index; | 
| - | 
| -	# Make an empty directory path a special case | 
| -	if (!$_[0]) { return(""); } | 
| - | 
| -	# Count number of /s in path | 
| -	$index = ($_[0] =~ s/\//\//g); | 
| - | 
| -	# Add a ../ to $result for each / in the directory path + 1 | 
| -	for (; $index>=0; $index--) | 
| -	{ | 
| -		$result .= "../"; | 
| -	} | 
| - | 
| -	return $result; | 
| -} | 
| - | 
| - | 
| -# | 
| -# read_testfile(test_filename) | 
| -# | 
| -# Read in file TEST_FILENAME which contains test descriptions in the format: | 
| -# | 
| -#   TN:<whitespace><test name> | 
| -#   TD:<whitespace><test description> | 
| -# | 
| -# for each test case. Return a reference to a hash containing a mapping | 
| -# | 
| -#   test name -> test description. | 
| -# | 
| -# Die on error. | 
| -# | 
| - | 
| -sub read_testfile($) | 
| -{ | 
| -	my %result; | 
| -	my $test_name; | 
| -	my $changed_testname; | 
| -	local *TEST_HANDLE; | 
| - | 
| -	open(TEST_HANDLE, "<".$_[0]) | 
| -		or die("ERROR: cannot open $_[0]!\n"); | 
| - | 
| -	while (<TEST_HANDLE>) | 
| -	{ | 
| -		chomp($_); | 
| - | 
| -		# Match lines beginning with TN:<whitespace(s)> | 
| -		if (/^TN:\s+(.*?)\s*$/) | 
| -		{ | 
| -			# Store name for later use | 
| -			$test_name = $1; | 
| -			if ($test_name =~ s/\W/_/g) | 
| -			{ | 
| -				$changed_testname = 1; | 
| -			} | 
| -		} | 
| - | 
| -		# Match lines beginning with TD:<whitespace(s)> | 
| -		if (/^TD:\s+(.*?)\s*$/) | 
| -		{ | 
| -			# Check for empty line | 
| -			if ($1) | 
| -			{ | 
| -				# Add description to hash | 
| -				$result{$test_name} .= " $1"; | 
| -			} | 
| -			else | 
| -			{ | 
| -				# Add empty line | 
| -				$result{$test_name} .= "\n\n"; | 
| -			} | 
| -		} | 
| -	} | 
| - | 
| -	close(TEST_HANDLE); | 
| - | 
| -	if ($changed_testname) | 
| -	{ | 
| -		warn("WARNING: invalid characters removed from testname in ". | 
| -		     "descriptions file $_[0]\n"); | 
| -	} | 
| - | 
| -	return \%result; | 
| -} | 
| - | 
| - | 
| -# | 
| -# escape_html(STRING) | 
| -# | 
| -# Return a copy of STRING in which all occurrences of HTML special characters | 
| -# are escaped. | 
| -# | 
| - | 
| -sub escape_html($) | 
| -{ | 
| -	my $string = $_[0]; | 
| - | 
| -	if (!$string) { return ""; } | 
| - | 
| -	$string =~ s/&/&/g;		# & -> & | 
| -	$string =~ s/</</g;		# < -> < | 
| -	$string =~ s/>/>/g;		# > -> > | 
| -	$string =~ s/\"/"/g;	# " -> " | 
| - | 
| -	while ($string =~ /^([^\t]*)(\t)/) | 
| -	{ | 
| -		my $replacement = " "x($tab_size - (length($1) % $tab_size)); | 
| -		$string =~ s/^([^\t]*)(\t)/$1$replacement/; | 
| -	} | 
| - | 
| -	$string =~ s/\n/<br>/g;		# \n -> <br> | 
| - | 
| -	return $string; | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_date_string() | 
| -# | 
| -# Return the current date in the form: yyyy-mm-dd | 
| -# | 
| - | 
| -sub get_date_string() | 
| -{ | 
| -	my $year; | 
| -	my $month; | 
| -	my $day; | 
| - | 
| -	($year, $month, $day) = (localtime())[5, 4, 3]; | 
| - | 
| -	return sprintf("%d-%02d-%02d", $year+1900, $month+1, $day); | 
| -} | 
| - | 
| - | 
| -# | 
| -# create_sub_dir(dir_name) | 
| -# | 
| -# Create subdirectory DIR_NAME if it does not already exist, including all its | 
| -# parent directories. | 
| -# | 
| -# Die on error. | 
| -# | 
| - | 
| -sub create_sub_dir($) | 
| -{ | 
| -	my ($dir) = @_; | 
| - | 
| -	system("mkdir", "-p" ,$dir) | 
| -		and die("ERROR: cannot create directory $dir!\n"); | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_description_file(descriptions, overall_found, overall_hit, | 
| -#                        total_fn_found, total_fn_hit, total_br_found, | 
| -#                        total_br_hit) | 
| -# | 
| -# Write HTML file containing all test case descriptions. DESCRIPTIONS is a | 
| -# reference to a hash containing a mapping | 
| -# | 
| -#   test case name -> test case description | 
| -# | 
| -# Die on error. | 
| -# | 
| - | 
| -sub write_description_file($$$$$$$) | 
| -{ | 
| -	my %description = %{$_[0]}; | 
| -	my $found = $_[1]; | 
| -	my $hit = $_[2]; | 
| -	my $fn_found = $_[3]; | 
| -	my $fn_hit = $_[4]; | 
| -	my $br_found = $_[5]; | 
| -	my $br_hit = $_[6]; | 
| -	my $test_name; | 
| -	local *HTML_HANDLE; | 
| - | 
| -	html_create(*HTML_HANDLE,"descriptions.$html_ext"); | 
| -	write_html_prolog(*HTML_HANDLE, "", "LCOV - test case descriptions"); | 
| -	write_header(*HTML_HANDLE, 3, "", "", $found, $hit, $fn_found, | 
| -		     $fn_hit, $br_found, $br_hit, 0); | 
| - | 
| -	write_test_table_prolog(*HTML_HANDLE, | 
| -			 "Test case descriptions - alphabetical list"); | 
| - | 
| -	foreach $test_name (sort(keys(%description))) | 
| -	{ | 
| -		write_test_table_entry(*HTML_HANDLE, $test_name, | 
| -				       escape_html($description{$test_name})); | 
| -	} | 
| - | 
| -	write_test_table_epilog(*HTML_HANDLE); | 
| -	write_html_epilog(*HTML_HANDLE, ""); | 
| - | 
| -	close(*HTML_HANDLE); | 
| -} | 
| - | 
| - | 
| - | 
| -# | 
| -# write_png_files() | 
| -# | 
| -# Create all necessary .png files for the HTML-output in the current | 
| -# directory. .png-files are used as bar graphs. | 
| -# | 
| -# Die on error. | 
| -# | 
| - | 
| -sub write_png_files() | 
| -{ | 
| -	my %data; | 
| -	local *PNG_HANDLE; | 
| - | 
| -	$data{"ruby.png"} = | 
| -		[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, | 
| -		 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, | 
| -		 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25, | 
| -		 0xdb, 0x56, 0xca, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, | 
| -		 0x45, 0x07, 0xd2, 0x07, 0x11, 0x0f, 0x18, 0x10, 0x5d, 0x57, | 
| -		 0x34, 0x6e, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, | 
| -		 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2, | 
| -		 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, | 
| -		 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, | 
| -		 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0xff, 0x35, 0x2f, | 
| -		 0x00, 0x00, 0x00, 0xd0, 0x33, 0x9a, 0x9d, 0x00, 0x00, 0x00, | 
| -		 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x00, | 
| -		 0x00, 0x00, 0x02, 0x00, 0x01, 0xe5, 0x27, 0xde, 0xfc, 0x00, | 
| -		 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, | 
| -		 0x82]; | 
| -	$data{"amber.png"} = | 
| -		[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, | 
| -		 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, | 
| -		 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25, | 
| -		 0xdb, 0x56, 0xca, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, | 
| -		 0x45, 0x07, 0xd2, 0x07, 0x11, 0x0f, 0x28, 0x04, 0x98, 0xcb, | 
| -		 0xd6, 0xe0, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, | 
| -		 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2, | 
| -		 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, | 
| -		 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, | 
| -		 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xe0, 0x50, | 
| -		 0x00, 0x00, 0x00, 0xa2, 0x7a, 0xda, 0x7e, 0x00, 0x00, 0x00, | 
| -		 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x00, | 
| -	  	 0x00, 0x00, 0x02, 0x00, 0x01, 0xe5, 0x27, 0xde, 0xfc, 0x00, | 
| -		 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, | 
| -		 0x82]; | 
| -	$data{"emerald.png"} = | 
| -		[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, | 
| -		 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, | 
| -		 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25, | 
| -		 0xdb, 0x56, 0xca, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, | 
| -		 0x45, 0x07, 0xd2, 0x07, 0x11, 0x0f, 0x22, 0x2b, 0xc9, 0xf5, | 
| -		 0x03, 0x33, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, | 
| -		 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2, | 
| -		 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, | 
| -		 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, | 
| -		 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0x1b, 0xea, 0x59, | 
| -		 0x0a, 0x0a, 0x0a, 0x0f, 0xba, 0x50, 0x83, 0x00, 0x00, 0x00, | 
| -		 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x00, | 
| -		 0x00, 0x00, 0x02, 0x00, 0x01, 0xe5, 0x27, 0xde, 0xfc, 0x00, | 
| -		 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, | 
| -		 0x82]; | 
| -	$data{"snow.png"} = | 
| -		[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, | 
| -		 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, | 
| -		 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25, | 
| -		 0xdb, 0x56, 0xca, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, | 
| -		 0x45, 0x07, 0xd2, 0x07, 0x11, 0x0f, 0x1e, 0x1d, 0x75, 0xbc, | 
| -		 0xef, 0x55, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, | 
| -		 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2, | 
| -		 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, | 
| -		 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, | 
| -		 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, | 
| -		 0x00, 0x00, 0x00, 0x55, 0xc2, 0xd3, 0x7e, 0x00, 0x00, 0x00, | 
| -		 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x00, | 
| -		 0x00, 0x00, 0x02, 0x00, 0x01, 0xe5, 0x27, 0xde, 0xfc, 0x00, | 
| -		 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, | 
| -		 0x82]; | 
| -	$data{"glass.png"} = | 
| -		[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, | 
| -		 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, | 
| -		 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25, | 
| -		 0xdb, 0x56, 0xca, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, | 
| -		 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, | 
| -		 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, | 
| -		 0x00, 0x00, 0x00, 0x55, 0xc2, 0xd3, 0x7e, 0x00, 0x00, 0x00, | 
| -		 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, | 
| -		 0x00, 0x00, 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, 0x00, 0x88, | 
| -		 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, | 
| -		 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, | 
| -		 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, | 
| -		 0x4d, 0x45, 0x07, 0xd2, 0x07, 0x13, 0x0f, 0x08, 0x19, 0xc4, | 
| -		 0x40, 0x56, 0x10, 0x00, 0x00, 0x00, 0x0a, 0x49, 0x44, 0x41, | 
| -		 0x54, 0x78, 0x9c, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, | 
| -		 0x01, 0x48, 0xaf, 0xa4, 0x71, 0x00, 0x00, 0x00, 0x00, 0x49, | 
| -		 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82]; | 
| -	$data{"updown.png"} = | 
| -		[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, | 
| -		 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x0a, | 
| -		 0x00, 0x00, 0x00, 0x0e, 0x08, 0x06, 0x00, 0x00, 0x00, 0x16, | 
| -		 0xa3, 0x8d, 0xab, 0x00, 0x00, 0x00, 0x3c, 0x49, 0x44, 0x41, | 
| -		 0x54, 0x28, 0xcf, 0x63, 0x60, 0x40, 0x03, 0xff, 0xa1, 0x00, | 
| -		 0x5d, 0x9c, 0x11, 0x5d, 0x11, 0x8a, 0x24, 0x23, 0x23, 0x23, | 
| -		 0x86, 0x42, 0x6c, 0xa6, 0x20, 0x2b, 0x66, 0xc4, 0xa7, 0x08, | 
| -		 0x59, 0x31, 0x23, 0x21, 0x45, 0x30, 0xc0, 0xc4, 0x30, 0x60, | 
| -		 0x80, 0xfa, 0x6e, 0x24, 0x3e, 0x78, 0x48, 0x0a, 0x70, 0x62, | 
| -		 0xa2, 0x90, 0x81, 0xd8, 0x44, 0x01, 0x00, 0xe9, 0x5c, 0x2f, | 
| -		 0xf5, 0xe2, 0x9d, 0x0f, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x49, | 
| -		 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82] if ($sort); | 
| -	foreach (keys(%data)) | 
| -	{ | 
| -		open(PNG_HANDLE, ">".$_) | 
| -			or die("ERROR: cannot create $_!\n"); | 
| -		binmode(PNG_HANDLE); | 
| -		print(PNG_HANDLE map(chr,@{$data{$_}})); | 
| -		close(PNG_HANDLE); | 
| -	} | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_htaccess_file() | 
| -# | 
| - | 
| -sub write_htaccess_file() | 
| -{ | 
| -	local *HTACCESS_HANDLE; | 
| -	my $htaccess_data; | 
| - | 
| -	open(*HTACCESS_HANDLE, ">.htaccess") | 
| -		or die("ERROR: cannot open .htaccess for writing!\n"); | 
| - | 
| -	$htaccess_data = (<<"END_OF_HTACCESS") | 
| -AddEncoding x-gzip .html | 
| -END_OF_HTACCESS | 
| -	; | 
| - | 
| -	print(HTACCESS_HANDLE $htaccess_data); | 
| -	close(*HTACCESS_HANDLE); | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_css_file() | 
| -# | 
| -# Write the cascading style sheet file gcov.css to the current directory. | 
| -# This file defines basic layout attributes of all generated HTML pages. | 
| -# | 
| - | 
| -sub write_css_file() | 
| -{ | 
| -	local *CSS_HANDLE; | 
| - | 
| -	# Check for a specified external style sheet file | 
| -	if ($css_filename) | 
| -	{ | 
| -		# Simply copy that file | 
| -		system("cp", $css_filename, "gcov.css") | 
| -			and die("ERROR: cannot copy file $css_filename!\n"); | 
| -		return; | 
| -	} | 
| - | 
| -	open(CSS_HANDLE, ">gcov.css") | 
| -		or die ("ERROR: cannot open gcov.css for writing!\n"); | 
| - | 
| - | 
| -	# ************************************************************* | 
| - | 
| -	my $css_data = ($_=<<"END_OF_CSS") | 
| -	/* All views: initial background and text color */ | 
| -	body | 
| -	{ | 
| -	  color: #000000; | 
| -	  background-color: #FFFFFF; | 
| -	} | 
| - | 
| -	/* All views: standard link format*/ | 
| -	a:link | 
| -	{ | 
| -	  color: #284FA8; | 
| -	  text-decoration: underline; | 
| -	} | 
| - | 
| -	/* All views: standard link - visited format */ | 
| -	a:visited | 
| -	{ | 
| -	  color: #00CB40; | 
| -	  text-decoration: underline; | 
| -	} | 
| - | 
| -	/* All views: standard link - activated format */ | 
| -	a:active | 
| -	{ | 
| -	  color: #FF0040; | 
| -	  text-decoration: underline; | 
| -	} | 
| - | 
| -	/* All views: main title format */ | 
| -	td.title | 
| -	{ | 
| -	  text-align: center; | 
| -	  padding-bottom: 10px; | 
| -	  font-family: sans-serif; | 
| -	  font-size: 20pt; | 
| -	  font-style: italic; | 
| -	  font-weight: bold; | 
| -	} | 
| - | 
| -	/* All views: header item format */ | 
| -	td.headerItem | 
| -	{ | 
| -	  text-align: right; | 
| -	  padding-right: 6px; | 
| -	  font-family: sans-serif; | 
| -	  font-weight: bold; | 
| -	  vertical-align: top; | 
| -	  white-space: nowrap; | 
| -	} | 
| - | 
| -	/* All views: header item value format */ | 
| -	td.headerValue | 
| -	{ | 
| -	  text-align: left; | 
| -	  color: #284FA8; | 
| -	  font-family: sans-serif; | 
| -	  font-weight: bold; | 
| -	  white-space: nowrap; | 
| -	} | 
| - | 
| -	/* All views: header item coverage table heading */ | 
| -	td.headerCovTableHead | 
| -	{ | 
| -	  text-align: center; | 
| -	  padding-right: 6px; | 
| -	  padding-left: 6px; | 
| -	  padding-bottom: 0px; | 
| -	  font-family: sans-serif; | 
| -	  font-size: 80%; | 
| -	  white-space: nowrap; | 
| -	} | 
| - | 
| -	/* All views: header item coverage table entry */ | 
| -	td.headerCovTableEntry | 
| -	{ | 
| -	  text-align: right; | 
| -	  color: #284FA8; | 
| -	  font-family: sans-serif; | 
| -	  font-weight: bold; | 
| -	  white-space: nowrap; | 
| -	  padding-left: 12px; | 
| -	  padding-right: 4px; | 
| -	  background-color: #DAE7FE; | 
| -	} | 
| - | 
| -	/* All views: header item coverage table entry for high coverage rate */ | 
| -	td.headerCovTableEntryHi | 
| -	{ | 
| -	  text-align: right; | 
| -	  color: #000000; | 
| -	  font-family: sans-serif; | 
| -	  font-weight: bold; | 
| -	  white-space: nowrap; | 
| -	  padding-left: 12px; | 
| -	  padding-right: 4px; | 
| -	  background-color: #A7FC9D; | 
| -	} | 
| - | 
| -	/* All views: header item coverage table entry for medium coverage rate */ | 
| -	td.headerCovTableEntryMed | 
| -	{ | 
| -	  text-align: right; | 
| -	  color: #000000; | 
| -	  font-family: sans-serif; | 
| -	  font-weight: bold; | 
| -	  white-space: nowrap; | 
| -	  padding-left: 12px; | 
| -	  padding-right: 4px; | 
| -	  background-color: #FFEA20; | 
| -	} | 
| - | 
| -	/* All views: header item coverage table entry for ow coverage rate */ | 
| -	td.headerCovTableEntryLo | 
| -	{ | 
| -	  text-align: right; | 
| -	  color: #000000; | 
| -	  font-family: sans-serif; | 
| -	  font-weight: bold; | 
| -	  white-space: nowrap; | 
| -	  padding-left: 12px; | 
| -	  padding-right: 4px; | 
| -	  background-color: #FF0000; | 
| -	} | 
| - | 
| -	/* All views: header legend value for legend entry */ | 
| -	td.headerValueLeg | 
| -	{ | 
| -	  text-align: left; | 
| -	  color: #000000; | 
| -	  font-family: sans-serif; | 
| -	  font-size: 80%; | 
| -	  white-space: nowrap; | 
| -	  padding-top: 4px; | 
| -	} | 
| - | 
| -	/* All views: color of horizontal ruler */ | 
| -	td.ruler | 
| -	{ | 
| -	  background-color: #6688D4; | 
| -	} | 
| - | 
| -	/* All views: version string format */ | 
| -	td.versionInfo | 
| -	{ | 
| -	  text-align: center; | 
| -	  padding-top: 2px; | 
| -	  font-family: sans-serif; | 
| -	  font-style: italic; | 
| -	} | 
| - | 
| -	/* Directory view/File view (all)/Test case descriptions: | 
| -	   table headline format */ | 
| -	td.tableHead | 
| -	{ | 
| -	  text-align: center; | 
| -	  color: #FFFFFF; | 
| -	  background-color: #6688D4; | 
| -	  font-family: sans-serif; | 
| -	  font-size: 120%; | 
| -	  font-weight: bold; | 
| -	  white-space: nowrap; | 
| -	  padding-left: 4px; | 
| -	  padding-right: 4px; | 
| -	} | 
| - | 
| -	span.tableHeadSort | 
| -	{ | 
| -	  padding-right: 4px; | 
| -	} | 
| - | 
| -	/* Directory view/File view (all): filename entry format */ | 
| -	td.coverFile | 
| -	{ | 
| -	  text-align: left; | 
| -	  padding-left: 10px; | 
| -	  padding-right: 20px; | 
| -	  color: #284FA8; | 
| -	  background-color: #DAE7FE; | 
| -	  font-family: monospace; | 
| -	} | 
| - | 
| -	/* Directory view/File view (all): bar-graph entry format*/ | 
| -	td.coverBar | 
| -	{ | 
| -	  padding-left: 10px; | 
| -	  padding-right: 10px; | 
| -	  background-color: #DAE7FE; | 
| -	} | 
| - | 
| -	/* Directory view/File view (all): bar-graph outline color */ | 
| -	td.coverBarOutline | 
| -	{ | 
| -	  background-color: #000000; | 
| -	} | 
| - | 
| -	/* Directory view/File view (all): percentage entry for files with | 
| -	   high coverage rate */ | 
| -	td.coverPerHi | 
| -	{ | 
| -	  text-align: right; | 
| -	  padding-left: 10px; | 
| -	  padding-right: 10px; | 
| -	  background-color: #A7FC9D; | 
| -	  font-weight: bold; | 
| -	  font-family: sans-serif; | 
| -	} | 
| - | 
| -	/* Directory view/File view (all): line count entry for files with | 
| -	   high coverage rate */ | 
| -	td.coverNumHi | 
| -	{ | 
| -	  text-align: right; | 
| -	  padding-left: 10px; | 
| -	  padding-right: 10px; | 
| -	  background-color: #A7FC9D; | 
| -	  white-space: nowrap; | 
| -	  font-family: sans-serif; | 
| -	} | 
| - | 
| -	/* Directory view/File view (all): percentage entry for files with | 
| -	   medium coverage rate */ | 
| -	td.coverPerMed | 
| -	{ | 
| -	  text-align: right; | 
| -	  padding-left: 10px; | 
| -	  padding-right: 10px; | 
| -	  background-color: #FFEA20; | 
| -	  font-weight: bold; | 
| -	  font-family: sans-serif; | 
| -	} | 
| - | 
| -	/* Directory view/File view (all): line count entry for files with | 
| -	   medium coverage rate */ | 
| -	td.coverNumMed | 
| -	{ | 
| -	  text-align: right; | 
| -	  padding-left: 10px; | 
| -	  padding-right: 10px; | 
| -	  background-color: #FFEA20; | 
| -	  white-space: nowrap; | 
| -	  font-family: sans-serif; | 
| -	} | 
| - | 
| -	/* Directory view/File view (all): percentage entry for files with | 
| -	   low coverage rate */ | 
| -	td.coverPerLo | 
| -	{ | 
| -	  text-align: right; | 
| -	  padding-left: 10px; | 
| -	  padding-right: 10px; | 
| -	  background-color: #FF0000; | 
| -	  font-weight: bold; | 
| -	  font-family: sans-serif; | 
| -	} | 
| - | 
| -	/* Directory view/File view (all): line count entry for files with | 
| -	   low coverage rate */ | 
| -	td.coverNumLo | 
| -	{ | 
| -	  text-align: right; | 
| -	  padding-left: 10px; | 
| -	  padding-right: 10px; | 
| -	  background-color: #FF0000; | 
| -	  white-space: nowrap; | 
| -	  font-family: sans-serif; | 
| -	} | 
| - | 
| -	/* File view (all): "show/hide details" link format */ | 
| -	a.detail:link | 
| -	{ | 
| -	  color: #B8D0FF; | 
| -	  font-size:80%; | 
| -	} | 
| - | 
| -	/* File view (all): "show/hide details" link - visited format */ | 
| -	a.detail:visited | 
| -	{ | 
| -	  color: #B8D0FF; | 
| -	  font-size:80%; | 
| -	} | 
| - | 
| -	/* File view (all): "show/hide details" link - activated format */ | 
| -	a.detail:active | 
| -	{ | 
| -	  color: #FFFFFF; | 
| -	  font-size:80%; | 
| -	} | 
| - | 
| -	/* File view (detail): test name entry */ | 
| -	td.testName | 
| -	{ | 
| -	  text-align: right; | 
| -	  padding-right: 10px; | 
| -	  background-color: #DAE7FE; | 
| -	  font-family: sans-serif; | 
| -	} | 
| - | 
| -	/* File view (detail): test percentage entry */ | 
| -	td.testPer | 
| -	{ | 
| -	  text-align: right; | 
| -	  padding-left: 10px; | 
| -	  padding-right: 10px; | 
| -	  background-color: #DAE7FE; | 
| -	  font-family: sans-serif; | 
| -	} | 
| - | 
| -	/* File view (detail): test lines count entry */ | 
| -	td.testNum | 
| -	{ | 
| -	  text-align: right; | 
| -	  padding-left: 10px; | 
| -	  padding-right: 10px; | 
| -	  background-color: #DAE7FE; | 
| -	  font-family: sans-serif; | 
| -	} | 
| - | 
| -	/* Test case descriptions: test name format*/ | 
| -	dt | 
| -	{ | 
| -	  font-family: sans-serif; | 
| -	  font-weight: bold; | 
| -	} | 
| - | 
| -	/* Test case descriptions: description table body */ | 
| -	td.testDescription | 
| -	{ | 
| -	  padding-top: 10px; | 
| -	  padding-left: 30px; | 
| -	  padding-bottom: 10px; | 
| -	  padding-right: 30px; | 
| -	  background-color: #DAE7FE; | 
| -	} | 
| - | 
| -	/* Source code view: function entry */ | 
| -	td.coverFn | 
| -	{ | 
| -	  text-align: left; | 
| -	  padding-left: 10px; | 
| -	  padding-right: 20px; | 
| -	  color: #284FA8; | 
| -	  background-color: #DAE7FE; | 
| -	  font-family: monospace; | 
| -	} | 
| - | 
| -	/* Source code view: function entry zero count*/ | 
| -	td.coverFnLo | 
| -	{ | 
| -	  text-align: right; | 
| -	  padding-left: 10px; | 
| -	  padding-right: 10px; | 
| -	  background-color: #FF0000; | 
| -	  font-weight: bold; | 
| -	  font-family: sans-serif; | 
| -	} | 
| - | 
| -	/* Source code view: function entry nonzero count*/ | 
| -	td.coverFnHi | 
| -	{ | 
| -	  text-align: right; | 
| -	  padding-left: 10px; | 
| -	  padding-right: 10px; | 
| -	  background-color: #DAE7FE; | 
| -	  font-weight: bold; | 
| -	  font-family: sans-serif; | 
| -	} | 
| - | 
| -	/* Source code view: source code format */ | 
| -	pre.source | 
| -	{ | 
| -	  font-family: monospace; | 
| -	  white-space: pre; | 
| -	  margin-top: 2px; | 
| -	} | 
| - | 
| -	/* Source code view: line number format */ | 
| -	span.lineNum | 
| -	{ | 
| -	  background-color: #EFE383; | 
| -	} | 
| - | 
| -	/* Source code view: format for lines which were executed */ | 
| -	td.lineCov, | 
| -	span.lineCov | 
| -	{ | 
| -	  background-color: #CAD7FE; | 
| -	} | 
| - | 
| -	/* Source code view: format for Cov legend */ | 
| -	span.coverLegendCov | 
| -	{ | 
| -	  padding-left: 10px; | 
| -	  padding-right: 10px; | 
| -	  padding-bottom: 2px; | 
| -	  background-color: #CAD7FE; | 
| -	} | 
| - | 
| -	/* Source code view: format for lines which were not executed */ | 
| -	td.lineNoCov, | 
| -	span.lineNoCov | 
| -	{ | 
| -	  background-color: #FF6230; | 
| -	} | 
| - | 
| -	/* Source code view: format for NoCov legend */ | 
| -	span.coverLegendNoCov | 
| -	{ | 
| -	  padding-left: 10px; | 
| -	  padding-right: 10px; | 
| -	  padding-bottom: 2px; | 
| -	  background-color: #FF6230; | 
| -	} | 
| - | 
| -	/* Source code view (function table): standard link - visited format */ | 
| -	td.lineNoCov > a:visited, | 
| -	td.lineCov > a:visited | 
| -	{ | 
| -	  color: black; | 
| -	  text-decoration: underline; | 
| -	} | 
| - | 
| -	/* Source code view: format for lines which were executed only in a | 
| -	   previous version */ | 
| -	span.lineDiffCov | 
| -	{ | 
| -	  background-color: #B5F7AF; | 
| -	} | 
| - | 
| -	/* Source code view: format for branches which were executed | 
| -	 * and taken */ | 
| -	span.branchCov | 
| -	{ | 
| -	  background-color: #CAD7FE; | 
| -	} | 
| - | 
| -	/* Source code view: format for branches which were executed | 
| -	 * but not taken */ | 
| -	span.branchNoCov | 
| -	{ | 
| -	  background-color: #FF6230; | 
| -	} | 
| - | 
| -	/* Source code view: format for branches which were not executed */ | 
| -	span.branchNoExec | 
| -	{ | 
| -	  background-color: #FF6230; | 
| -	} | 
| - | 
| -	/* Source code view: format for the source code heading line */ | 
| -	pre.sourceHeading | 
| -	{ | 
| -	  white-space: pre; | 
| -	  font-family: monospace; | 
| -	  font-weight: bold; | 
| -	  margin: 0px; | 
| -	} | 
| - | 
| -	/* All views: header legend value for low rate */ | 
| -	td.headerValueLegL | 
| -	{ | 
| -	  font-family: sans-serif; | 
| -	  text-align: center; | 
| -	  white-space: nowrap; | 
| -	  padding-left: 4px; | 
| -	  padding-right: 2px; | 
| -	  background-color: #FF0000; | 
| -	  font-size: 80%; | 
| -	} | 
| - | 
| -	/* All views: header legend value for med rate */ | 
| -	td.headerValueLegM | 
| -	{ | 
| -	  font-family: sans-serif; | 
| -	  text-align: center; | 
| -	  white-space: nowrap; | 
| -	  padding-left: 2px; | 
| -	  padding-right: 2px; | 
| -	  background-color: #FFEA20; | 
| -	  font-size: 80%; | 
| -	} | 
| - | 
| -	/* All views: header legend value for hi rate */ | 
| -	td.headerValueLegH | 
| -	{ | 
| -	  font-family: sans-serif; | 
| -	  text-align: center; | 
| -	  white-space: nowrap; | 
| -	  padding-left: 2px; | 
| -	  padding-right: 4px; | 
| -	  background-color: #A7FC9D; | 
| -	  font-size: 80%; | 
| -	} | 
| - | 
| -	/* All views except source code view: legend format for low coverage */ | 
| -	span.coverLegendCovLo | 
| -	{ | 
| -	  padding-left: 10px; | 
| -	  padding-right: 10px; | 
| -	  padding-top: 2px; | 
| -	  background-color: #FF0000; | 
| -	} | 
| - | 
| -	/* All views except source code view: legend format for med coverage */ | 
| -	span.coverLegendCovMed | 
| -	{ | 
| -	  padding-left: 10px; | 
| -	  padding-right: 10px; | 
| -	  padding-top: 2px; | 
| -	  background-color: #FFEA20; | 
| -	} | 
| - | 
| -	/* All views except source code view: legend format for hi coverage */ | 
| -	span.coverLegendCovHi | 
| -	{ | 
| -	  padding-left: 10px; | 
| -	  padding-right: 10px; | 
| -	  padding-top: 2px; | 
| -	  background-color: #A7FC9D; | 
| -	} | 
| -END_OF_CSS | 
| -	; | 
| - | 
| -	# ************************************************************* | 
| - | 
| - | 
| -	# Remove leading tab from all lines | 
| -	$css_data =~ s/^\t//gm; | 
| - | 
| -	print(CSS_HANDLE $css_data); | 
| - | 
| -	close(CSS_HANDLE); | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_bar_graph_code(base_dir, cover_found, cover_hit) | 
| -# | 
| -# Return a string containing HTML code which implements a bar graph display | 
| -# for a coverage rate of cover_hit * 100 / cover_found. | 
| -# | 
| - | 
| -sub get_bar_graph_code($$$) | 
| -{ | 
| -	my $rate; | 
| -	my $alt; | 
| -	my $width; | 
| -	my $remainder; | 
| -	my $png_name; | 
| -	my $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); | 
| - | 
| -	# Decide which .png file to use | 
| -	$png_name = $rate_png[classify_rate($_[1], $_[2], $med_limit, | 
| -					    $hi_limit)]; | 
| - | 
| -	if ($width == 0) | 
| -	{ | 
| -		# Zero coverage | 
| -		$graph_code = (<<END_OF_HTML) | 
| -	        <table border=0 cellspacing=0 cellpadding=1><tr><td class="coverBarOutline"><img src="$_[0]snow.png" width=100 height=10 alt="$alt"></td></tr></table> | 
| -END_OF_HTML | 
| -		; | 
| -	} | 
| -	elsif ($width == 100) | 
| -	{ | 
| -		# Full coverage | 
| -		$graph_code = (<<END_OF_HTML) | 
| -		<table border=0 cellspacing=0 cellpadding=1><tr><td class="coverBarOutline"><img src="$_[0]$png_name" width=100 height=10 alt="$alt"></td></tr></table> | 
| -END_OF_HTML | 
| -		; | 
| -	} | 
| -	else | 
| -	{ | 
| -		# Positive coverage | 
| -		$graph_code = (<<END_OF_HTML) | 
| -		<table border=0 cellspacing=0 cellpadding=1><tr><td class="coverBarOutline"><img src="$_[0]$png_name" width=$width height=10 alt="$alt"><img src="$_[0]snow.png" width=$remainder height=10 alt="$alt"></td></tr></table> | 
| -END_OF_HTML | 
| -		; | 
| -	} | 
| - | 
| -	# Remove leading tabs from all lines | 
| -	$graph_code =~ s/^\t+//gm; | 
| -	chomp($graph_code); | 
| - | 
| -	return($graph_code); | 
| -} | 
| - | 
| -# | 
| -# sub classify_rate(found, hit, med_limit, high_limit) | 
| -# | 
| -# Return 0 for low rate, 1 for medium rate and 2 for hi rate. | 
| -# | 
| - | 
| -sub classify_rate($$$$) | 
| -{ | 
| -	my ($found, $hit, $med, $hi) = @_; | 
| -	my $rate; | 
| - | 
| -	if ($found == 0) { | 
| -		return 2; | 
| -	} | 
| -	$rate = $hit * 100 / $found; | 
| -	if ($rate < $med) { | 
| -		return 0; | 
| -	} elsif ($rate < $hi) { | 
| -		return 1; | 
| -	} | 
| -	return 2; | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_html(filehandle, html_code) | 
| -# | 
| -# Write out HTML_CODE to FILEHANDLE while removing a leading tabulator mark | 
| -# in each line of HTML_CODE. | 
| -# | 
| - | 
| -sub write_html(*$) | 
| -{ | 
| -	local *HTML_HANDLE = $_[0]; | 
| -	my $html_code = $_[1]; | 
| - | 
| -	# Remove leading tab from all lines | 
| -	$html_code =~ s/^\t//gm; | 
| - | 
| -	print(HTML_HANDLE $html_code) | 
| -		or die("ERROR: cannot write HTML data ($!)\n"); | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_html_prolog(filehandle, base_dir, pagetitle) | 
| -# | 
| -# Write an HTML prolog common to all HTML files to FILEHANDLE. PAGETITLE will | 
| -# be used as HTML page title. BASE_DIR contains a relative path which points | 
| -# to the base directory. | 
| -# | 
| - | 
| -sub write_html_prolog(*$$) | 
| -{ | 
| -	my $basedir = $_[1]; | 
| -	my $pagetitle = $_[2]; | 
| -	my $prolog; | 
| - | 
| -	$prolog = $html_prolog; | 
| -	$prolog =~ s/\@pagetitle\@/$pagetitle/g; | 
| -	$prolog =~ s/\@basedir\@/$basedir/g; | 
| - | 
| -	write_html($_[0], $prolog); | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_header_prolog(filehandle, base_dir) | 
| -# | 
| -# Write beginning of page header HTML code. | 
| -# | 
| - | 
| -sub write_header_prolog(*$) | 
| -{ | 
| -	# ************************************************************* | 
| - | 
| -	write_html($_[0], <<END_OF_HTML) | 
| -	  <table width="100%" border=0 cellspacing=0 cellpadding=0> | 
| -	    <tr><td class="title">$title</td></tr> | 
| -	    <tr><td class="ruler"><img src="$_[1]glass.png" width=3 height=3 alt=""></td></tr> | 
| - | 
| -	    <tr> | 
| -	      <td width="100%"> | 
| -	        <table cellpadding=1 border=0 width="100%"> | 
| -END_OF_HTML | 
| -	; | 
| - | 
| -	# ************************************************************* | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_header_line(handle, content) | 
| -# | 
| -# Write a header line with the specified table contents. | 
| -# | 
| - | 
| -sub write_header_line(*@) | 
| -{ | 
| -	my ($handle, @content) = @_; | 
| -	my $entry; | 
| - | 
| -	write_html($handle, "          <tr>\n"); | 
| -	foreach $entry (@content) { | 
| -		my ($width, $class, $text, $colspan) = @{$entry}; | 
| - | 
| -		if (defined($width)) { | 
| -			$width = " width=\"$width\""; | 
| -		} else { | 
| -			$width = ""; | 
| -		} | 
| -		if (defined($class)) { | 
| -			$class = " class=\"$class\""; | 
| -		} else { | 
| -			$class = ""; | 
| -		} | 
| -		if (defined($colspan)) { | 
| -			$colspan = " colspan=\"$colspan\""; | 
| -		} else { | 
| -			$colspan = ""; | 
| -		} | 
| -		$text = "" if (!defined($text)); | 
| -		write_html($handle, | 
| -			   "            <td$width$class$colspan>$text</td>\n"); | 
| -	} | 
| -	write_html($handle, "          </tr>\n"); | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_header_epilog(filehandle, base_dir) | 
| -# | 
| -# Write end of page header HTML code. | 
| -# | 
| - | 
| -sub write_header_epilog(*$) | 
| -{ | 
| -	# ************************************************************* | 
| - | 
| -	write_html($_[0], <<END_OF_HTML) | 
| -	          <tr><td><img src="$_[1]glass.png" width=3 height=3 alt=""></td></tr> | 
| -	        </table> | 
| -	      </td> | 
| -	    </tr> | 
| - | 
| -	    <tr><td class="ruler"><img src="$_[1]glass.png" width=3 height=3 alt=""></td></tr> | 
| -	  </table> | 
| - | 
| -END_OF_HTML | 
| -	; | 
| - | 
| -	# ************************************************************* | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_file_table_prolog(handle, file_heading, ([heading, num_cols], ...)) | 
| -# | 
| -# Write heading for file table. | 
| -# | 
| - | 
| -sub write_file_table_prolog(*$@) | 
| -{ | 
| -	my ($handle, $file_heading, @columns) = @_; | 
| -	my $num_columns = 0; | 
| -	my $file_width; | 
| -	my $col; | 
| -	my $width; | 
| - | 
| -	$width = 20 if (scalar(@columns) == 1); | 
| -	$width = 10 if (scalar(@columns) == 2); | 
| -	$width = 8 if (scalar(@columns) > 2); | 
| - | 
| -	foreach $col (@columns) { | 
| -		my ($heading, $cols) = @{$col}; | 
| - | 
| -		$num_columns += $cols; | 
| -	} | 
| -	$file_width = 100 - $num_columns * $width; | 
| - | 
| -	# Table definition | 
| -	write_html($handle, <<END_OF_HTML); | 
| -	  <center> | 
| -	  <table width="80%" cellpadding=1 cellspacing=1 border=0> | 
| - | 
| -	    <tr> | 
| -	      <td width="$file_width%"><br></td> | 
| -END_OF_HTML | 
| -	# Empty first row | 
| -	foreach $col (@columns) { | 
| -		my ($heading, $cols) = @{$col}; | 
| - | 
| -		while ($cols-- > 0) { | 
| -			write_html($handle, <<END_OF_HTML); | 
| -	      <td width="$width%"></td> | 
| -END_OF_HTML | 
| -		} | 
| -	} | 
| -	# Next row | 
| -	write_html($handle, <<END_OF_HTML); | 
| -	    </tr> | 
| - | 
| -	    <tr> | 
| -	      <td class="tableHead">$file_heading</td> | 
| -END_OF_HTML | 
| -	# Heading row | 
| -	foreach $col (@columns) { | 
| -		my ($heading, $cols) = @{$col}; | 
| -		my $colspan = ""; | 
| - | 
| -		$colspan = " colspan=$cols" if ($cols > 1); | 
| -		write_html($handle, <<END_OF_HTML); | 
| -	      <td class="tableHead"$colspan>$heading</td> | 
| -END_OF_HTML | 
| -	} | 
| -	write_html($handle, <<END_OF_HTML); | 
| -	    </tr> | 
| -END_OF_HTML | 
| -} | 
| - | 
| - | 
| -# write_file_table_entry(handle, base_dir, filename, page_link, | 
| -#			 ([ found, hit, med_limit, hi_limit, graph ], ..) | 
| -# | 
| -# Write an entry of the file table. | 
| -# | 
| - | 
| -sub write_file_table_entry(*$$$@) | 
| -{ | 
| -	my ($handle, $base_dir, $filename, $page_link, @entries) = @_; | 
| -	my $file_code; | 
| -	my $entry; | 
| - | 
| -	# Add link to source if provided | 
| -	if (defined($page_link) && $page_link ne "") { | 
| -		$file_code = "<a href=\"$page_link\">$filename</a>"; | 
| -	} else { | 
| -		$file_code = $filename; | 
| -	} | 
| - | 
| -	# First column: filename | 
| -	write_html($handle, <<END_OF_HTML); | 
| -	    <tr> | 
| -	      <td class="coverFile">$file_code</td> | 
| -END_OF_HTML | 
| -	# Columns as defined | 
| -	foreach $entry (@entries) { | 
| -		my ($found, $hit, $med, $hi, $graph) = @{$entry}; | 
| -		my $bar_graph; | 
| -		my $class; | 
| -		my $rate; | 
| - | 
| -		# Generate bar graph if requested | 
| -		if ($graph) { | 
| -			$bar_graph = get_bar_graph_code($base_dir, $found, | 
| -							$hit); | 
| -			write_html($handle, <<END_OF_HTML); | 
| -	      <td class="coverBar" align="center"> | 
| -	        $bar_graph | 
| -	      </td> | 
| -END_OF_HTML | 
| -		} | 
| -		# Get rate color and text | 
| -		if ($found == 0) { | 
| -			$rate = "-"; | 
| -			$class = "Hi"; | 
| -		} else { | 
| -			$rate = sprintf("%.1f %%", $hit * 100 / $found); | 
| -			$class = $rate_name[classify_rate($found, $hit, | 
| -					    $med, $hi)]; | 
| -		} | 
| -		write_html($handle, <<END_OF_HTML); | 
| -	      <td class="coverPer$class">$rate</td> | 
| -	      <td class="coverNum$class">$hit / $found</td> | 
| -END_OF_HTML | 
| -	} | 
| -	# End of row | 
| -        write_html($handle, <<END_OF_HTML); | 
| -	    </tr> | 
| -END_OF_HTML | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_file_table_detail_entry(filehandle, test_name, ([found, hit], ...)) | 
| -# | 
| -# Write entry for detail section in file table. | 
| -# | 
| - | 
| -sub write_file_table_detail_entry(*$@) | 
| -{ | 
| -	my ($handle, $test, @entries) = @_; | 
| -	my $entry; | 
| - | 
| -	if ($test eq "") { | 
| -		$test = "<span style=\"font-style:italic\"><unnamed></span>"; | 
| -	} elsif ($test =~ /^(.*),diff$/) { | 
| -		$test = $1." (converted)"; | 
| -	} | 
| -	# Testname | 
| -	write_html($handle, <<END_OF_HTML); | 
| -	    <tr> | 
| -	      <td class="testName" colspan=2>$test</td> | 
| -END_OF_HTML | 
| -	# Test data | 
| -	foreach $entry (@entries) { | 
| -		my ($found, $hit) = @{$entry}; | 
| -		my $rate = "-"; | 
| - | 
| -		if ($found > 0) { | 
| -			$rate = sprintf("%.1f %%", $hit * 100 / $found); | 
| -		} | 
| -		write_html($handle, <<END_OF_HTML); | 
| -	      <td class="testPer">$rate</td> | 
| -	      <td class="testNum">$hit / $found</td> | 
| -END_OF_HTML | 
| -	} | 
| - | 
| -        write_html($handle, <<END_OF_HTML); | 
| -	    </tr> | 
| - | 
| -END_OF_HTML | 
| - | 
| -	# ************************************************************* | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_file_table_epilog(filehandle) | 
| -# | 
| -# Write end of file table HTML code. | 
| -# | 
| - | 
| -sub write_file_table_epilog(*) | 
| -{ | 
| -	# ************************************************************* | 
| - | 
| -	write_html($_[0], <<END_OF_HTML) | 
| -	  </table> | 
| -	  </center> | 
| -	  <br> | 
| - | 
| -END_OF_HTML | 
| -	; | 
| - | 
| -	# ************************************************************* | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_test_table_prolog(filehandle, table_heading) | 
| -# | 
| -# Write heading for test case description table. | 
| -# | 
| - | 
| -sub write_test_table_prolog(*$) | 
| -{ | 
| -	# ************************************************************* | 
| - | 
| -	write_html($_[0], <<END_OF_HTML) | 
| -	  <center> | 
| -	  <table width="80%" cellpadding=2 cellspacing=1 border=0> | 
| - | 
| -	    <tr> | 
| -	      <td><br></td> | 
| -	    </tr> | 
| - | 
| -	    <tr> | 
| -	      <td class="tableHead">$_[1]</td> | 
| -	    </tr> | 
| - | 
| -	    <tr> | 
| -	      <td class="testDescription"> | 
| -	        <dl> | 
| -END_OF_HTML | 
| -	; | 
| - | 
| -	# ************************************************************* | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_test_table_entry(filehandle, test_name, test_description) | 
| -# | 
| -# Write entry for the test table. | 
| -# | 
| - | 
| -sub write_test_table_entry(*$$) | 
| -{ | 
| -	# ************************************************************* | 
| - | 
| -	write_html($_[0], <<END_OF_HTML) | 
| -          <dt>$_[1]<a name="$_[1]"> </a></dt> | 
| -          <dd>$_[2]<br><br></dd> | 
| -END_OF_HTML | 
| -	; | 
| - | 
| -	# ************************************************************* | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_test_table_epilog(filehandle) | 
| -# | 
| -# Write end of test description table HTML code. | 
| -# | 
| - | 
| -sub write_test_table_epilog(*) | 
| -{ | 
| -	# ************************************************************* | 
| - | 
| -	write_html($_[0], <<END_OF_HTML) | 
| -	        </dl> | 
| -	      </td> | 
| -	    </tr> | 
| -	  </table> | 
| -	  </center> | 
| -	  <br> | 
| - | 
| -END_OF_HTML | 
| -	; | 
| - | 
| -	# ************************************************************* | 
| -} | 
| - | 
| - | 
| -sub fmt_centered($$) | 
| -{ | 
| -	my ($width, $text) = @_; | 
| -	my $w0 = length($text); | 
| -	my $w1 = int(($width - $w0) / 2); | 
| -	my $w2 = $width - $w0 - $w1; | 
| - | 
| -	return (" "x$w1).$text.(" "x$w2); | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_source_prolog(filehandle) | 
| -# | 
| -# Write start of source code table. | 
| -# | 
| - | 
| -sub write_source_prolog(*) | 
| -{ | 
| -	my $lineno_heading = "         "; | 
| -	my $branch_heading = ""; | 
| -	my $line_heading = fmt_centered($line_field_width, "Line data"); | 
| -	my $source_heading = " Source code"; | 
| - | 
| -	if ($br_coverage) { | 
| -		$branch_heading = fmt_centered($br_field_width, "Branch data"). | 
| -				  " "; | 
| -	} | 
| -	# ************************************************************* | 
| - | 
| -	write_html($_[0], <<END_OF_HTML) | 
| -	  <table cellpadding=0 cellspacing=0 border=0> | 
| -	    <tr> | 
| -	      <td><br></td> | 
| -	    </tr> | 
| -	    <tr> | 
| -	      <td> | 
| -<pre class="sourceHeading">${lineno_heading}${branch_heading}${line_heading} ${source_heading}</pre> | 
| -<pre class="source"> | 
| -END_OF_HTML | 
| -	; | 
| - | 
| -	# ************************************************************* | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_branch_blocks(brdata) | 
| -# | 
| -# Group branches that belong to the same basic block. | 
| -# | 
| -# Returns: [block1, block2, ...] | 
| -# block:   [branch1, branch2, ...] | 
| -# branch:  [block_num, branch_num, taken_count, text_length, open, close] | 
| -# | 
| - | 
| -sub get_branch_blocks($) | 
| -{ | 
| -	my ($brdata) = @_; | 
| -	my $last_block_num; | 
| -	my $block = []; | 
| -	my @blocks; | 
| -	my $i; | 
| -	my $num = br_ivec_len($brdata); | 
| - | 
| -	# Group branches | 
| -	for ($i = 0; $i < $num; $i++) { | 
| -		my ($block_num, $branch, $taken) = br_ivec_get($brdata, $i); | 
| -		my $br; | 
| - | 
| -		if (defined($last_block_num) && $block_num != $last_block_num) { | 
| -			push(@blocks, $block); | 
| -			$block = []; | 
| -		} | 
| -		$br = [$block_num, $branch, $taken, 3, 0, 0]; | 
| -		push(@{$block}, $br); | 
| -		$last_block_num = $block_num; | 
| -	} | 
| -	push(@blocks, $block) if (scalar(@{$block}) > 0); | 
| - | 
| -	# Add braces to first and last branch in group | 
| -	foreach $block (@blocks) { | 
| -		$block->[0]->[$BR_OPEN] = 1; | 
| -		$block->[0]->[$BR_LEN]++; | 
| -		$block->[scalar(@{$block}) - 1]->[$BR_CLOSE] = 1; | 
| -		$block->[scalar(@{$block}) - 1]->[$BR_LEN]++; | 
| -	} | 
| - | 
| -	return @blocks; | 
| -} | 
| - | 
| -# | 
| -# get_block_len(block) | 
| -# | 
| -# Calculate total text length of all branches in a block of branches. | 
| -# | 
| - | 
| -sub get_block_len($) | 
| -{ | 
| -	my ($block) = @_; | 
| -	my $len = 0; | 
| -	my $branch; | 
| - | 
| -	foreach $branch (@{$block}) { | 
| -		$len += $branch->[$BR_LEN]; | 
| -	} | 
| - | 
| -	return $len; | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_branch_html(brdata) | 
| -# | 
| -# Return a list of HTML lines which represent the specified branch coverage | 
| -# data in source code view. | 
| -# | 
| - | 
| -sub get_branch_html($) | 
| -{ | 
| -	my ($brdata) = @_; | 
| -	my @blocks = get_branch_blocks($brdata); | 
| -	my $block; | 
| -	my $branch; | 
| -	my $line_len = 0; | 
| -	my $line = [];	# [branch2|" ", branch|" ", ...] | 
| -	my @lines;	# [line1, line2, ...] | 
| -	my @result; | 
| - | 
| -	# Distribute blocks to lines | 
| -	foreach $block (@blocks) { | 
| -		my $block_len = get_block_len($block); | 
| - | 
| -		# Does this block fit into the current line? | 
| -		if ($line_len + $block_len <= $br_field_width) { | 
| -			# Add it | 
| -			$line_len += $block_len; | 
| -			push(@{$line}, @{$block}); | 
| -			next; | 
| -		} elsif ($block_len <= $br_field_width) { | 
| -			# It would fit if the line was empty - add it to new | 
| -			# line | 
| -			push(@lines, $line); | 
| -			$line_len = $block_len; | 
| -			$line = [ @{$block} ]; | 
| -			next; | 
| -		} | 
| -		# Split the block into several lines | 
| -		foreach $branch (@{$block}) { | 
| -			if ($line_len + $branch->[$BR_LEN] >= $br_field_width) { | 
| -				# Start a new line | 
| -				if (($line_len + 1 <= $br_field_width) && | 
| -				    scalar(@{$line}) > 0 && | 
| -				    !$line->[scalar(@$line) - 1]->[$BR_CLOSE]) { | 
| -					# Try to align branch symbols to be in | 
| -					# one # row | 
| -					push(@{$line}, " "); | 
| -				} | 
| -				push(@lines, $line); | 
| -				$line_len = 0; | 
| -				$line = []; | 
| -			} | 
| -			push(@{$line}, $branch); | 
| -			$line_len += $branch->[$BR_LEN]; | 
| -		} | 
| -	} | 
| -	push(@lines, $line); | 
| - | 
| -	# Convert to HTML | 
| -	foreach $line (@lines) { | 
| -		my $current = ""; | 
| -		my $current_len = 0; | 
| - | 
| -		foreach $branch (@$line) { | 
| -			# Skip alignment space | 
| -			if ($branch eq " ") { | 
| -				$current .= " "; | 
| -				$current_len++; | 
| -				next; | 
| -			} | 
| - | 
| -			my ($block_num, $br_num, $taken, $len, $open, $close) = | 
| -			   @{$branch}; | 
| -			my $class; | 
| -			my $title; | 
| -			my $text; | 
| - | 
| -			if ($taken eq '-') { | 
| -				$class	= "branchNoExec"; | 
| -				$text	= " # "; | 
| -				$title	= "Branch $br_num was not executed"; | 
| -			} elsif ($taken == 0) { | 
| -				$class	= "branchNoCov"; | 
| -				$text	= " - "; | 
| -				$title	= "Branch $br_num was not taken"; | 
| -			} else { | 
| -				$class	= "branchCov"; | 
| -				$text	= " + "; | 
| -				$title	= "Branch $br_num was taken $taken ". | 
| -					  "time"; | 
| -				$title .= "s" if ($taken > 1); | 
| -			} | 
| -			$current .= "[" if ($open); | 
| -			$current .= "<span class=\"$class\" title=\"$title\">"; | 
| -			$current .= $text."</span>"; | 
| -			$current .= "]" if ($close); | 
| -			$current_len += $len; | 
| -		} | 
| - | 
| -		# Right-align result text | 
| -		if ($current_len < $br_field_width) { | 
| -			$current = (" "x($br_field_width - $current_len)). | 
| -				   $current; | 
| -		} | 
| -		push(@result, $current); | 
| -	} | 
| - | 
| -	return @result; | 
| -} | 
| - | 
| - | 
| -# | 
| -# format_count(count, width) | 
| -# | 
| -# Return a right-aligned representation of count that fits in width characters. | 
| -# | 
| - | 
| -sub format_count($$) | 
| -{ | 
| -	my ($count, $width) = @_; | 
| -	my $result; | 
| -	my $exp; | 
| - | 
| -	$result = sprintf("%*.0f", $width, $count); | 
| -	while (length($result) > $width) { | 
| -		last if ($count < 10); | 
| -		$exp++; | 
| -		$count = int($count/10); | 
| -		$result = sprintf("%*s", $width, ">$count*10^$exp"); | 
| -	} | 
| -	return $result; | 
| -} | 
| - | 
| -# | 
| -# write_source_line(filehandle, line_num, source, hit_count, converted, | 
| -#                   brdata, add_anchor) | 
| -# | 
| -# Write formatted source code line. Return a line in a format as needed | 
| -# by gen_png() | 
| -# | 
| - | 
| -sub write_source_line(*$$$$$$) | 
| -{ | 
| -	my ($handle, $line, $source, $count, $converted, $brdata, | 
| -	    $add_anchor) = @_; | 
| -	my $source_format; | 
| -	my $count_format; | 
| -	my $result; | 
| -	my $anchor_start = ""; | 
| -	my $anchor_end = ""; | 
| -	my $count_field_width = $line_field_width - 1; | 
| -	my @br_html; | 
| -	my $html; | 
| - | 
| -	# Get branch HTML data for this line | 
| -	@br_html = get_branch_html($brdata) if ($br_coverage); | 
| - | 
| -	if (!defined($count)) { | 
| -		$result		= ""; | 
| -		$source_format	= ""; | 
| -		$count_format	= " "x$count_field_width; | 
| -	} | 
| -	elsif ($count == 0) { | 
| -		$result		= $count; | 
| -		$source_format	= '<span class="lineNoCov">'; | 
| -		$count_format	= format_count($count, $count_field_width); | 
| -	} | 
| -	elsif ($converted && defined($highlight)) { | 
| -		$result		= "*".$count; | 
| -		$source_format	= '<span class="lineDiffCov">'; | 
| -		$count_format	= format_count($count, $count_field_width); | 
| -	} | 
| -	else { | 
| -		$result		= $count; | 
| -		$source_format	= '<span class="lineCov">'; | 
| -		$count_format	= format_count($count, $count_field_width); | 
| -	} | 
| -	$result .= ":".$source; | 
| - | 
| -	# Write out a line number navigation anchor every $nav_resolution | 
| -	# lines if necessary | 
| -	if ($add_anchor) | 
| -	{ | 
| -		$anchor_start	= "<a name=\"$_[1]\">"; | 
| -		$anchor_end	= "</a>"; | 
| -	} | 
| - | 
| - | 
| -	# ************************************************************* | 
| - | 
| -	$html = $anchor_start; | 
| -	$html .= "<span class=\"lineNum\">".sprintf("%8d", $line)." </span>"; | 
| -	$html .= shift(@br_html).":" if ($br_coverage); | 
| -	$html .= "$source_format$count_format : "; | 
| -	$html .= escape_html($source); | 
| -	$html .= "</span>" if ($source_format); | 
| -	$html .= $anchor_end."\n"; | 
| - | 
| -	write_html($handle, $html); | 
| - | 
| -	if ($br_coverage) { | 
| -		# Add lines for overlong branch information | 
| -		foreach (@br_html) { | 
| -			write_html($handle, "<span class=\"lineNum\">". | 
| -				   "         </span>$_\n"); | 
| -		} | 
| -	} | 
| -	# ************************************************************* | 
| - | 
| -	return($result); | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_source_epilog(filehandle) | 
| -# | 
| -# Write end of source code table. | 
| -# | 
| - | 
| -sub write_source_epilog(*) | 
| -{ | 
| -	# ************************************************************* | 
| - | 
| -	write_html($_[0], <<END_OF_HTML) | 
| -	</pre> | 
| -	      </td> | 
| -	    </tr> | 
| -	  </table> | 
| -	  <br> | 
| - | 
| -END_OF_HTML | 
| -	; | 
| - | 
| -	# ************************************************************* | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_html_epilog(filehandle, base_dir[, break_frames]) | 
| -# | 
| -# Write HTML page footer to FILEHANDLE. BREAK_FRAMES should be set when | 
| -# this page is embedded in a frameset, clicking the URL link will then | 
| -# break this frameset. | 
| -# | 
| - | 
| -sub write_html_epilog(*$;$) | 
| -{ | 
| -	my $basedir = $_[1]; | 
| -	my $break_code = ""; | 
| -	my $epilog; | 
| - | 
| -	if (defined($_[2])) | 
| -	{ | 
| -		$break_code = " target=\"_parent\""; | 
| -	} | 
| - | 
| -	# ************************************************************* | 
| - | 
| -	write_html($_[0], <<END_OF_HTML) | 
| -	  <table width="100%" border=0 cellspacing=0 cellpadding=0> | 
| -	    <tr><td class="ruler"><img src="$_[1]glass.png" width=3 height=3 alt=""></td></tr> | 
| -	    <tr><td class="versionInfo">Generated by: <a href="$lcov_url"$break_code>$lcov_version</a></td></tr> | 
| -	  </table> | 
| -	  <br> | 
| -END_OF_HTML | 
| -	; | 
| - | 
| -	$epilog = $html_epilog; | 
| -	$epilog =~ s/\@basedir\@/$basedir/g; | 
| - | 
| -	write_html($_[0], $epilog); | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_frameset(filehandle, basedir, basename, pagetitle) | 
| -# | 
| -# | 
| - | 
| -sub write_frameset(*$$$) | 
| -{ | 
| -	my $frame_width = $overview_width + 40; | 
| - | 
| -	# ************************************************************* | 
| - | 
| -	write_html($_[0], <<END_OF_HTML) | 
| -	<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"> | 
| - | 
| -	<html lang="en"> | 
| - | 
| -	<head> | 
| -	  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> | 
| -	  <title>$_[3]</title> | 
| -	  <link rel="stylesheet" type="text/css" href="$_[1]gcov.css"> | 
| -	</head> | 
| - | 
| -	<frameset cols="$frame_width,*"> | 
| -	  <frame src="$_[2].gcov.overview.$html_ext" name="overview"> | 
| -	  <frame src="$_[2].gcov.$html_ext" name="source"> | 
| -	  <noframes> | 
| -	    <center>Frames not supported by your browser!<br></center> | 
| -	  </noframes> | 
| -	</frameset> | 
| - | 
| -	</html> | 
| -END_OF_HTML | 
| -	; | 
| - | 
| -	# ************************************************************* | 
| -} | 
| - | 
| - | 
| -# | 
| -# sub write_overview_line(filehandle, basename, line, link) | 
| -# | 
| -# | 
| - | 
| -sub write_overview_line(*$$$) | 
| -{ | 
| -	my $y1 = $_[2] - 1; | 
| -	my $y2 = $y1 + $nav_resolution - 1; | 
| -	my $x2 = $overview_width - 1; | 
| - | 
| -	# ************************************************************* | 
| - | 
| -	write_html($_[0], <<END_OF_HTML) | 
| -	    <area shape="rect" coords="0,$y1,$x2,$y2" href="$_[1].gcov.$html_ext#$_[3]" target="source" alt="overview"> | 
| -END_OF_HTML | 
| -	; | 
| - | 
| -	# ************************************************************* | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_overview(filehandle, basedir, basename, pagetitle, lines) | 
| -# | 
| -# | 
| - | 
| -sub write_overview(*$$$$) | 
| -{ | 
| -	my $index; | 
| -	my $max_line = $_[4] - 1; | 
| -	my $offset; | 
| - | 
| -	# ************************************************************* | 
| - | 
| -	write_html($_[0], <<END_OF_HTML) | 
| -	<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> | 
| - | 
| -	<html lang="en"> | 
| - | 
| -	<head> | 
| -	  <title>$_[3]</title> | 
| -	  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> | 
| -	  <link rel="stylesheet" type="text/css" href="$_[1]gcov.css"> | 
| -	</head> | 
| - | 
| -	<body> | 
| -	  <map name="overview"> | 
| -END_OF_HTML | 
| -	; | 
| - | 
| -	# ************************************************************* | 
| - | 
| -	# Make $offset the next higher multiple of $nav_resolution | 
| -	$offset = ($nav_offset + $nav_resolution - 1) / $nav_resolution; | 
| -	$offset = sprintf("%d", $offset ) * $nav_resolution; | 
| - | 
| -	# Create image map for overview image | 
| -	for ($index = 1; $index <= $_[4]; $index += $nav_resolution) | 
| -	{ | 
| -		# Enforce nav_offset | 
| -		if ($index < $offset + 1) | 
| -		{ | 
| -			write_overview_line($_[0], $_[2], $index, 1); | 
| -		} | 
| -		else | 
| -		{ | 
| -			write_overview_line($_[0], $_[2], $index, $index - $offset); | 
| -		} | 
| -	} | 
| - | 
| -	# ************************************************************* | 
| - | 
| -	write_html($_[0], <<END_OF_HTML) | 
| -	  </map> | 
| - | 
| -	  <center> | 
| -	  <a href="$_[2].gcov.$html_ext#top" target="source">Top</a><br><br> | 
| -	  <img src="$_[2].gcov.png" width=$overview_width height=$max_line alt="Overview" border=0 usemap="#overview"> | 
| -	  </center> | 
| -	</body> | 
| -	</html> | 
| -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) = @_; | 
| - | 
| -	return $a if ($a > $b); | 
| -	return $b; | 
| -} | 
| - | 
| - | 
| -# | 
| -# write_header(filehandle, type, trunc_file_name, rel_file_name, lines_found, | 
| -# lines_hit, funcs_found, funcs_hit, sort_type) | 
| -# | 
| -# Write a complete standard page header. TYPE may be (0, 1, 2, 3, 4) | 
| -# corresponding to (directory view header, file view header, source view | 
| -# header, test case description header, function view header) | 
| -# | 
| - | 
| -sub write_header(*$$$$$$$$$$) | 
| -{ | 
| -	local *HTML_HANDLE = $_[0]; | 
| -	my $type = $_[1]; | 
| -	my $trunc_name = $_[2]; | 
| -	my $rel_filename = $_[3]; | 
| -	my $lines_found = $_[4]; | 
| -	my $lines_hit = $_[5]; | 
| -	my $fn_found = $_[6]; | 
| -	my $fn_hit = $_[7]; | 
| -	my $br_found = $_[8]; | 
| -	my $br_hit = $_[9]; | 
| -	my $sort_type = $_[10]; | 
| -	my $base_dir; | 
| -	my $view; | 
| -	my $test; | 
| -	my $base_name; | 
| -	my $style; | 
| -	my $rate; | 
| -	my @row_left; | 
| -	my @row_right; | 
| -	my $num_rows; | 
| -	my $i; | 
| - | 
| -	$base_name = basename($rel_filename); | 
| - | 
| -	# Prepare text for "current view" field | 
| -	if ($type == $HDR_DIR) | 
| -	{ | 
| -		# Main overview | 
| -		$base_dir = ""; | 
| -		$view = $overview_title; | 
| -	} | 
| -	elsif ($type == $HDR_FILE) | 
| -	{ | 
| -		# Directory overview | 
| -		$base_dir = get_relative_base_path($rel_filename); | 
| -		$view = "<a href=\"$base_dir"."index.$html_ext\">". | 
| -			"$overview_title</a> - $trunc_name"; | 
| -	} | 
| -	elsif ($type == $HDR_SOURCE || $type == $HDR_FUNC) | 
| -	{ | 
| -		# File view | 
| -		my $dir_name = dirname($rel_filename); | 
| - | 
| -		$base_dir = get_relative_base_path($dir_name); | 
| -		if ($frames) | 
| -		{ | 
| -			# Need to break frameset when clicking any of these | 
| -			# links | 
| -			$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"; | 
| -		} | 
| -		else | 
| -		{ | 
| -			$view = "<a href=\"$base_dir"."index.$html_ext\">". | 
| -				"$overview_title</a> - ". | 
| -				"<a href=\"index.$html_ext\">". | 
| -				"$dir_name</a> - $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>)"; | 
| -			} elsif ($type == $HDR_FUNC) { | 
| -				$view .= " (<a href=\"$base_name.gcov.$html_ext\">source</a> / functions)"; | 
| -			} | 
| -			$view .= "</span>"; | 
| -		} | 
| -	} | 
| -	elsif ($type == $HDR_TESTDESC) | 
| -	{ | 
| -		# Test description header | 
| -		$base_dir = ""; | 
| -		$view = "<a href=\"$base_dir"."index.$html_ext\">". | 
| -			"$overview_title</a> - test case descriptions"; | 
| -	} | 
| - | 
| -	# Prepare text for "test" field | 
| -	$test = escape_html($test_title); | 
| - | 
| -	# Append link to test description page if available | 
| -	if (%test_description && ($type != $HDR_TESTDESC)) | 
| -	{ | 
| -		if ($frames && ($type == $HDR_SOURCE || $type == $HDR_FUNC)) | 
| -		{ | 
| -			# Need to break frameset when clicking this link | 
| -			$test .= " ( <span style=\"font-size:80%;\">". | 
| -				 "<a href=\"$base_dir". | 
| -				 "descriptions.$html_ext\" target=\"_parent\">". | 
| -				 "view descriptions</a></span> )"; | 
| -		} | 
| -		else | 
| -		{ | 
| -			$test .= " ( <span style=\"font-size:80%;\">". | 
| -				 "<a href=\"$base_dir". | 
| -				 "descriptions.$html_ext\">". | 
| -				 "view descriptions</a></span> )"; | 
| -		} | 
| -	} | 
| - | 
| -	# Write header | 
| -	write_header_prolog(*HTML_HANDLE, $base_dir); | 
| - | 
| -	# Left row | 
| -	push(@row_left, [[ "10%", "headerItem", "Current view:" ], | 
| -			 [ "35%", "headerValue", $view ]]); | 
| -	push(@row_left, [[undef, "headerItem", "Test:"], | 
| -			 [undef, "headerValue", $test]]); | 
| -	push(@row_left, [[undef, "headerItem", "Date:"], | 
| -			 [undef, "headerValue", $date]]); | 
| - | 
| -	# Right row | 
| -	if ($legend && ($type == $HDR_SOURCE || $type == $HDR_FUNC)) { | 
| -		my $text = <<END_OF_HTML; | 
| -            Lines: | 
| -            <span class="coverLegendCov">hit</span> | 
| -            <span class="coverLegendNoCov">not hit</span> | 
| -END_OF_HTML | 
| -		if ($br_coverage) { | 
| -			$text .= <<END_OF_HTML; | 
| -            | Branches: | 
| -            <span class="coverLegendCov">+</span> taken | 
| -            <span class="coverLegendNoCov">-</span> not taken | 
| -            <span class="coverLegendNoCov">#</span> not executed | 
| -END_OF_HTML | 
| -		} | 
| -		push(@row_left, [[undef, "headerItem", "Legend:"], | 
| -				 [undef, "headerValueLeg", $text]]); | 
| -	} elsif ($legend && ($type != $HDR_TESTDESC)) { | 
| -		my $text = <<END_OF_HTML; | 
| -	    Rating: | 
| -            <span class="coverLegendCovLo" title="Coverage rates below $med_limit % are classified as low">low: < $med_limit %</span> | 
| -            <span class="coverLegendCovMed" title="Coverage rates between $med_limit % and $hi_limit % are classified as medium">medium: >= $med_limit %</span> | 
| -            <span class="coverLegendCovHi" title="Coverage rates of $hi_limit % and more are classified as high">high: >= $hi_limit %</span> | 
| -END_OF_HTML | 
| -		push(@row_left, [[undef, "headerItem", "Legend:"], | 
| -				 [undef, "headerValueLeg", $text]]); | 
| -	} | 
| -	if ($type == $HDR_TESTDESC) { | 
| -		push(@row_right, [[ "55%" ]]); | 
| -	} else { | 
| -		push(@row_right, [["15%", undef, undef ], | 
| -				  ["10%", "headerCovTableHead", "Hit" ], | 
| -				  ["10%", "headerCovTableHead", "Total" ], | 
| -				  ["15%", "headerCovTableHead", "Coverage"]]); | 
| -	} | 
| -	# Line coverage | 
| -	$style = $rate_name[classify_rate($lines_found, $lines_hit, | 
| -					  $med_limit, $hi_limit)]; | 
| -	$rate = format_rate($lines_found, $lines_hit); | 
| -	push(@row_right, [[undef, "headerItem", "Lines:"], | 
| -			  [undef, "headerCovTableEntry", $lines_hit], | 
| -			  [undef, "headerCovTableEntry", $lines_found], | 
| -			  [undef, "headerCovTableEntry$style", $rate]]) | 
| -			if ($type != $HDR_TESTDESC); | 
| -	# Function coverage | 
| -	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); | 
| -		push(@row_right, [[undef, "headerItem", "Functions:"], | 
| -				  [undef, "headerCovTableEntry", $fn_hit], | 
| -				  [undef, "headerCovTableEntry", $fn_found], | 
| -				  [undef, "headerCovTableEntry$style", $rate]]) | 
| -			if ($type != $HDR_TESTDESC); | 
| -	} | 
| -	# Branch coverage | 
| -	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); | 
| -		push(@row_right, [[undef, "headerItem", "Branches:"], | 
| -				  [undef, "headerCovTableEntry", $br_hit], | 
| -				  [undef, "headerCovTableEntry", $br_found], | 
| -				  [undef, "headerCovTableEntry$style", $rate]]) | 
| -			if ($type != $HDR_TESTDESC); | 
| -	} | 
| - | 
| -	# Print rows | 
| -	$num_rows = max(scalar(@row_left), scalar(@row_right)); | 
| -	for ($i = 0; $i < $num_rows; $i++) { | 
| -		my $left = $row_left[$i]; | 
| -		my $right = $row_right[$i]; | 
| - | 
| -		if (!defined($left)) { | 
| -			$left = [[undef, undef, undef], [undef, undef, undef]]; | 
| -		} | 
| -		if (!defined($right)) { | 
| -			$right = []; | 
| -		} | 
| -		write_header_line(*HTML_HANDLE, @{$left}, | 
| -				  [ $i == 0 ? "5%" : undef, undef, undef], | 
| -				  @{$right}); | 
| -	} | 
| - | 
| -	# Fourth line | 
| -	write_header_epilog(*HTML_HANDLE, $base_dir); | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_sorted_keys(hash_ref, sort_type) | 
| -# | 
| - | 
| -sub get_sorted_keys($$) | 
| -{ | 
| -	my ($hash, $type) = @_; | 
| - | 
| -	if ($type == $SORT_FILE) { | 
| -		# Sort by name | 
| -		return sort(keys(%{$hash})); | 
| -	} elsif ($type == $SORT_LINE) { | 
| -		# Sort by line coverage | 
| -		return sort({$hash->{$a}[7] <=> $hash->{$b}[7]} keys(%{$hash})); | 
| -	} elsif ($type == $SORT_FUNC) { | 
| -		# Sort by function coverage; | 
| -		return sort({$hash->{$a}[8] <=> $hash->{$b}[8]}	keys(%{$hash})); | 
| -	} elsif ($type == $SORT_BRANCH) { | 
| -		# Sort by br coverage; | 
| -		return sort({$hash->{$a}[9] <=> $hash->{$b}[9]}	keys(%{$hash})); | 
| -	} | 
| -} | 
| - | 
| -sub get_sort_code($$$) | 
| -{ | 
| -	my ($link, $alt, $base) = @_; | 
| -	my $png; | 
| -	my $link_start; | 
| -	my $link_end; | 
| - | 
| -	if (!defined($link)) { | 
| -		$png = "glass.png"; | 
| -		$link_start = ""; | 
| -		$link_end = ""; | 
| -	} else { | 
| -		$png = "updown.png"; | 
| -		$link_start = '<a href="'.$link.'">'; | 
| -		$link_end = "</a>"; | 
| -	} | 
| - | 
| -	return ' <span class="tableHeadSort">'.$link_start. | 
| -	       '<img src="'.$base.$png.'" width=10 height=14 '. | 
| -	       'alt="'.$alt.'" title="'.$alt.'" border=0>'.$link_end.'</span>'; | 
| -} | 
| - | 
| -sub get_file_code($$$$) | 
| -{ | 
| -	my ($type, $text, $sort_button, $base) = @_; | 
| -	my $result = $text; | 
| -	my $link; | 
| - | 
| -	if ($sort_button) { | 
| -		if ($type == $HEAD_NO_DETAIL) { | 
| -			$link = "index.$html_ext"; | 
| -		} else { | 
| -			$link = "index-detail.$html_ext"; | 
| -		} | 
| -	} | 
| -	$result .= get_sort_code($link, "Sort by name", $base); | 
| - | 
| -	return $result; | 
| -} | 
| - | 
| -sub get_line_code($$$$$) | 
| -{ | 
| -	my ($type, $sort_type, $text, $sort_button, $base) = @_; | 
| -	my $result = $text; | 
| -	my $sort_link; | 
| - | 
| -	if ($type == $HEAD_NO_DETAIL) { | 
| -		# Just text | 
| -		if ($sort_button) { | 
| -			$sort_link = "index-sort-l.$html_ext"; | 
| -		} | 
| -	} elsif ($type == $HEAD_DETAIL_HIDDEN) { | 
| -		# Text + link to detail view | 
| -		$result .= ' ( <a class="detail" href="index-detail'. | 
| -			   $fileview_sortname[$sort_type].'.'.$html_ext. | 
| -			   '">show details</a> )'; | 
| -		if ($sort_button) { | 
| -			$sort_link = "index-sort-l.$html_ext"; | 
| -		} | 
| -	} else { | 
| -		# Text + link to standard view | 
| -		$result .= ' ( <a class="detail" href="index'. | 
| -			   $fileview_sortname[$sort_type].'.'.$html_ext. | 
| -			   '">hide details</a> )'; | 
| -		if ($sort_button) { | 
| -			$sort_link = "index-detail-sort-l.$html_ext"; | 
| -		} | 
| -	} | 
| -	# Add sort button | 
| -	$result .= get_sort_code($sort_link, "Sort by line coverage", $base); | 
| - | 
| -	return $result; | 
| -} | 
| - | 
| -sub get_func_code($$$$) | 
| -{ | 
| -	my ($type, $text, $sort_button, $base) = @_; | 
| -	my $result = $text; | 
| -	my $link; | 
| - | 
| -	if ($sort_button) { | 
| -		if ($type == $HEAD_NO_DETAIL) { | 
| -			$link = "index-sort-f.$html_ext"; | 
| -		} else { | 
| -			$link = "index-detail-sort-f.$html_ext"; | 
| -		} | 
| -	} | 
| -	$result .= get_sort_code($link, "Sort by function coverage", $base); | 
| -	return $result; | 
| -} | 
| - | 
| -sub get_br_code($$$$) | 
| -{ | 
| -	my ($type, $text, $sort_button, $base) = @_; | 
| -	my $result = $text; | 
| -	my $link; | 
| - | 
| -	if ($sort_button) { | 
| -		if ($type == $HEAD_NO_DETAIL) { | 
| -			$link = "index-sort-b.$html_ext"; | 
| -		} else { | 
| -			$link = "index-detail-sort-b.$html_ext"; | 
| -		} | 
| -	} | 
| -	$result .= get_sort_code($link, "Sort by branch coverage", $base); | 
| -	return $result; | 
| -} | 
| - | 
| -# | 
| -# write_file_table(filehandle, base_dir, overview, testhash, testfnchash, | 
| -#                  testbrhash, fileview, sort_type) | 
| -# | 
| -# Write a complete file table. OVERVIEW is a reference to a hash containing | 
| -# the following mapping: | 
| -# | 
| -#   filename -> "lines_found,lines_hit,funcs_found,funcs_hit,page_link, | 
| -#		 func_link" | 
| -# | 
| -# TESTHASH is a reference to the following hash: | 
| -# | 
| -#   filename -> \%testdata | 
| -#   %testdata: name of test affecting this file -> \%testcount | 
| -#   %testcount: line number -> execution count for a single test | 
| -# | 
| -# Heading of first column is "Filename" if FILEVIEW is true, "Directory name" | 
| -# otherwise. | 
| -# | 
| - | 
| -sub write_file_table(*$$$$$$$) | 
| -{ | 
| -	local *HTML_HANDLE = $_[0]; | 
| -	my $base_dir = $_[1]; | 
| -	my $overview = $_[2]; | 
| -	my $testhash = $_[3]; | 
| -	my $testfnchash = $_[4]; | 
| -	my $testbrhash = $_[5]; | 
| -	my $fileview = $_[6]; | 
| -	my $sort_type = $_[7]; | 
| -	my $filename; | 
| -	my $bar_graph; | 
| -	my $hit; | 
| -	my $found; | 
| -	my $fn_found; | 
| -	my $fn_hit; | 
| -	my $br_found; | 
| -	my $br_hit; | 
| -	my $page_link; | 
| -	my $testname; | 
| -	my $testdata; | 
| -	my $testfncdata; | 
| -	my $testbrdata; | 
| -	my %affecting_tests; | 
| -	my $line_code = ""; | 
| -	my $func_code; | 
| -	my $br_code; | 
| -	my $file_code; | 
| -	my @head_columns; | 
| - | 
| -	# Determine HTML code for column headings | 
| -	if (($base_dir ne "") && $show_details) | 
| -	{ | 
| -		my $detailed = keys(%{$testhash}); | 
| - | 
| -		$file_code = get_file_code($detailed ? $HEAD_DETAIL_HIDDEN : | 
| -					$HEAD_NO_DETAIL, | 
| -					$fileview ? "Filename" : "Directory", | 
| -					$sort && $sort_type != $SORT_FILE, | 
| -					$base_dir); | 
| -		$line_code = get_line_code($detailed ? $HEAD_DETAIL_SHOWN : | 
| -					$HEAD_DETAIL_HIDDEN, | 
| -					$sort_type, | 
| -					"Line Coverage", | 
| -					$sort && $sort_type != $SORT_LINE, | 
| -					$base_dir); | 
| -		$func_code = get_func_code($detailed ? $HEAD_DETAIL_HIDDEN : | 
| -					$HEAD_NO_DETAIL, | 
| -					"Functions", | 
| -					$sort && $sort_type != $SORT_FUNC, | 
| -					$base_dir); | 
| -		$br_code = get_br_code($detailed ? $HEAD_DETAIL_HIDDEN : | 
| -					$HEAD_NO_DETAIL, | 
| -					"Branches", | 
| -					$sort && $sort_type != $SORT_BRANCH, | 
| -					$base_dir); | 
| -	} else { | 
| -		$file_code = get_file_code($HEAD_NO_DETAIL, | 
| -					$fileview ? "Filename" : "Directory", | 
| -					$sort && $sort_type != $SORT_FILE, | 
| -					$base_dir); | 
| -		$line_code = get_line_code($HEAD_NO_DETAIL, $sort_type, "Line Coverage", | 
| -					$sort && $sort_type != $SORT_LINE, | 
| -					$base_dir); | 
| -		$func_code = get_func_code($HEAD_NO_DETAIL, "Functions", | 
| -					$sort && $sort_type != $SORT_FUNC, | 
| -					$base_dir); | 
| -		$br_code = get_br_code($HEAD_NO_DETAIL, "Branches", | 
| -					$sort && $sort_type != $SORT_BRANCH, | 
| -					$base_dir); | 
| -	} | 
| -	push(@head_columns, [ $line_code, 3 ]); | 
| -	push(@head_columns, [ $func_code, 2]) if ($func_coverage); | 
| -	push(@head_columns, [ $br_code, 2]) if ($br_coverage); | 
| - | 
| -	write_file_table_prolog(*HTML_HANDLE, $file_code, @head_columns); | 
| - | 
| -	foreach $filename (get_sorted_keys($overview, $sort_type)) | 
| -	{ | 
| -		my @columns; | 
| -		($found, $hit, $fn_found, $fn_hit, $br_found, $br_hit, | 
| -		 $page_link) = @{$overview->{$filename}}; | 
| - | 
| -		# Line coverage | 
| -		push(@columns, [$found, $hit, $med_limit, $hi_limit, 1]); | 
| -		# Function coverage | 
| -		if ($func_coverage) { | 
| -			push(@columns, [$fn_found, $fn_hit, $fn_med_limit, | 
| -					$fn_hi_limit, 0]); | 
| -		} | 
| -		# Branch coverage | 
| -		if ($br_coverage) { | 
| -			push(@columns, [$br_found, $br_hit, $br_med_limit, | 
| -					$br_hi_limit, 0]); | 
| -		} | 
| -		write_file_table_entry(*HTML_HANDLE, $base_dir, $filename, | 
| -				       $page_link, @columns); | 
| - | 
| -		$testdata = $testhash->{$filename}; | 
| -		$testfncdata = $testfnchash->{$filename}; | 
| -		$testbrdata = $testbrhash->{$filename}; | 
| - | 
| -		# Check whether we should write test specific coverage | 
| -		# as well | 
| -		if (!($show_details && $testdata)) { next; } | 
| - | 
| -		# Filter out those tests that actually affect this file | 
| -		%affecting_tests = %{ get_affecting_tests($testdata, | 
| -					$testfncdata, $testbrdata) }; | 
| - | 
| -		# Does any of the tests affect this file at all? | 
| -		if (!%affecting_tests) { next; } | 
| - | 
| -		foreach $testname (keys(%affecting_tests)) | 
| -		{ | 
| -			my @results; | 
| -			($found, $hit, $fn_found, $fn_hit, $br_found, $br_hit) = | 
| -				split(",", $affecting_tests{$testname}); | 
| - | 
| -			# Insert link to description of available | 
| -			if ($test_description{$testname}) | 
| -			{ | 
| -				$testname = "<a href=\"$base_dir". | 
| -					    "descriptions.$html_ext#$testname\">". | 
| -					    "$testname</a>"; | 
| -			} | 
| - | 
| -			push(@results, [$found, $hit]); | 
| -			push(@results, [$fn_found, $fn_hit]) if ($func_coverage); | 
| -			push(@results, [$br_found, $br_hit]) if ($br_coverage); | 
| -			write_file_table_detail_entry(*HTML_HANDLE, $testname, | 
| -				@results); | 
| -		} | 
| -	} | 
| - | 
| -	write_file_table_epilog(*HTML_HANDLE); | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_found_and_hit(hash) | 
| -# | 
| -# Return the count for entries (found) and entries with an execution count | 
| -# greater than zero (hit) in a hash (linenumber -> execution count) as | 
| -# a list (found, hit) | 
| -# | 
| - | 
| -sub get_found_and_hit($) | 
| -{ | 
| -	my %hash = %{$_[0]}; | 
| -	my $found = 0; | 
| -	my $hit = 0; | 
| - | 
| -	# Calculate sum | 
| -	$found = 0; | 
| -	$hit = 0; | 
| - | 
| -	foreach (keys(%hash)) | 
| -	{ | 
| -		$found++; | 
| -		if ($hash{$_}>0) { $hit++; } | 
| -	} | 
| - | 
| -	return ($found, $hit); | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_func_found_and_hit(sumfnccount) | 
| -# | 
| -# Return (f_found, f_hit) for sumfnccount | 
| -# | 
| - | 
| -sub get_func_found_and_hit($) | 
| -{ | 
| -	my ($sumfnccount) = @_; | 
| -	my $function; | 
| -	my $fn_found; | 
| -	my $fn_hit; | 
| - | 
| -	$fn_found = scalar(keys(%{$sumfnccount})); | 
| -	$fn_hit = 0; | 
| -	foreach $function (keys(%{$sumfnccount})) { | 
| -		if ($sumfnccount->{$function} > 0) { | 
| -			$fn_hit++; | 
| -		} | 
| -	} | 
| -	return ($fn_found, $fn_hit); | 
| -} | 
| - | 
| - | 
| -# | 
| -# br_taken_to_num(taken) | 
| -# | 
| -# Convert a branch taken value .info format to number format. | 
| -# | 
| - | 
| -sub br_taken_to_num($) | 
| -{ | 
| -	my ($taken) = @_; | 
| - | 
| -	return 0 if ($taken eq '-'); | 
| -	return $taken + 1; | 
| -} | 
| - | 
| - | 
| -# | 
| -# br_num_to_taken(taken) | 
| -# | 
| -# Convert a branch taken value in number format to .info format. | 
| -# | 
| - | 
| -sub br_num_to_taken($) | 
| -{ | 
| -	my ($taken) = @_; | 
| - | 
| -	return '-' if ($taken == 0); | 
| -	return $taken - 1; | 
| -} | 
| - | 
| - | 
| -# | 
| -# br_taken_add(taken1, taken2) | 
| -# | 
| -# Return the result of taken1 + taken2 for 'branch taken' values. | 
| -# | 
| - | 
| -sub br_taken_add($$) | 
| -{ | 
| -	my ($t1, $t2) = @_; | 
| - | 
| -	return $t1 if (!defined($t2)); | 
| -	return $t2 if (!defined($t1)); | 
| -	return $t1 if ($t2 eq '-'); | 
| -	return $t2 if ($t1 eq '-'); | 
| -	return $t1 + $t2; | 
| -} | 
| - | 
| - | 
| -# | 
| -# br_taken_sub(taken1, taken2) | 
| -# | 
| -# Return the result of taken1 - taken2 for 'branch taken' values. Return 0 | 
| -# if the result would become negative. | 
| -# | 
| - | 
| -sub br_taken_sub($$) | 
| -{ | 
| -	my ($t1, $t2) = @_; | 
| - | 
| -	return $t1 if (!defined($t2)); | 
| -	return undef if (!defined($t1)); | 
| -	return $t1 if ($t1 eq '-'); | 
| -	return $t1 if ($t2 eq '-'); | 
| -	return 0 if $t2 > $t1; | 
| -	return $t1 - $t2; | 
| -} | 
| - | 
| - | 
| -# | 
| -# br_ivec_len(vector) | 
| -# | 
| -# Return the number of entries in the branch coverage vector. | 
| -# | 
| - | 
| -sub br_ivec_len($) | 
| -{ | 
| -	my ($vec) = @_; | 
| - | 
| -	return 0 if (!defined($vec)); | 
| -	return (length($vec) * 8 / $BR_VEC_WIDTH) / $BR_VEC_ENTRIES; | 
| -} | 
| - | 
| - | 
| -# | 
| -# br_ivec_get(vector, number) | 
| -# | 
| -# Return an entry from the branch coverage vector. | 
| -# | 
| - | 
| -sub br_ivec_get($$) | 
| -{ | 
| -	my ($vec, $num) = @_; | 
| -	my $block; | 
| -	my $branch; | 
| -	my $taken; | 
| -	my $offset = $num * $BR_VEC_ENTRIES; | 
| - | 
| -	# Retrieve data from vector | 
| -	$block	= vec($vec, $offset + $BR_BLOCK, $BR_VEC_WIDTH); | 
| -	$branch = vec($vec, $offset + $BR_BRANCH, $BR_VEC_WIDTH); | 
| -	$taken	= vec($vec, $offset + $BR_TAKEN, $BR_VEC_WIDTH); | 
| - | 
| -	# Decode taken value from an integer | 
| -	$taken = br_num_to_taken($taken); | 
| - | 
| -	return ($block, $branch, $taken); | 
| -} | 
| - | 
| - | 
| -# | 
| -# br_ivec_push(vector, block, branch, taken) | 
| -# | 
| -# Add an entry to the branch coverage vector. If an entry with the same | 
| -# branch ID already exists, add the corresponding taken values. | 
| -# | 
| - | 
| -sub br_ivec_push($$$$) | 
| -{ | 
| -	my ($vec, $block, $branch, $taken) = @_; | 
| -	my $offset; | 
| -	my $num = br_ivec_len($vec); | 
| -	my $i; | 
| - | 
| -	$vec = "" if (!defined($vec)); | 
| - | 
| -	# 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); | 
| - | 
| -		next if ($v_block != $block || $v_branch != $branch); | 
| - | 
| -		# Add taken counts | 
| -		$taken = br_taken_add($taken, $v_taken); | 
| -		last; | 
| -	} | 
| - | 
| -	$offset = $i * $BR_VEC_ENTRIES; | 
| -	$taken = br_taken_to_num($taken); | 
| - | 
| -	# Add to vector | 
| -	vec($vec, $offset + $BR_BLOCK, $BR_VEC_WIDTH) = $block; | 
| -	vec($vec, $offset + $BR_BRANCH, $BR_VEC_WIDTH) = $branch; | 
| -	vec($vec, $offset + $BR_TAKEN, $BR_VEC_WIDTH) = $taken; | 
| - | 
| -	return $vec; | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_br_found_and_hit(sumbrcount) | 
| -# | 
| -# Return (br_found, br_hit) for sumbrcount | 
| -# | 
| - | 
| -sub get_br_found_and_hit($) | 
| -{ | 
| -	my ($sumbrcount) = @_; | 
| -	my $line; | 
| -	my $br_found = 0; | 
| -	my $br_hit = 0; | 
| - | 
| -	foreach $line (keys(%{$sumbrcount})) { | 
| -		my $brdata = $sumbrcount->{$line}; | 
| -		my $i; | 
| -		my $num = br_ivec_len($brdata); | 
| - | 
| -		for ($i = 0; $i < $num; $i++) { | 
| -			my $taken; | 
| - | 
| -			(undef, undef, $taken) = br_ivec_get($brdata, $i); | 
| - | 
| -			$br_found++; | 
| -			$br_hit++ if ($taken ne "-" && $taken > 0); | 
| -		} | 
| -	} | 
| - | 
| -	return ($br_found, $br_hit); | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_affecting_tests(testdata, testfncdata, testbrdata) | 
| -# | 
| -# HASHREF contains a mapping filename -> (linenumber -> exec count). Return | 
| -# a hash containing mapping filename -> "lines found, lines hit" for each | 
| -# filename which has a nonzero hit count. | 
| -# | 
| - | 
| -sub get_affecting_tests($$$) | 
| -{ | 
| -	my ($testdata, $testfncdata, $testbrdata) = @_; | 
| -	my $testname; | 
| -	my $testcount; | 
| -	my $testfnccount; | 
| -	my $testbrcount; | 
| -	my %result; | 
| -	my $found; | 
| -	my $hit; | 
| -	my $fn_found; | 
| -	my $fn_hit; | 
| -	my $br_found; | 
| -	my $br_hit; | 
| - | 
| -	foreach $testname (keys(%{$testdata})) | 
| -	{ | 
| -		# Get (line number -> count) hash for this test case | 
| -		$testcount = $testdata->{$testname}; | 
| -		$testfnccount = $testfncdata->{$testname}; | 
| -		$testbrcount = $testbrdata->{$testname}; | 
| - | 
| -		# Calculate sum | 
| -		($found, $hit) = get_found_and_hit($testcount); | 
| -		($fn_found, $fn_hit) = get_func_found_and_hit($testfnccount); | 
| -		($br_found, $br_hit) = get_br_found_and_hit($testbrcount); | 
| - | 
| -		if ($hit>0) | 
| -		{ | 
| -			$result{$testname} = "$found,$hit,$fn_found,$fn_hit,". | 
| -					     "$br_found,$br_hit"; | 
| -		} | 
| -	} | 
| - | 
| -	return(\%result); | 
| -} | 
| - | 
| - | 
| -sub get_hash_reverse($) | 
| -{ | 
| -	my ($hash) = @_; | 
| -	my %result; | 
| - | 
| -	foreach (keys(%{$hash})) { | 
| -		$result{$hash->{$_}} = $_; | 
| -	} | 
| - | 
| -	return \%result; | 
| -} | 
| - | 
| -# | 
| -# write_source(filehandle, source_filename, count_data, checksum_data, | 
| -#              converted_data, func_data, sumbrcount) | 
| -# | 
| -# Write an HTML view of a source code file. Returns a list containing | 
| -# data as needed by gen_png(). | 
| -# | 
| -# Die on error. | 
| -# | 
| - | 
| -sub write_source($$$$$$$) | 
| -{ | 
| -	local *HTML_HANDLE = $_[0]; | 
| -	local *SOURCE_HANDLE; | 
| -	my $source_filename = $_[1]; | 
| -	my %count_data; | 
| -	my $line_number; | 
| -	my @result; | 
| -	my $checkdata = $_[3]; | 
| -	my $converted = $_[4]; | 
| -	my $funcdata  = $_[5]; | 
| -	my $sumbrcount = $_[6]; | 
| -	my $datafunc = get_hash_reverse($funcdata); | 
| -	my $add_anchor; | 
| - | 
| -	if ($_[2]) | 
| -	{ | 
| -		%count_data = %{$_[2]}; | 
| -	} | 
| - | 
| -	open(SOURCE_HANDLE, "<".$source_filename) | 
| -		or die("ERROR: cannot open $source_filename for reading!\n"); | 
| - | 
| -	write_source_prolog(*HTML_HANDLE); | 
| - | 
| -	for ($line_number = 1; <SOURCE_HANDLE> ; $line_number++) | 
| -	{ | 
| -		chomp($_); | 
| - | 
| -		# Also remove CR from line-end | 
| -		s/\015$//; | 
| - | 
| -		# Source code matches coverage data? | 
| -		if (defined($checkdata->{$line_number}) && | 
| -		    ($checkdata->{$line_number} ne md5_base64($_))) | 
| -		{ | 
| -			die("ERROR: checksum mismatch  at $source_filename:". | 
| -			    "$line_number\n"); | 
| -		} | 
| - | 
| -		$add_anchor = 0; | 
| -		if ($frames) { | 
| -			if (($line_number - 1) % $nav_resolution == 0) { | 
| -				$add_anchor = 1; | 
| -			} | 
| -		} | 
| -		if ($func_coverage) { | 
| -			if ($line_number == 1) { | 
| -				$add_anchor = 1; | 
| -			} elsif (defined($datafunc->{$line_number + | 
| -						     $func_offset})) { | 
| -				$add_anchor = 1; | 
| -			} | 
| -		} | 
| -		push (@result, | 
| -		      write_source_line(HTML_HANDLE, $line_number, | 
| -					$_, $count_data{$line_number}, | 
| -					$converted->{$line_number}, | 
| -					$sumbrcount->{$line_number}, $add_anchor)); | 
| -	} | 
| - | 
| -	close(SOURCE_HANDLE); | 
| -	write_source_epilog(*HTML_HANDLE); | 
| -	return(@result); | 
| -} | 
| - | 
| - | 
| -sub funcview_get_func_code($$$) | 
| -{ | 
| -	my ($name, $base, $type) = @_; | 
| -	my $result; | 
| -	my $link; | 
| - | 
| -	if ($sort && $type == 1) { | 
| -		$link = "$name.func.$html_ext"; | 
| -	} | 
| -	$result = "Function Name"; | 
| -	$result .= get_sort_code($link, "Sort by function name", $base); | 
| - | 
| -	return $result; | 
| -} | 
| - | 
| -sub funcview_get_count_code($$$) | 
| -{ | 
| -	my ($name, $base, $type) = @_; | 
| -	my $result; | 
| -	my $link; | 
| - | 
| -	if ($sort && $type == 0) { | 
| -		$link = "$name.func-sort-c.$html_ext"; | 
| -	} | 
| -	$result = "Hit count"; | 
| -	$result .= get_sort_code($link, "Sort by hit count", $base); | 
| - | 
| -	return $result; | 
| -} | 
| - | 
| -# | 
| -# funcview_get_sorted(funcdata, sumfncdata, sort_type) | 
| -# | 
| -# Depending on the value of sort_type, return a list of functions sorted | 
| -# by name (type 0) or by the associated call count (type 1). | 
| -# | 
| - | 
| -sub funcview_get_sorted($$$) | 
| -{ | 
| -	my ($funcdata, $sumfncdata, $type) = @_; | 
| - | 
| -	if ($type == 0) { | 
| -		return sort(keys(%{$funcdata})); | 
| -	} | 
| -	return sort({$sumfncdata->{$b} <=> $sumfncdata->{$a}} | 
| -		    keys(%{$sumfncdata})); | 
| -} | 
| - | 
| -# | 
| -# write_function_table(filehandle, source_file, sumcount, funcdata, | 
| -#		       sumfnccount, testfncdata, sumbrcount, testbrdata, | 
| -#		       base_name, base_dir, sort_type) | 
| -# | 
| -# Write an HTML table listing all functions in a source file, including | 
| -# also function call counts and line coverages inside of each function. | 
| -# | 
| -# Die on error. | 
| -# | 
| - | 
| -sub write_function_table(*$$$$$$$$$$) | 
| -{ | 
| -	local *HTML_HANDLE = $_[0]; | 
| -	my $source = $_[1]; | 
| -	my $sumcount = $_[2]; | 
| -	my $funcdata = $_[3]; | 
| -	my $sumfncdata = $_[4]; | 
| -	my $testfncdata = $_[5]; | 
| -	my $sumbrcount = $_[6]; | 
| -	my $testbrdata = $_[7]; | 
| -	my $name = $_[8]; | 
| -	my $base = $_[9]; | 
| -	my $type = $_[10]; | 
| -	my $func; | 
| -	my $func_code; | 
| -	my $count_code; | 
| - | 
| -	# Get HTML code for headings | 
| -	$func_code = funcview_get_func_code($name, $base, $type); | 
| -	$count_code = funcview_get_count_code($name, $base, $type); | 
| -	write_html(*HTML_HANDLE, <<END_OF_HTML) | 
| -	  <center> | 
| -	  <table width="60%" cellpadding=1 cellspacing=1 border=0> | 
| -	    <tr><td><br></td></tr> | 
| -	    <tr> | 
| -	      <td width="80%" class="tableHead">$func_code</td> | 
| -	      <td width="20%" class="tableHead">$count_code</td> | 
| -	    </tr> | 
| -END_OF_HTML | 
| -	; | 
| - | 
| -	# Get a sorted table | 
| -	foreach $func (funcview_get_sorted($funcdata, $sumfncdata, $type)) { | 
| -		if (!defined($funcdata->{$func})) | 
| -		{ | 
| -			next; | 
| -		} | 
| - | 
| -		my $startline = $funcdata->{$func} - $func_offset; | 
| -		my $name = $func; | 
| -		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 | 
| -		$name = escape_html($name); | 
| -		if ($startline < 1) { | 
| -			$startline = 1; | 
| -		} | 
| -		if ($count == 0) { | 
| -			$countstyle = "coverFnLo"; | 
| -		} else { | 
| -			$countstyle = "coverFnHi"; | 
| -		} | 
| - | 
| -		write_html(*HTML_HANDLE, <<END_OF_HTML) | 
| -	    <tr> | 
| -              <td class="coverFn"><a href="$source#$startline">$name</a></td> | 
| -              <td class="$countstyle">$count</td> | 
| -            </tr> | 
| -END_OF_HTML | 
| -                ; | 
| -	} | 
| -	write_html(*HTML_HANDLE, <<END_OF_HTML) | 
| -	  </table> | 
| -	  <br> | 
| -	  </center> | 
| -END_OF_HTML | 
| -	; | 
| -} | 
| - | 
| - | 
| -# | 
| -# info(printf_parameter) | 
| -# | 
| -# Use printf to write PRINTF_PARAMETER to stdout only when the $quiet flag | 
| -# is not set. | 
| -# | 
| - | 
| -sub info(@) | 
| -{ | 
| -	if (!$quiet) | 
| -	{ | 
| -		# Print info string | 
| -		printf(@_); | 
| -	} | 
| -} | 
| - | 
| - | 
| -# | 
| -# subtract_counts(data_ref, base_ref) | 
| -# | 
| - | 
| -sub subtract_counts($$) | 
| -{ | 
| -	my %data = %{$_[0]}; | 
| -	my %base = %{$_[1]}; | 
| -	my $line; | 
| -	my $data_count; | 
| -	my $base_count; | 
| -	my $hit = 0; | 
| -	my $found = 0; | 
| - | 
| -	foreach $line (keys(%data)) | 
| -	{ | 
| -		$found++; | 
| -		$data_count = $data{$line}; | 
| -		$base_count = $base{$line}; | 
| - | 
| -		if (defined($base_count)) | 
| -		{ | 
| -			$data_count -= $base_count; | 
| - | 
| -			# Make sure we don't get negative numbers | 
| -			if ($data_count<0) { $data_count = 0; } | 
| -		} | 
| - | 
| -		$data{$line} = $data_count; | 
| -		if ($data_count > 0) { $hit++; } | 
| -	} | 
| - | 
| -	return (\%data, $found, $hit); | 
| -} | 
| - | 
| - | 
| -# | 
| -# subtract_fnccounts(data, base) | 
| -# | 
| -# Subtract function call counts found in base from those in data. | 
| -# Return (data, f_found, f_hit). | 
| -# | 
| - | 
| -sub subtract_fnccounts($$) | 
| -{ | 
| -	my %data; | 
| -	my %base; | 
| -	my $func; | 
| -	my $data_count; | 
| -	my $base_count; | 
| -	my $fn_hit = 0; | 
| -	my $fn_found = 0; | 
| - | 
| -	%data = %{$_[0]} if (defined($_[0])); | 
| -	%base = %{$_[1]} if (defined($_[1])); | 
| -	foreach $func (keys(%data)) { | 
| -		$fn_found++; | 
| -		$data_count = $data{$func}; | 
| -		$base_count = $base{$func}; | 
| - | 
| -		if (defined($base_count)) { | 
| -			$data_count -= $base_count; | 
| - | 
| -			# Make sure we don't get negative numbers | 
| -			if ($data_count < 0) { | 
| -				$data_count = 0; | 
| -			} | 
| -		} | 
| - | 
| -		$data{$func} = $data_count; | 
| -		if ($data_count > 0) { | 
| -			$fn_hit++; | 
| -		} | 
| -	} | 
| - | 
| -	return (\%data, $fn_found, $fn_hit); | 
| -} | 
| - | 
| - | 
| -# | 
| -# apply_baseline(data_ref, baseline_ref) | 
| -# | 
| -# Subtract the execution counts found in the baseline hash referenced by | 
| -# BASELINE_REF from actual data in DATA_REF. | 
| -# | 
| - | 
| -sub apply_baseline($$) | 
| -{ | 
| -	my %data_hash = %{$_[0]}; | 
| -	my %base_hash = %{$_[1]}; | 
| -	my $filename; | 
| -	my $testname; | 
| -	my $data; | 
| -	my $data_testdata; | 
| -	my $data_funcdata; | 
| -	my $data_checkdata; | 
| -	my $data_testfncdata; | 
| -	my $data_testbrdata; | 
| -	my $data_count; | 
| -	my $data_testfnccount; | 
| -	my $data_testbrcount; | 
| -	my $base; | 
| -	my $base_checkdata; | 
| -	my $base_sumfnccount; | 
| -	my $base_sumbrcount; | 
| -	my $base_count; | 
| -	my $sumcount; | 
| -	my $sumfnccount; | 
| -	my $sumbrcount; | 
| -	my $found; | 
| -	my $hit; | 
| -	my $fn_found; | 
| -	my $fn_hit; | 
| -	my $br_found; | 
| -	my $br_hit; | 
| - | 
| -	foreach $filename (keys(%data_hash)) | 
| -	{ | 
| -		# Get data set for data and baseline | 
| -		$data = $data_hash{$filename}; | 
| -		$base = $base_hash{$filename}; | 
| - | 
| -		# Skip data entries for which no base entry exists | 
| -		if (!defined($base)) | 
| -		{ | 
| -			next; | 
| -		} | 
| - | 
| -		# Get set entries for data and baseline | 
| -		($data_testdata, undef, $data_funcdata, $data_checkdata, | 
| -		 $data_testfncdata, undef, $data_testbrdata) = | 
| -			get_info_entry($data); | 
| -		(undef, $base_count, undef, $base_checkdata, undef, | 
| -		 $base_sumfnccount, undef, $base_sumbrcount) = | 
| -			get_info_entry($base); | 
| - | 
| -		# Check for compatible checksums | 
| -		merge_checksums($data_checkdata, $base_checkdata, $filename); | 
| - | 
| -		# sumcount has to be calculated anew | 
| -		$sumcount = {}; | 
| -		$sumfnccount = {}; | 
| -		$sumbrcount = {}; | 
| - | 
| -		# For each test case, subtract test specific counts | 
| -		foreach $testname (keys(%{$data_testdata})) | 
| -		{ | 
| -			# Get counts of both data and baseline | 
| -			$data_count = $data_testdata->{$testname}; | 
| -			$data_testfnccount = $data_testfncdata->{$testname}; | 
| -			$data_testbrcount = $data_testbrdata->{$testname}; | 
| - | 
| -			($data_count, undef, $hit) = | 
| -				subtract_counts($data_count, $base_count); | 
| -			($data_testfnccount) = | 
| -				subtract_fnccounts($data_testfnccount, | 
| -						   $base_sumfnccount); | 
| -			($data_testbrcount) = | 
| -				combine_brcount($data_testbrcount, | 
| -						 $base_sumbrcount, $BR_SUB); | 
| - | 
| - | 
| -			# Check whether this test case did hit any line at all | 
| -			if ($hit > 0) | 
| -			{ | 
| -				# Write back resulting hash | 
| -				$data_testdata->{$testname} = $data_count; | 
| -				$data_testfncdata->{$testname} = | 
| -					$data_testfnccount; | 
| -				$data_testbrdata->{$testname} = | 
| -					$data_testbrcount; | 
| -			} | 
| -			else | 
| -			{ | 
| -				# Delete test case which did not impact this | 
| -				# file | 
| -				delete($data_testdata->{$testname}); | 
| -				delete($data_testfncdata->{$testname}); | 
| -				delete($data_testbrdata->{$testname}); | 
| -			} | 
| - | 
| -			# Add counts to sum of counts | 
| -			($sumcount, $found, $hit) = | 
| -				add_counts($sumcount, $data_count); | 
| -			($sumfnccount, $fn_found, $fn_hit) = | 
| -				add_fnccount($sumfnccount, $data_testfnccount); | 
| -			($sumbrcount, $br_found, $br_hit) = | 
| -				combine_brcount($sumbrcount, $data_testbrcount, | 
| -						$BR_ADD); | 
| -		} | 
| - | 
| -		# Write back resulting entry | 
| -		set_info_entry($data, $data_testdata, $sumcount, $data_funcdata, | 
| -			       $data_checkdata, $data_testfncdata, $sumfnccount, | 
| -			       $data_testbrdata, $sumbrcount, $found, $hit, | 
| -			       $fn_found, $fn_hit, $br_found, $br_hit); | 
| - | 
| -		$data_hash{$filename} = $data; | 
| -	} | 
| - | 
| -	return (\%data_hash); | 
| -} | 
| - | 
| - | 
| -# | 
| -# remove_unused_descriptions() | 
| -# | 
| -# Removes all test descriptions from the global hash %test_description which | 
| -# are not present in %info_data. | 
| -# | 
| - | 
| -sub remove_unused_descriptions() | 
| -{ | 
| -	my $filename;		# The current filename | 
| -	my %test_list;		# Hash containing found test names | 
| -	my $test_data;		# Reference to hash test_name -> count_data | 
| -	my $before;		# Initial number of descriptions | 
| -	my $after;		# Remaining number of descriptions | 
| - | 
| -	$before = scalar(keys(%test_description)); | 
| - | 
| -	foreach $filename (keys(%info_data)) | 
| -	{ | 
| -		($test_data) = get_info_entry($info_data{$filename}); | 
| -		foreach (keys(%{$test_data})) | 
| -		{ | 
| -			$test_list{$_} = ""; | 
| -		} | 
| -	} | 
| - | 
| -	# Remove descriptions for tests which are not in our list | 
| -	foreach (keys(%test_description)) | 
| -	{ | 
| -		if (!defined($test_list{$_})) | 
| -		{ | 
| -			delete($test_description{$_}); | 
| -		} | 
| -	} | 
| - | 
| -	$after = scalar(keys(%test_description)); | 
| -	if ($after < $before) | 
| -	{ | 
| -		info("Removed ".($before - $after). | 
| -		     " unused descriptions, $after remaining.\n"); | 
| -	} | 
| -} | 
| - | 
| - | 
| -# | 
| -# apply_prefix(filename, prefix) | 
| -# | 
| -# If FILENAME begins with PREFIX, remove PREFIX from FILENAME and return | 
| -# resulting string, otherwise return FILENAME. | 
| -# | 
| - | 
| -sub apply_prefix($$) | 
| -{ | 
| -	my $filename = $_[0]; | 
| -	my $prefix = $_[1]; | 
| - | 
| -	if (defined($prefix) && ($prefix ne "")) | 
| -	{ | 
| -		if ($filename =~ /^\Q$prefix\E\/(.*)$/) | 
| -		{ | 
| -			return substr($filename, length($prefix) + 1); | 
| -		} | 
| -	} | 
| - | 
| -	return $filename; | 
| -} | 
| - | 
| - | 
| -# | 
| -# system_no_output(mode, parameters) | 
| -# | 
| -# Call an external program using PARAMETERS while suppressing depending on | 
| -# the value of MODE: | 
| -# | 
| -#   MODE & 1: suppress STDOUT | 
| -#   MODE & 2: suppress STDERR | 
| -# | 
| -# Return 0 on success, non-zero otherwise. | 
| -# | 
| - | 
| -sub system_no_output($@) | 
| -{ | 
| -	my $mode = shift; | 
| -	my $result; | 
| -	local *OLD_STDERR; | 
| -	local *OLD_STDOUT; | 
| - | 
| -	# Save old stdout and stderr handles | 
| -	($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"); | 
| - | 
| -	system(@_); | 
| -	$result = $?; | 
| - | 
| -	# Close redirected handles | 
| -	($mode & 1) && close(STDOUT); | 
| -	($mode & 2) && close(STDERR); | 
| - | 
| -	# Restore old handles | 
| -	($mode & 1) && open(STDOUT, ">>&OLD_STDOUT"); | 
| -	($mode & 2) && open(STDERR, ">>&OLD_STDERR"); | 
| - | 
| -	return $result; | 
| -} | 
| - | 
| - | 
| -# | 
| -# read_config(filename) | 
| -# | 
| -# Read configuration file FILENAME and return a reference to a hash containing | 
| -# all valid key=value pairs found. | 
| -# | 
| - | 
| -sub read_config($) | 
| -{ | 
| -	my $filename = $_[0]; | 
| -	my %result; | 
| -	my $key; | 
| -	my $value; | 
| -	local *HANDLE; | 
| - | 
| -	if (!open(HANDLE, "<$filename")) | 
| -	{ | 
| -		warn("WARNING: cannot read configuration file $filename\n"); | 
| -		return undef; | 
| -	} | 
| -	while (<HANDLE>) | 
| -	{ | 
| -		chomp; | 
| -		# Skip comments | 
| -		s/#.*//; | 
| -		# Remove leading blanks | 
| -		s/^\s+//; | 
| -		# Remove trailing blanks | 
| -		s/\s+$//; | 
| -		next unless length; | 
| -		($key, $value) = split(/\s*=\s*/, $_, 2); | 
| -		if (defined($key) && defined($value)) | 
| -		{ | 
| -			$result{$key} = $value; | 
| -		} | 
| -		else | 
| -		{ | 
| -			warn("WARNING: malformed statement in line $. ". | 
| -			     "of configuration file $filename\n"); | 
| -		} | 
| -	} | 
| -	close(HANDLE); | 
| -	return \%result; | 
| -} | 
| - | 
| - | 
| -# | 
| -# apply_config(REF) | 
| -# | 
| -# REF is a reference to a hash containing the following mapping: | 
| -# | 
| -#   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. | 
| -# | 
| - | 
| -sub apply_config($) | 
| -{ | 
| -	my $ref = $_[0]; | 
| - | 
| -	foreach (keys(%{$ref})) | 
| -	{ | 
| -		if (defined($config->{$_})) | 
| -		{ | 
| -			${$ref->{$_}} = $config->{$_}; | 
| -		} | 
| -	} | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_html_prolog(FILENAME) | 
| -# | 
| -# If FILENAME is defined, return contents of file. Otherwise return default | 
| -# HTML prolog. Die on error. | 
| -# | 
| - | 
| -sub get_html_prolog($) | 
| -{ | 
| -	my $filename = $_[0]; | 
| -	my $result = ""; | 
| - | 
| -	if (defined($filename)) | 
| -	{ | 
| -		local *HANDLE; | 
| - | 
| -		open(HANDLE, "<".$filename) | 
| -			or die("ERROR: cannot open html prolog $filename!\n"); | 
| -		while (<HANDLE>) | 
| -		{ | 
| -			$result .= $_; | 
| -		} | 
| -		close(HANDLE); | 
| -	} | 
| -	else | 
| -	{ | 
| -		$result = <<END_OF_HTML | 
| -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> | 
| - | 
| -<html lang="en"> | 
| - | 
| -<head> | 
| -  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> | 
| -  <title>\@pagetitle\@</title> | 
| -  <link rel="stylesheet" type="text/css" href="\@basedir\@gcov.css"> | 
| -</head> | 
| - | 
| -<body> | 
| - | 
| -END_OF_HTML | 
| -		; | 
| -	} | 
| - | 
| -	return $result; | 
| -} | 
| - | 
| - | 
| -# | 
| -# get_html_epilog(FILENAME) | 
| -# | 
| -# If FILENAME is defined, return contents of file. Otherwise return default | 
| -# HTML epilog. Die on error. | 
| -# | 
| -sub get_html_epilog($) | 
| -{ | 
| -	my $filename = $_[0]; | 
| -	my $result = ""; | 
| - | 
| -	if (defined($filename)) | 
| -	{ | 
| -		local *HANDLE; | 
| - | 
| -		open(HANDLE, "<".$filename) | 
| -			or die("ERROR: cannot open html epilog $filename!\n"); | 
| -		while (<HANDLE>) | 
| -		{ | 
| -			$result .= $_; | 
| -		} | 
| -		close(HANDLE); | 
| -	} | 
| -	else | 
| -	{ | 
| -		$result = <<END_OF_HTML | 
| - | 
| -</body> | 
| -</html> | 
| -END_OF_HTML | 
| -		; | 
| -	} | 
| - | 
| -	return $result; | 
| - | 
| -} | 
| - | 
| -sub warn_handler($) | 
| -{ | 
| -	my ($msg) = @_; | 
| - | 
| -	warn("$tool_name: $msg"); | 
| -} | 
| - | 
| -sub die_handler($) | 
| -{ | 
| -	my ($msg) = @_; | 
| - | 
| -	die("$tool_name: $msg"); | 
| -} | 
|  |