Upgrades lcov to 1.10, removes lcov-1.9

BUG=273902

Review URL: https://chromiumcodereview.appspot.com/23189008

git-svn-id: http://src.chromium.org/svn/trunk/src/third_party/lcov@218387 4ff67af0-8c30-449e-8e8b-ad334ec8d88c
diff --git a/CHANGES b/CHANGES
index bb67896..18cfcef 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,183 @@
+Version 1.10
+============
+
+All:
+- Disabled branch coverage processing per default to improve processing
+  performance (can be re-enabled using a config file setting)
+- Added option --rc to override config file settings from the command line
+- Added option --config-file to override the config file location
+- Fixed handling of '<' in filenames
+
+genhtml:
+- Added option --ignore-errors to continue after errors
+- Added man page note to further explain branch coverage output
+- Fixed man page description of default coverage rates
+- Fixed HTML page title for directory pages
+- Fixed handling of special characters in file and directory names
+- Fixed directory prefix calculation
+- Fixed warning when source files are found in root directory
+
+geninfo:
+- Added options --external and --no-external to include/exclude external
+  source files such as standard libary headers
+- Added option --compat to specify compatibility modes
+- Added missing man page sections for --derive-func-data and --no-markers
+- Added support for MinGW output on MSYS. From martin.hopfeld@sse-erfurt.de
+- Added support for gcc 4.7 .gcno file format.  Based on patch by
+  berrange@redhat.com
+- Added auto-detection of gcc-4.7 function record format. Includes
+  suggestions by garnold@google.com
+- Fixed exclusion markers for --derive-func-data.  Reported by bettse@gmail.com
+- Fixed processing of pre-3.3 gcov files. Reported by georgysebastian@gmail.com
+- Fixed handling of '<built-in>.gcov' files
+- Fixed warning about unhandled .gcov files
+- Improved --debug output
+- Removed help text for obsolete parameter --function-coverage
+
+genpng:
+- Fixed handling of empty source files. Reported by: sylvestre@debian.org
+
+lcov:
+- Added options --external and --no-external to include/exclude external source
+  files such as standard libary headers
+- Added option --summary to show summary coverage information
+- Added option --compat to specify compatibility modes
+- Fixed missing Perl version dependency in RPM spec file. Reported by
+  Martin Hopfeld <martin.hopfeld@sse-erfurt.de>
+- Fixed geninfo not recognizing Objective-C functions. Based on patch
+  by abrahamh@web.de
+- Fixed option --no-recursion not being passed to geninfo
+- Fixed capitalization of the term 'Perl'
+- Improved coverage rate calculation to only show 0%/100% when no/full coverage
+  is achieved.  Based on suggestions by Paul.Zimmermann@loria.fr and
+  vincent@vinc17.net
+
+lcovrc:
+- Added description for geninfo_compat setting
+- Added config file setting 'genhtml_charset' to specify HTML charset
+- Added config file setting 'geninfo_external' to include/exclude external
+  source files such as standard libary headers
+- Added config file setting 'geninfo_gcov_all_blocks' to modify lcov's use
+  of gcov's -a option
+- Added config file setting 'geninfo_compat' to specify compatibility modes
+- Added config file setting 'geninfo_adjust_src_path' to enabled source path
+  adjustments.  Inspired by patch by ammon.riley@gmail.com
+- Added config file setting 'geninfo_auto_base' to automatically determine
+  the base directory when collecting coverage data
+- Added config file setting 'lcov_function_coverage' to enable/disable
+  function coverage processing
+- Added config file setting 'lcov_branch_coverage' to enable/disable
+  branch coverage processing
+
+
+Version 1.9
+===========
+
+genhtml:
+- Improved wording for branch representation tooltip text
+- Fixed vertical alignment of HTML branch representation
+
+geninfo:
+- Improved warning message about --initial not generating branch coverage data
+- Debugging messages are now printed to STDERR instead of STDOUT
+- Fixed problem with some .gcno files. Reported by gui@futarque.com.
+  (file.gcno: reached unexpected end of file)
+- Fixed problem with relative build paths. Reported by zhanbiao2000@gmail.com.
+  (cannot find an entry for ^#src#test.c.gcov in .gcno file, skipping file!)
+- Fixed problem where coverage data is missing for some files. Reported by
+  weston_schmidt@open-roadster.com
+- Fixed problem where exclusion markers are ignored when gathering
+  initial coverage data. Reported by ahmed_osman@mentor.com.
+- Fixed large execution counts showing as negative numbers in HTML output.
+  Reported by kkyriako@yahoo.com.
+- Fixed problem that incorrectly associated branches outside of a block with
+  branches inside the first block
+
+lcov:
+- Fixed problem that made lcov ignore --kernel-directory parameters when
+  specifying --initial. Reported by hjia@redhat.com.
+- Added --list-full-path option to prevent lcov from truncating paths in list
+  output
+- Added lcov_list_width and lcov_list_truncate_max directives to the
+  lcov configuration file to allow for list output customization
+- Improved list output
+
+COPYING:
+- Added license text to better comply with GPL recommendations
+
+
+Version 1.8
+===========
+
+gendesc:
+- Fixed problem with single word descriptions
+
+genhtml:
+- Added support for branch coverage measurements
+- Added --demangle-cpp option to convert C++ function names to human readable
+  format. Based on a patch by slava.semushin@gmail.com.
+- Improved color legend: legend display takes up less space in HTML output
+- Improved coverage rate limits: all coverage types use the same limits
+  unless specified otherwise
+- Fixed CRLF line breaks in source code when generating html output. Based
+  on patch by michael.knigge@set-software.de.
+- Fixed warning when $HOME is not set
+- Fixed problem with --baseline-file option. Reported by sixarm@gmail.com.
+  (Undefined subroutine &main::add_fnccounts called at genhtml line 4560.)
+- Fixed problem with --baseline-file option and files without function
+  coverage data (Can't use an undefined value as a HASH reference at genhtml
+  line 4441.)
+- Fixed short-name option ambiguities
+- Fixed --highlight option not showing line data from converted test data
+- Fixed warnings about undefined value used. Reported by nikita@zhuk.fi.
+- Fixed error when processing tracefiles without function data. Reported
+  by richard.corden@gmail.com (Can't use an undefined value as a HASH
+  reference at genhtml line 1506.)
+
+geninfo:
+- Added support for branch coverage measurements
+- Added support for exclusion markers: Users can exclude lines of code from
+  coverage reports by adding keywords to the source code.
+- Added --derive-func-data option
+- Added --debug option to better debug problems with graph files
+- Fixed CRLF line breaks in source code when generating tracefiles. Based on
+  patch by michael.knigge@set-software.de.
+- Fixed problems with unnamed source files
+- Fixed warning when $HOME is not set. Reported by acalando@free.fr.
+- Fixed errors when processing unnamed source files
+- Fixed help text typo
+- Fixed errors when processing incomplete function names in .bb files
+- Fixed filename prefix detection
+- Fixed problem with matching filename
+- Fixed problem when LANG is set to non-english locale. Reported by
+  benoit_belbezet@yahoo.fr.
+- Fixed short-name option ambiguities
+
+genpng:
+- Fixed runtime-warning
+
+lcov:
+- Added support for branch coverage measurements
+- Added support for the linux-2.6.31 upstream gcov kernel support
+- Added --from-package and --to-package options
+- Added --derive-func-data option
+- Added overall coverage result output for more operations
+- Improved output of lcov --list
+- Improved gcov-kernel handling
+- Fixed minor problem with --diff
+- Fixed double-counting of function data
+- Fixed warning when $HOME is not set. Reported by acalando@free.fr.
+- Fixed error when combining tracefiles without function data. Reported by
+  richard.corden@gmail.com. (Can't use an undefined value as a HASH reference
+  at lcov line 1341.)
+- Fixed help text typo
+- Fixed filename prefix detection
+- Fixed lcov ignoring information about converted test data
+
+README: 
+- Added note to mention required -lgcov switch during linking
+
+ 
 Version 1.7:
 ============
 
@@ -85,7 +265,7 @@
 - Updated help text
 - Updated man page
 - Fixed lcov not working when -k is specified more than once
-- Fixed lcov not deleting .gcda files when specifiying -z and -d
+- Fixed lcov not deleting .gcda files when specifying -z and -d
 
 lcovrc:
 - Added geninfo_compat_libtool option
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 0ba2158..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,18 +0,0 @@
-License copied from bin/lcov:
-
-#
-#   Copyright (c) International Business Machines  Corp., 2002,2007
-#
-#   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
diff --git a/Makefile b/Makefile
index f6063e5..7cb838a 100644
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,7 @@
 #   - clean:     remove all generated files
 #
 
-VERSION := 1.7
+VERSION := 1.10
 RELEASE := 1
 
 CFG_DIR := $(PREFIX)/etc
@@ -37,18 +37,18 @@
 	make -C example clean
 
 install:
-	bin/install.sh bin/lcov $(BIN_DIR)/lcov
-	bin/install.sh bin/genhtml $(BIN_DIR)/genhtml
-	bin/install.sh bin/geninfo $(BIN_DIR)/geninfo
-	bin/install.sh bin/genpng $(BIN_DIR)/genpng
-	bin/install.sh bin/gendesc $(BIN_DIR)/gendesc
-	bin/install.sh man/lcov.1 $(MAN_DIR)/man1/lcov.1
-	bin/install.sh man/genhtml.1 $(MAN_DIR)/man1/genhtml.1
-	bin/install.sh man/geninfo.1 $(MAN_DIR)/man1/geninfo.1
-	bin/install.sh man/genpng.1 $(MAN_DIR)/man1/genpng.1
-	bin/install.sh man/gendesc.1 $(MAN_DIR)/man1/gendesc.1
-	bin/install.sh man/lcovrc.5 $(MAN_DIR)/man5/lcovrc.5
-	bin/install.sh lcovrc $(CFG_DIR)/lcovrc
+	bin/install.sh bin/lcov $(BIN_DIR)/lcov -m 755
+	bin/install.sh bin/genhtml $(BIN_DIR)/genhtml -m 755
+	bin/install.sh bin/geninfo $(BIN_DIR)/geninfo -m 755
+	bin/install.sh bin/genpng $(BIN_DIR)/genpng -m 755
+	bin/install.sh bin/gendesc $(BIN_DIR)/gendesc -m 755
+	bin/install.sh man/lcov.1 $(MAN_DIR)/man1/lcov.1 -m 644
+	bin/install.sh man/genhtml.1 $(MAN_DIR)/man1/genhtml.1 -m 644
+	bin/install.sh man/geninfo.1 $(MAN_DIR)/man1/geninfo.1 -m 644
+	bin/install.sh man/genpng.1 $(MAN_DIR)/man1/genpng.1 -m 644
+	bin/install.sh man/gendesc.1 $(MAN_DIR)/man1/gendesc.1 -m 644
+	bin/install.sh man/lcovrc.5 $(MAN_DIR)/man5/lcovrc.5 -m 644
+	bin/install.sh lcovrc $(CFG_DIR)/lcovrc -m 644
 
 uninstall:
 	bin/install.sh --uninstall bin/lcov $(BIN_DIR)/lcov
diff --git a/README b/README
index ad36451..7fcd438 100644
--- a/README
+++ b/README
@@ -1,13 +1,13 @@
 -------------------------------------------------
 - README file for the LTP GCOV extension (LCOV) -
-- Last changes: 2008-11-17                      -
+- Last changes: 2012-10-10                      -
 -------------------------------------------------
 
 Description
 -----------
   LCOV is an extension of GCOV, a GNU tool which provides information about
   what parts of a program are actually executed (i.e. "covered") while running
-  a particular test case. The extension consists of a set of PERL scripts
+  a particular test case. The extension consists of a set of Perl scripts
   which build on the textual GCOV output to implement the following enhanced
   functionality:
 
@@ -80,7 +80,7 @@
   http://sourceforge.net/projects/ltp
 
 Copy the resulting gcov kernel module file to either the system wide modules
-directory or the same directory as the PERL scripts. As root, do the following:
+directory or the same directory as the Perl scripts. As root, do the following:
 
   a) Resetting counters
 
@@ -100,8 +100,10 @@
 4. An example of how to access coverage data for a user space program
 ---------------------------------------------------------------------
 Requirements: compile the program in question using GCC with the options
--fprofile-arcs and -ftest-coverage. Assuming the compile directory is called
-"appdir", do the following:
+-fprofile-arcs and -ftest-coverage. During linking, make sure to specify
+-lgcov or -coverage.
+
+Assuming the compile directory is called "appdir", do the following:
 
   a) Resetting counters
 
@@ -118,6 +120,13 @@
 
 Point the web browser of your choice to the resulting index.html file.
 
+Please note that independently of where the application is installed or
+from which directory it is run, the --directory statement needs to
+point to the directory in which the application was compiled.
+
+For further information on the gcc profiling mechanism, please also
+consult the gcov man page.
+
 
 5. Questions and comments
 -------------------------
diff --git a/README.chromium b/README.chromium
index 81da5d4..03f6af9 100644
--- a/README.chromium
+++ b/README.chromium
@@ -1,13 +1,17 @@
 Name: LCOV - the LTP GCOV extension
 Short Name: lcov
 URL: http://ltp.sourceforge.net/coverage/lcov.php
-Version: 1.7
+Version: 1.10
 License: GPL v2
+License file: COPYING
 Security Critical: no
 
 Description:
-This directory contains a stock lcov-1.7 with the following changes:
+This directory contains a stock lcov-1.10 with the following changes:
 
 - Added bin/mcov, an assembly script derived from lcov which is
  particularly useful for dealing with Mac XCode build paths.
-- Added LICENSE file pulled from the top of bin/lcov.
+- Added LICENSE file which symlinks to COPYING
+
+N.B.: This code will not be shipped with Chrome.
+      It is only part of the build/test infrastructure.
\ No newline at end of file
diff --git a/bin/gendesc b/bin/gendesc
index e7a8113..f4cfcbc 100755
--- a/bin/gendesc
+++ b/bin/gendesc
@@ -41,7 +41,7 @@
 
 
 # Constants
-our $lcov_version	= "LCOV version 1.7";
+our $lcov_version	= 'LCOV version 1.10';
 our $lcov_url		= "http://ltp.sourceforge.net/coverage/lcov.php";
 our $tool_name		= basename($0);
 
@@ -67,6 +67,9 @@
 $SIG{__WARN__} = \&warn_handler;
 $SIG{__DIE__} = \&die_handler;
 
+# Prettify version string
+$lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
+
 # Parse command line options
 if (!GetOptions("output-filename=s" => \$output_filename,
 		"version" =>\$version,
@@ -150,13 +153,13 @@
 	local *OUTPUT_HANDLE;
 	my $empty_line = "ignore";
 
-	open(INPUT_HANDLE, $input_filename)
+	open(INPUT_HANDLE, "<", $input_filename)
 		or die("ERROR: cannot open $input_filename!\n");
 
 	# Open output file for writing
 	if ($output_filename)
 	{
-		open(OUTPUT_HANDLE, ">$output_filename")
+		open(OUTPUT_HANDLE, ">", $output_filename)
 			or die("ERROR: cannot create $output_filename!\n");
 	}
 	else
@@ -169,7 +172,7 @@
 	{
 		chomp($_);
 
-		if (/^\s*(\w[\w-]*)(\s*)$/)
+		if (/^(\w[\w-]*)(\s*)$/)
 		{
 			# Matched test name
 			# Name starts with alphanum or _, continues with
diff --git a/bin/genhtml b/bin/genhtml
index af573ff..8979f8c 100755
--- a/bin/genhtml
+++ b/bin/genhtml
@@ -1,6 +1,6 @@
 #!/usr/bin/perl -w
 #
-#   Copyright (c) International Business Machines  Corp., 2002
+#   Copyright (c) International Business Machines  Corp., 2002,2012
 #
 #   This program is free software;  you can redistribute it and/or modify
 #   it under the terms of the GNU General Public License as published by
@@ -72,7 +72,7 @@
 
 # Global constants
 our $title		= "LCOV - code coverage report";
-our $lcov_version	= "LCOV version 1.7";
+our $lcov_version	= 'LCOV version 1.10';
 our $lcov_url		= "http://ltp.sourceforge.net/coverage/lcov.php";
 our $tool_name		= basename($0);
 
@@ -81,13 +81,17 @@
 # MED: $med_limit <= rate <  $hi_limit    graph color: orange
 # LO:          0  <= rate <  $med_limit   graph color: red
 
-# For line coverage
-our $hi_limit	= 50;
-our $med_limit	= 15;
+# For line coverage/all coverage types if not specified
+our $hi_limit = 90;
+our $med_limit = 75;
 
 # For function coverage
-our $fn_hi_limit	= 90;
-our $fn_med_limit	= 75;
+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;
@@ -107,7 +111,55 @@
 # specifies that offset in lines.
 our $func_offset = 2;
 
-our $overview_title = "directory";
+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;
+
+# Error classes which users may specify to ignore during processing
+our $ERROR_SOURCE	= 0;
+our %ERROR_ID = (
+	"source" => $ERROR_SOURCE,
+);
 
 # Data related prototypes
 sub print_usage(*);
@@ -118,21 +170,20 @@
 sub info(@);
 sub read_info_file($);
 sub get_info_entry($);
-sub set_info_entry($$$$$$$;$$$$);
-sub get_prefix(@);
+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 split_filename($);
 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 get_affecting_tests($$$);
 sub combine_info_files($$);
 sub merge_checksums($$$);
 sub combine_info_entries($$$);
@@ -142,6 +193,19 @@
 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($);
+sub parse_ignore_errors(@);
+sub rate($$;$$$);
 
 
 # HTML related prototypes
@@ -151,32 +215,31 @@
 sub write_png_files();
 sub write_htaccess_file();
 sub write_css_file();
-sub write_description_file($$$$$);
-sub write_function_rable(*$$$);
+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(*$$$$$$$$$$);
 sub write_header_prolog(*$);
-sub write_header_line(*$@);
+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_heading(*$$$);
-sub write_file_table_detail_entry(*$$$$$);
+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($$$$$$$);
 sub write_source_prolog(*);
-sub write_source_line(*$$$$$);
+sub write_source_line(*$$$$$$);
 sub write_source_epilog(*);
 
 sub write_frameset(*$$$);
@@ -204,8 +267,10 @@
 our $version;		# Version option flag
 our $show_details;	# If set, generate detailed directory view
 our $no_prefix;		# If set, do not remove filename prefix
-our $func_coverage = 1;	# If set, generate function coverage statistics
+our $func_coverage;	# If set, generate function coverage statistics
 our $no_func_coverage;	# Disable func_coverage
+our $br_coverage;	# 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
@@ -221,11 +286,19 @@
 our $html_epilog;	# Actual HTML epilog
 our $html_ext = "html";	# Extension for generated HTML files
 our $html_gzip = 0;	# Compress with gzip
+our $demangle_cpp = 0;	# Demangle C++ function names
+our @opt_ignore_errors;	# Ignore certain error classes during processing
+our @ignore;
+our $opt_config_file;	# User-specified configuration file location
+our %opt_rc;
+our $charset = "UTF-8";	# Default charset for HTML pages
 our @fileview_sortlist;
-our @fileview_sortname = ("", "-sort-l", "-sort-f");
+our @fileview_sortname = ("", "-sort-l", "-sort-f", "-sort-b");
 our @funcview_sortlist;
 our @rate_name = ("Lo", "Med", "Hi");
 our @rate_png = ("ruby.png", "amber.png", "emerald.png");
+our $lcov_func_coverage = 1;
+our $lcov_branch_coverage = 0;
 
 our $cwd = `pwd`;	# Current working directory
 chomp($cwd);
@@ -239,14 +312,25 @@
 $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";
 }
 
+# Check command line for a configuration file name
+Getopt::Long::Configure("pass_through", "no_auto_abbrev");
+GetOptions("config-file=s" => \$opt_config_file,
+	   "rc=s%" => \%opt_rc);
+Getopt::Long::Configure("default");
+
 # Read configuration file if available
-if (-r $ENV{"HOME"}."/.lcovrc")
+if (defined($opt_config_file)) {
+	$config = read_config($opt_config_file);
+} elsif (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
 {
 	$config = read_config($ENV{"HOME"}."/.lcovrc");
 }
@@ -255,13 +339,14 @@
 	$config = read_config("/etc/lcovrc");
 }
 
-if ($config)
+if ($config || %opt_rc)
 {
-	# Copy configuration file values to variables
+	# Copy configuration file and --rc values to variables
 	apply_config({
 		"genhtml_css_file"		=> \$css_filename,
 		"genhtml_hi_limit"		=> \$hi_limit,
 		"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,
@@ -278,36 +363,57 @@
 		"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,
+		"genhtml_charset"		=> \$charset,
+		"lcov_function_coverage"	=> \$lcov_func_coverage,
+		"lcov_branch_coverage"		=> \$lcov_branch_coverage,
 		});
 }
 
+# Copy related values if not specified
+$fn_hi_limit	= $hi_limit if (!defined($fn_hi_limit));
+$fn_med_limit	= $med_limit if (!defined($fn_med_limit));
+$br_hi_limit	= $hi_limit if (!defined($br_hi_limit));
+$br_med_limit	= $med_limit if (!defined($br_med_limit));
+$func_coverage	= $lcov_func_coverage if (!defined($func_coverage));
+$br_coverage	= $lcov_branch_coverage if (!defined($br_coverage));
+
 # Parse command line options
-if (!GetOptions("output-directory=s"	=> \$output_directory,
-		"title=s"		=> \$test_title,
-		"description-file=s"	=> \$desc_filename,
-		"keep-descriptions"	=> \$keep_descriptions,
-		"css-file=s"		=> \$css_filename,
-		"baseline-file=s"	=> \$base_filename,
-		"prefix=s"		=> \$dir_prefix,
+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"		=> \$show_details,
-		"frames"		=> \$frames,
+		"show-details|s"	=> \$show_details,
+		"frames|f"		=> \$frames,
 		"highlight"		=> \$highlight,
 		"legend"		=> \$legend,
-		"quiet"			=> \$quiet,
+		"quiet|q"		=> \$quiet,
 		"help|h|?"		=> \$help,
-		"version"		=> \$version,
+		"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,
+		"ignore-errors=s"	=> \@opt_ignore_errors,
+		"config-file=s"		=> \$opt_config_file,
+		"rc=s%"			=> \%opt_rc,
 		))
 {
 	print(STDERR "Use $tool_name --help to get usage information\n");
@@ -317,6 +423,9 @@
 	if ($no_func_coverage) {
 		$func_coverage = 0;
 	}
+	if ($no_br_coverage) {
+		$br_coverage = 0;
+	}
 
 	# Merge sort options
 	if ($no_sort) {
@@ -340,6 +449,9 @@
 	exit(0);
 }
 
+# Determine which errors the user wants us to ignore
+parse_ignore_errors(@opt_ignore_errors);
+
 # Check for info filename
 if (!@info_filenames)
 {
@@ -400,16 +512,14 @@
 	$dir_prefix = undef;
 }
 
+@fileview_sortlist = ($SORT_FILE);
+@funcview_sortlist = ($SORT_FILE);
+
 if ($sort) {
-	@funcview_sortlist = (0, 1);
-	if ($func_coverage) {
-		@fileview_sortlist = (0, 1, 2);
-	} else {
-		@fileview_sortlist = (0, 1);
-	}
-} else {
-	@fileview_sortlist = (0);
-	@funcview_sortlist = (0);
+	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)
@@ -418,6 +528,15 @@
 	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)
 {
@@ -425,8 +544,7 @@
 
 	if (! -e _)
 	{
-		system("mkdir", "-p", $output_directory)
-			and die("ERROR: cannot create directory $_!\n");
+		create_sub_dir($output_directory);
 	}
 }
 
@@ -457,6 +575,9 @@
   -h, --help                        Print this help, then exit
   -v, --version                     Print version number, then exit
   -q, --quiet                       Do not print progress messages
+      --config-file FILENAME        Specify configuration file location
+      --rc SETTING=VALUE            Override configuration file setting
+      --ignore-errors ERRORS        Continue after ERRORS (source)
 
 Operation:
   -o, --output-directory OUTDIR     Write HTML output to OUTDIR
@@ -467,6 +588,7 @@
   -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
@@ -481,6 +603,7 @@
       --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
@@ -508,6 +631,49 @@
 
 
 #
+# 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 rate($hit, $found, "% ($hit of $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.
@@ -527,10 +693,14 @@
 	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;
@@ -570,7 +740,7 @@
 	elsif (!defined($dir_prefix))
 	{
 		# Get prefix common to most directories in list
-		$dir_prefix = get_prefix(@dir_list);
+		$dir_prefix = get_prefix(1, keys(%info_data));
 
 		if ($dir_prefix)
 		{
@@ -625,9 +795,13 @@
 	# Process each subdirectory and collect overview information
 	foreach $dir_name (@dir_list)
 	{
-		($lines_found, $lines_hit, $fn_found, $fn_hit)
+		($lines_found, $lines_hit, $fn_found, $fn_hit,
+		 $br_found, $br_hit)
 			= process_dir($dir_name);
 
+		# Handle files in root directory gracefully
+		$dir_name = "root" if ($dir_name eq "");
+
 		# Remove prefix if applicable
 		if (!$no_prefix && $dir_prefix)
 		{
@@ -646,13 +820,16 @@
 		}
 
 		$overview{$dir_name} = [$lines_found, $lines_hit, $fn_found,
-					$fn_hit, $link_name,
+					$fn_hit, $br_found, $br_hit, $link_name,
 					get_rate($lines_found, $lines_hit),
-					get_rate($fn_found, $fn_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
@@ -662,8 +839,8 @@
 	foreach (@fileview_sortlist) {
 		write_dir_page($fileview_sortname[$_], ".", "", $test_title,
 			       undef, $overall_found, $overall_hit,
-			       $total_fn_found, $total_fn_hit, \%overview,
-			       {}, {}, 0, $_);
+			       $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
@@ -672,37 +849,15 @@
 		info("Writing test case description file.\n");
 		write_description_file( \%test_description,
 					$overall_found, $overall_hit,
-					$total_fn_found, $total_fn_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);
-
-	info("Overall coverage rate:\n");
-
-	if ($overall_found == 0)
-	{
-		info("  lines......: no data found\n");
-		return;
-	}
-	info("  lines......: %.1f%% (%d of %d lines)\n",
-	     $overall_hit * 100 / $overall_found, $overall_hit,
-	     $overall_found,);
-
-	if ($func_coverage)
-	{
-		if ($total_fn_found == 0)
-		{
-			info("  functions..: no data found\n");
-		}
-		else
-		{
-			info("  functions..: %.1f%% (%d of %d functions)\n",
-			     $total_fn_hit * 100 / $total_fn_found,
-			     $total_fn_hit, $total_fn_found);
-
-		}
-	}
-
 }
 
 #
@@ -716,34 +871,36 @@
 
 	if ($html_gzip)
 	{
-		open($handle, "|gzip -c >$filename")
+		open($handle, "|-", "gzip -c >'$filename'")
 			or die("ERROR: cannot open $filename for writing ".
 			       "(gzip)!\n");
 	}
 	else
 	{
-		open($handle, ">$filename")
+		open($handle, ">", $filename)
 			or die("ERROR: cannot open $filename for writing!\n");
 	}
 }
 
-sub write_dir_page($$$$$$$$$$$$$$)
+sub write_dir_page($$$$$$$$$$$$$$$$$)
 {
 	my ($name, $rel_dir, $base_dir, $title, $trunc_dir, $overall_found,
-	    $overall_hit, $total_fn_found, $total_fn_hit, $overview,
-	    $testhash, $testfnchash, $view_type, $sort_type) = @_;
+	    $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 = "";
 	}
+	$title .= " - " if ($trunc_dir ne "");
 	write_html_prolog(*HTML_HANDLE, $base_dir, "LCOV - $title$trunc_dir");
 	write_header(*HTML_HANDLE, $view_type, $trunc_dir, $rel_dir,
 		     $overall_found, $overall_hit, $total_fn_found,
-		     $total_fn_hit, $sort_type);
+		     $total_fn_hit, $total_br_found, $total_br_hit, $sort_type);
 	write_file_table(*HTML_HANDLE, $base_dir, $overview, $testhash,
-			 $testfnchash, $view_type, $sort_type);
+			 $testfnchash, $testbrhash, $view_type, $sort_type);
 	write_html_epilog(*HTML_HANDLE, $base_dir);
 	close(*HTML_HANDLE);
 }
@@ -765,16 +922,22 @@
 	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;
 
@@ -793,6 +956,10 @@
 		$rel_dir = substr($rel_dir, 1);
 	}
 
+	# Handle files in root directory gracefully
+	$rel_dir = "root" if ($rel_dir eq "");
+	$trunc_dir = "root" if ($trunc_dir eq "");
+
 	$base_dir = get_relative_base_path($rel_dir);
 
 	create_sub_dir($rel_dir);
@@ -804,8 +971,9 @@
 		my $page_link;
 		my $func_link;
 
-		($lines_found, $lines_hit, $fn_found, $fn_hit, $testdata,
-		 $testfncdata) = process_file($trunc_dir, $rel_dir, $filename);
+		($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);
 
@@ -819,18 +987,24 @@
 			$page_link = "$base_name.gcov.$html_ext";
 		}
 		$overview{$base_name} = [$lines_found, $lines_hit, $fn_found,
-					 $fn_hit, $page_link,
+					 $fn_hit, $br_found, $br_hit,
+					 $page_link,
 					 get_rate($lines_found, $lines_hit),
-					 get_rate($fn_found, $fn_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
@@ -839,7 +1013,8 @@
 		write_dir_page($fileview_sortname[$_], $rel_dir, $base_dir,
 			       $test_title, $trunc_dir, $overall_found,
 			       $overall_hit, $total_fn_found, $total_fn_hit,
-			       \%overview, {}, {}, 1, $_);
+			       $total_br_found, $total_br_hit, \%overview, {},
+			       {}, {}, 1, $_);
 		if (!$show_details) {
 			next;
 		}
@@ -847,12 +1022,14 @@
 		write_dir_page("-detail".$fileview_sortname[$_], $rel_dir,
 			       $base_dir, $test_title, $trunc_dir,
 			       $overall_found, $overall_hit, $total_fn_found,
-			       $total_fn_hit, \%overview, \%testhash,
-			       \%testfnchash, 1, $_);
+			       $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);
+	return ($overall_found, $overall_hit, $total_fn_found, $total_fn_hit,
+		$total_br_found, $total_br_hit);
 }
 
 
@@ -913,11 +1090,12 @@
 }
 
 
-sub write_function_page($$$$$$$$$$$$$$)
+sub write_function_page($$$$$$$$$$$$$$$$$$)
 {
 	my ($base_dir, $rel_dir, $trunc_dir, $base_name, $title,
-	    $lines_found, $lines_hit, $fn_found, $fn_hit,
-	    $sumcount, $funcdata, $sumfnccount, $testfncdata, $sort_type) = @_;
+	    $lines_found, $lines_hit, $fn_found, $fn_hit, $br_found, $br_hit,
+	    $sumcount, $funcdata, $sumfnccount, $testfncdata, $sumbrcount,
+	    $testbrdata, $sort_type) = @_;
 	my $pagetitle;
 	my $filename;
 
@@ -932,10 +1110,11 @@
 	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, $sort_type);
+		     $fn_found, $fn_hit, $br_found, $br_hit, $sort_type);
 	write_function_table(*HTML_HANDLE, "$base_name.gcov.$html_ext",
 			     $sumcount, $funcdata,
-			     $sumfnccount, $testfncdata, $base_name,
+			     $sumfnccount, $testfncdata, $sumbrcount,
+			     $testbrdata, $base_name,
 			     $base_dir, $sort_type);
 	write_html_epilog(*HTML_HANDLE, $base_dir, 1);
 	close(*HTML_HANDLE);
@@ -962,25 +1141,31 @@
 	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, $lines_found, $lines_hit, $fn_found, $fn_hit)
+	 $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, $testdata);
+		return ($lines_found, $lines_hit, $fn_found, $fn_hit,
+			$br_found, $br_hit, $testdata, $testfncdata,
+			$testbrdata);
 	}
 
 	$converted = get_converted_lines($testdata);
@@ -990,9 +1175,9 @@
 	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, 0);
+		     $fn_found, $fn_hit, $br_found, $br_hit, 0);
 	@source = write_source(*HTML_HANDLE, $filename, $sumcount, $checkdata,
-			       $converted, $funcdata);
+			       $converted, $funcdata, $sumbrcount);
 
 	write_html_epilog(*HTML_HANDLE, $base_dir, 1);
 	close(*HTML_HANDLE);
@@ -1003,17 +1188,20 @@
 			write_function_page($base_dir, $rel_dir, $trunc_dir,
 					    $base_name, $test_title,
 					    $lines_found, $lines_hit,
-					    $fn_found, $fn_hit, $sumcount,
+					    $fn_found, $fn_hit, $br_found,
+					    $br_hit, $sumcount,
 					    $funcdata, $sumfnccount,
-					    $testfncdata, $_);
+					    $testfncdata, $sumbrcount,
+					    $testbrdata, $_);
 		}
 	}
 
 	# Additional files are needed in case of frame output
 	if (!$frames)
 	{
-		return ($lines_found, $lines_hit,
-			$fn_found, $fn_hit, $testdata);
+		return ($lines_found, $lines_hit, $fn_found, $fn_hit,
+			$br_found, $br_hit, $testdata, $testfncdata,
+			$testbrdata);
 	}
 
 	# Create overview png file
@@ -1033,8 +1221,8 @@
 		       scalar(@source));
 	close(*HTML_HANDLE);
 
-	return ($lines_found, $lines_hit, $fn_found, $fn_hit, $testdata,
-		$testfncdata);
+	return ($lines_found, $lines_hit, $fn_found, $fn_hit, $br_found,
+		$br_hit, $testdata, $testfncdata, $testbrdata);
 }
 
 
@@ -1054,16 +1242,22 @@
 #        "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.
@@ -1088,6 +1282,9 @@
 	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
@@ -1096,6 +1293,8 @@
 	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");
@@ -1126,14 +1325,14 @@
 				"compressed file $_[0]!\n");
 
 		# Open compressed file
-		open(INFO_HANDLE, "gunzip -c $_[0]|")
+		open(INFO_HANDLE, "-|", "gunzip -c '$_[0]'")
 			or die("ERROR: cannot start gunzip to decompress ".
 			       "file $_[0]!\n");
 	}
 	else
 	{
 		# Open decompressed file
-		open(INFO_HANDLE, $_[0])
+		open(INFO_HANDLE, "<", $_[0])
 			or die("ERROR: cannot read file $_[0]!\n");
 	}
 
@@ -1146,7 +1345,7 @@
 		# Switch statement
 		foreach ($line)
 		{
-			/^TN:([^,]*)/ && do
+			/^TN:([^,]*)(,diff)?/ && do
 			{
 				# Test name information found
 				$testname = defined($1) ? $1 : "";
@@ -1154,6 +1353,7 @@
 				{
 					$changed_testname = 1;
 				}
+				$testname .= $2 if (defined($2));
 				last;
 			};
 
@@ -1165,18 +1365,21 @@
 
 				$data = $result{$filename};
 				($testdata, $sumcount, $funcdata, $checkdata,
-				 $testfncdata, $sumfnccount) =
+				 $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;
 			};
@@ -1220,6 +1423,8 @@
 
 			/^FN:(\d+),([^,]+)/ && do
 			{
+				last if (!$func_coverage);
+
 				# Function data found, add to structure
 				$funcdata->{$2} = $1;
 
@@ -1238,6 +1443,7 @@
 
 			/^FNDA:(\d+),([^,]+)/ && do
 			{
+				last if (!$func_coverage);
 				# Function call count found, add to structure
 				# Add summary counts
 				$sumfnccount->{$2} += $1;
@@ -1249,6 +1455,28 @@
 				}
 				last;
 			};
+
+			/^BRDA:(\d+),(\d+),(\d+),(\d+|-)/ && do {
+				# Branch coverage data found
+				my ($line, $block, $branch, $taken) =
+				   ($1, $2, $3, $4);
+
+				last if (!$br_coverage);
+				$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
@@ -1261,12 +1489,16 @@
 							$testcount;
 						$testfncdata->{$testname} =
 							$testfnccount;
+						$testbrdata->{$testname} =
+							$testbrcount;
 					}	
 
 					set_info_entry($data, $testdata,
 						       $sumcount, $funcdata,
 						       $checkdata, $testfncdata,
-						       $sumfnccount);
+						       $sumfnccount,
+						       $testbrdata,
+						       $sumbrcount);
 					$result{$filename} = $data;
 					last;
 				}
@@ -1284,7 +1516,8 @@
 		$data = $result{$filename};
 
 		($testdata, $sumcount, undef, undef, $testfncdata,
-		 $sumfnccount) = get_info_entry($data);
+		 $sumfnccount, $testbrdata, $sumbrcount) =
+			get_info_entry($data);
 
 		# Filter out empty files
 		if (scalar(keys(%{$sumcount})) == 0)
@@ -1323,6 +1556,12 @@
 			}
 		}
 		$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)
@@ -1362,26 +1601,32 @@
 	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, $lines_found, $lines_hit,
-		$fn_found, $fn_hit);
+		$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[,lines_found,
-#                lines_hit, f_found, f_hit])
+#                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($$$$$$$;$$$$)
+sub set_info_entry($$$$$$$$$;$$$$$$)
 {
 	my $data_ref = $_[0];
 
@@ -1391,11 +1636,15 @@
 	$data_ref->{"check"} = $_[4];
 	$data_ref->{"testfnc"} = $_[5];
 	$data_ref->{"sumfnc"} = $_[6];
+	$data_ref->{"testbr"} = $_[7];
+	$data_ref->{"sumbr"} = $_[8];
 
-	if (defined($_[7])) { $data_ref->{"found"} = $_[7]; }
-	if (defined($_[8])) { $data_ref->{"hit"} = $_[8]; }
-	if (defined($_[9])) { $data_ref->{"f_found"} = $_[9]; }
-	if (defined($_[10])) { $data_ref->{"f_hit"} = $_[10]; }
+	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]; }
 }
 
 
@@ -1503,7 +1752,9 @@
 	my %result;
 	my $func;
 
-	%result = %{$funcdata1};
+	if (defined($funcdata1)) {
+		%result = %{$funcdata1};
+	}
 
 	foreach $func (keys(%{$funcdata2})) {
 		my $line1 = $result{$func};
@@ -1535,7 +1786,9 @@
 	my $fn_hit;
 	my $function;
 
-	%result = %{$fnccount1};
+	if (defined($fnccount1)) {
+		%result = %{$fnccount1};
+	}
 	foreach $function (keys(%{$fnccount2})) {
 		$result{$function} += $fnccount2->{$function};
 	}
@@ -1589,6 +1842,167 @@
 	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)
 #
@@ -1605,6 +2019,8 @@
 	my $checkdata1;
 	my $testfncdata1;
 	my $sumfnccount1;
+	my $testbrdata1;
+	my $sumbrcount1;
 
 	my $entry2 = $_[1];	# Reference to hash containing second entry
 	my $testdata2;
@@ -1613,6 +2029,8 @@
 	my $checkdata2;
 	my $testfncdata2;
 	my $sumfnccount2;
+	my $testbrdata2;
+	my $sumbrcount2;
 
 	my %result;		# Hash containing combined entry
 	my %result_testdata;
@@ -1620,19 +2038,23 @@
 	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) = get_info_entry($entry1);
+	 $sumfnccount1, $testbrdata1, $sumbrcount1) = get_info_entry($entry1);
 	($testdata2, $sumcount2, $funcdata2, $checkdata2, $testfncdata2,
-	 $sumfnccount2) = get_info_entry($entry2);
+	 $sumfnccount2, $testbrdata2, $sumbrcount2) = get_info_entry($entry2);
 
 	# Merge checksums
 	$checkdata1 = merge_checksums($checkdata1, $checkdata2, $filename);
@@ -1645,6 +2067,11 @@
 	($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}))
 	{
@@ -1687,8 +2114,9 @@
 	# Store result
 	set_info_entry(\%result, \%result_testdata, $result_sumcount,
 		       $result_funcdata, $checkdata1, $result_testfncdata,
-		       $result_sumfnccount, $lines_found, $lines_hit,
-		       $fn_found, $fn_hit);
+		       $result_sumfnccount, $result_testbrdata,
+		       $result_sumbrcount, $lines_found, $lines_hit,
+		       $fn_found, $fn_hit, $br_found, $br_hit);
 
 	return(\%result);
 }
@@ -1730,16 +2158,17 @@
 
 
 #
-# get_prefix(filename_list)
+# get_prefix(min_dir, filename_list)
 #
 # Search FILENAME_LIST for a directory prefix which is common to as many
 # list entries as possible, so that removing this prefix will minimize the
-# sum of the lengths of all resulting shortened filenames.
+# sum of the lengths of all resulting shortened filenames while observing
+# that no filename has less than MIN_DIR parent directories.
 #
 
-sub get_prefix(@)
+sub get_prefix($@)
 {
-	my @filename_list = @_;		# provided list of filenames
+	my ($min_dir, @filename_list) = @_;
 	my %prefix;			# mapping: prefix -> sum of lengths
 	my $current;			# Temporary iteration variable
 
@@ -1748,12 +2177,14 @@
 	{
 		# Need explicit assignment to get a copy of $_ so that
 		# shortening the contained prefix does not affect the list
-		$current = shorten_prefix($_);
+		$current = $_;
 		while ($current = shorten_prefix($current))
 		{
+			$current .= "/";
+
 			# Skip rest if the remaining prefix has already been
 			# added to hash
-			if ($prefix{$current}) { last; }
+			if (exists($prefix{$current})) { last; }
 
 			# Initialize with 0
 			$prefix{$current}="0";
@@ -1761,6 +2192,20 @@
 
 	}
 
+	# Remove all prefixes that would cause filenames to have less than
+	# the minimum number of parent directories
+	foreach my $filename (@filename_list) {
+		my $dir = dirname($filename);
+
+		for (my $i = 0; $i < $min_dir; $i++) {
+			delete($prefix{$dir."/"});
+			$dir = shorten_prefix($dir);
+		}
+	}
+
+	# Check if any prefix remains
+	return undef if (!%prefix);
+
 	# Calculate sum of lengths for all prefixes
 	foreach $current (keys(%prefix))
 	{
@@ -1789,6 +2234,8 @@
 		}
 	}
 
+	$current =~ s/\/$//;
+
 	return($current);
 }
 
@@ -1880,7 +2327,7 @@
 	my $changed_testname;
 	local *TEST_HANDLE;
 
-	open(TEST_HANDLE, "<".$_[0])
+	open(TEST_HANDLE, "<", $_[0])
 		or die("ERROR: cannot open $_[0]!\n");
 
 	while (<TEST_HANDLE>)
@@ -1986,14 +2433,17 @@
 
 sub create_sub_dir($)
 {
-	system("mkdir", "-p" ,$_[0])
-		and die("ERROR: cannot create directory $_!\n");
+	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_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
@@ -2003,20 +2453,22 @@
 # Die on error.
 #
 
-sub write_description_file($$$$$)
+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, 0);
+		     $fn_hit, $br_found, $br_hit, 0);
 
 	write_test_table_prolog(*HTML_HANDLE,
 			 "Test case descriptions - alphabetical list");
@@ -2146,7 +2598,7 @@
 		 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82] if ($sort);
 	foreach (keys(%data))
 	{
-		open(PNG_HANDLE, ">".$_)
+		open(PNG_HANDLE, ">", $_)
 			or die("ERROR: cannot create $_!\n");
 		binmode(PNG_HANDLE);
 		print(PNG_HANDLE map(chr,@{$data{$_}}));
@@ -2164,7 +2616,7 @@
 	local *HTACCESS_HANDLE;
 	my $htaccess_data;
 
-	open(*HTACCESS_HANDLE, ">.htaccess")
+	open(*HTACCESS_HANDLE, ">", ".htaccess")
 		or die("ERROR: cannot open .htaccess for writing!\n");
 
 	$htaccess_data = (<<"END_OF_HTACCESS")
@@ -2197,7 +2649,7 @@
 		return;
 	}
 
-	open(CSS_HANDLE, ">gcov.css")
+	open(CSS_HANDLE, ">", "gcov.css")
 		or die ("ERROR: cannot open gcov.css for writing!\n");
 
 
@@ -2328,17 +2780,6 @@
 	  background-color: #FF0000;
 	}
 	
-	/* All views: header legend item for legend entry */
-	td.headerItemLeg
-	{
-	  text-align: right;
-	  padding-right: 6px;
-	  font-family: sans-serif;
-	  font-weight: bold;
-	  vertical-align: bottom;
-	  white-space: nowrap;
-	}
-
 	/* All views: header legend value for legend entry */
 	td.headerValueLeg
 	{
@@ -2419,6 +2860,7 @@
 	  padding-right: 10px;
 	  background-color: #A7FC9D;
 	  font-weight: bold;
+	  font-family: sans-serif;
 	}
 	
 	/* Directory view/File view (all): line count entry for files with
@@ -2430,16 +2872,7 @@
 	  padding-right: 10px;
 	  background-color: #A7FC9D;
 	  white-space: nowrap;
-	}
-	
-	/* Directory view/File view (all): legend entry for high coverage
-	   rate */
-	span.coverLegendHi
-	{
-	  padding-left: 10px;
-	  padding-right: 10px;
-	  padding-bottom: 2px;
-	  background-color: #A7FC9D;
+	  font-family: sans-serif;
 	}
 	
 	/* Directory view/File view (all): percentage entry for files with
@@ -2451,6 +2884,7 @@
 	  padding-right: 10px;
 	  background-color: #FFEA20;
 	  font-weight: bold;
+	  font-family: sans-serif;
 	}
 	
 	/* Directory view/File view (all): line count entry for files with
@@ -2462,16 +2896,7 @@
 	  padding-right: 10px;
 	  background-color: #FFEA20;
 	  white-space: nowrap;
-	}
-	
-	/* Directory view/File view (all): legend entry for medium coverage
-	   rate */
-	span.coverLegendMed
-	{
-	  padding-left: 10px;
-	  padding-right: 10px;
-	  padding-bottom: 2px;
-	  background-color: #FFEA20;
+	  font-family: sans-serif;
 	}
 	
 	/* Directory view/File view (all): percentage entry for files with
@@ -2483,6 +2908,7 @@
 	  padding-right: 10px;
 	  background-color: #FF0000;
 	  font-weight: bold;
+	  font-family: sans-serif;
 	}
 	
 	/* Directory view/File view (all): line count entry for files with
@@ -2494,53 +2920,28 @@
 	  padding-right: 10px;
 	  background-color: #FF0000;
 	  white-space: nowrap;
-	}
-	
-	/* Directory view/File view (all): legend entry for low coverage
-	   rate */
-	span.coverLegendLo
-	{
-	  padding-left: 10px;
-	  padding-right: 10px;
-	  padding-bottom: 2px;
-	  background-color: #FF0000;
+	  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;
-	}
-	
-	/* File view (detail): test name table headline format */
-	td.testNameHead
-	{
-	  text-align: right;
-	  padding-right: 10px;
-	  background-color: #DAE7FE;
-	  font-family: sans-serif;
-	  font-weight: bold;
-	}
-	
-	/* File view (detail): test lines table headline format */
-	td.testLinesHead
-	{
-	  text-align: center;
-	  background-color: #DAE7FE;
-	  font-family: sans-serif;
-	  font-weight: bold;
+	  font-size:80%;
 	}
 	
 	/* File view (detail): test name entry */
@@ -2549,6 +2950,7 @@
 	  text-align: right;
 	  padding-right: 10px;
 	  background-color: #DAE7FE;
+	  font-family: sans-serif;
 	}
 	
 	/* File view (detail): test percentage entry */
@@ -2558,6 +2960,7 @@
 	  padding-left: 10px;
 	  padding-right: 10px; 
 	  background-color: #DAE7FE;
+	  font-family: sans-serif;
 	}
 	
 	/* File view (detail): test lines count entry */
@@ -2567,6 +2970,7 @@
 	  padding-left: 10px;
 	  padding-right: 10px; 
 	  background-color: #DAE7FE;
+	  font-family: sans-serif;
 	}
 	
 	/* Test case descriptions: test name format*/
@@ -2605,6 +3009,7 @@
 	  padding-right: 10px;
 	  background-color: #FF0000;
 	  font-weight: bold;
+	  font-family: sans-serif;
 	}
 
 	/* Source code view: function entry nonzero count*/
@@ -2615,14 +3020,15 @@
 	  padding-right: 10px;
 	  background-color: #DAE7FE;
 	  font-weight: bold;
+	  font-family: sans-serif;
 	}
 
 	/* Source code view: source code format */
-	/* Source code view: source code format */
 	pre.source
 	{
 	  font-family: monospace;
 	  white-space: pre;
+	  margin-top: 2px;
 	}
 	
 	/* Source code view: line number format */
@@ -2660,7 +3066,7 @@
 	  padding-left: 10px;
 	  padding-right: 10px;
 	  padding-bottom: 2px;
-	  background-color: #FF0000;
+	  background-color: #FF6230;
 	}
 	
 	/* Source code view (function table): standard link - visited format */
@@ -2678,13 +3084,96 @@
 	  background-color: #B5F7AF;
 	}
 	
-	/* Source code view: format for DiffCov legend */
-	span.LegendDiffCov
+	/* 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;
-	  background-color: #B5F7AF;
+	  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
 	;
@@ -2710,6 +3199,7 @@
 
 sub get_bar_graph_code($$$)
 {
+	my ($base_dir, $found, $hit) = @_;
 	my $rate;
 	my $alt;
 	my $width;
@@ -2720,13 +3210,12 @@
 	# Check number of instrumented lines
 	if ($_[1] == 0) { return ""; }
 
-	$rate		= $_[2] * 100 / $_[1];
-	$alt		= sprintf("%.1f", $rate)."%";
-	$width		= sprintf("%.0f", $rate);
-	$remainder	= sprintf("%d", 100-$width);
+	$alt		= rate($hit, $found, "%");
+	$width		= rate($hit, $found, undef, 0);
+	$remainder	= 100 - $width;
 
 	# Decide which .png file to use
-	$png_name = $rate_png[classify_rate($_[1], $_[2], $med_limit,
+	$png_name = $rate_png[classify_rate($found, $hit, $med_limit,
 					    $hi_limit)];
 
 	if ($width == 0)
@@ -2775,7 +3264,7 @@
 	if ($found == 0) {
 		return 2;
 	}
-	$rate = $hit * 100 / $found;
+	$rate = rate($hit, $found);
 	if ($rate < $med) {
 		return 0;
 	} elsif ($rate < $hi) {
@@ -2853,104 +3342,40 @@
 
 
 #
-# write_header_line(filehandle, type, additional params..)
+# write_header_line(handle, content)
 #
-# Write a header line.
+# Write a header line with the specified table contents.
 #
 
-sub write_header_line(*$@)
+sub write_header_line(*@)
 {
-	my $HANDLE = shift;
-	my $type = shift;
-	my @args = @_;
+	my ($handle, @content) = @_;
+	my $entry;
 
-	# Reduce indentation by using gotos
-	if ($type eq 0) {
-		goto header;
-	} elsif ($type eq 1) {
-		goto body;
-	} elsif ($type eq 2) {
-		goto legend_dir;
-	} elsif ($type eq 3) {
-		goto legend_source;
-	} elsif ($type eq 4) {
-		goto half_body;
+	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");
 	}
-
-header:
-	# *************************************************************
-	write_html($HANDLE, <<END_OF_HTML);
-        <tr>
-          <td width="5%"></td>
-          <td width="10%" class="headerItem">$args[0]</td>
-          <td width="35%" class="headerValue">$args[1]</td>
-          <td width="10%"></td>
-          <td width="10%" class="headerCovTableHead">$args[2]</td>
-          <td width="10%" class="headerCovTableHead">$args[3]</td>
-          <td width="15%" class="headerCovTableHead">$args[4]</td>
-          <td width="5%"></td>
-        </tr>
-END_OF_HTML
-	# *************************************************************
-	return;
-
-body:
-	# *************************************************************
-	write_html($HANDLE, <<END_OF_HTML);
-        <tr>
-          <td></td>
-          <td class="headerItem">$args[0]</td>
-          <td class="headerValue">$args[1]</td>
-          <td class="headerItem">$args[2]</td>
-          <td class="headerCovTableEntry">$args[3]</td>
-          <td class="headerCovTableEntry">$args[4]</td>
-          <td class="headerCovTableEntry$args[5]">$args[6]</td>
-        </tr>
-END_OF_HTML
-	# *************************************************************
-	return;
-
-half_body:
-	# *************************************************************
-	write_html($HANDLE, <<END_OF_HTML);
-        <tr>
-          <td></td>
-          <td class="headerItem">$args[0]</td>
-          <td class="headerValue">$args[1]</td>
-        </tr>
-END_OF_HTML
-	# *************************************************************
-	return;
-
-legend_dir:
-	# *************************************************************
-	write_html($HANDLE, <<END_OF_HTML);
-        <tr>
-          <td></td>
-          <td class="headerItemLeg">$args[0]</td>
-          <td class="headerValueLeg">
-$args[1]          </td>
-          <td></td>
-          <td class="headerValueLeg" colspan=3>
-$args[2]          </td>
-        </tr>
-END_OF_HTML
-	# *************************************************************
-	return;
-
-legend_source:
-	# *************************************************************
-	write_html($HANDLE, <<END_OF_HTML);
-        <tr>
-          <td></td>
-          <td class="headerItem">$args[0]</td>
-          <td class="headerValueLeg" colspan=5>
-            <span class="coverLegendNoCov">$args[1]</span>
-            <span class="coverLegendCov">$args[2]</span>
-          </td>
-        </tr>
-END_OF_HTML
-	# *************************************************************
+	write_html($handle, "          </tr>\n");
 }
 
 
@@ -2965,10 +3390,11 @@
 	# *************************************************************
 
 	write_html($_[0], <<END_OF_HTML)
-                <tr><td><img src="$_[1]glass.png" width=3 height=3 alt=""></td></tr>
+	          <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>
 
@@ -2980,244 +3406,170 @@
 
 
 #
-# write_file_table_prolog(filehandle, file_heading, lines_heading, func_heading)
+# write_file_table_prolog(handle, file_heading, ([heading, num_cols], ...))
 #
 # Write heading for file table.
 #
 
-sub write_file_table_prolog(*$$$)
+sub write_file_table_prolog(*$@)
 {
-	# *************************************************************
+	my ($handle, $file_heading, @columns) = @_;
+	my $num_columns = 0;
+	my $file_width;
+	my $col;
+	my $width;
 
-        if ($func_coverage)
-        {
-                write_html($_[0], <<END_OF_HTML)
+	$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="45%"><br></td>
-	      <td width="15%"></td>
-	      <td width="10%"></td>
-	      <td width="10%"></td>
-	      <td width="10%"></td>
-	      <td width="10%"></td>
-	    </tr>
-
-	    <tr>
-	      <td class="tableHead">$_[1]</td>
-	      <td class="tableHead" colspan=3>$_[2]</td>
-	      <td class="tableHead" colspan=2>$_[3]</td>
-	    </tr>
-
+	      <td width="$file_width%"><br></td>
 END_OF_HTML
-                ;
-        }
-        else
-        {
-                write_html($_[0], <<END_OF_HTML)
-	  <center>
-	  <table width="80%" cellpadding=1 cellspacing=1 border=0>
+	# Empty first row
+	foreach $col (@columns) {
+		my ($heading, $cols) = @{$col};
 
-	    <tr>
-	      <td width="50%"><br></td>
-	      <td width="15%"></td>
-	      <td width="15%"></td>
-	      <td width="20%"></td>
-	    </tr>
-
-	    <tr>
-	      <td class="tableHead">$_[1]</td>
-	      <td class="tableHead" colspan=3>$_[2]</td>
-	    </tr>
-
+		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(filehandle, cover_filename, cover_bar_graph,
-#                        cover_found, cover_hit, fn_found, fn_hit,
-#			 page_link, func_link)
+# 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(*$$$$$$$)
+sub write_file_table_entry(*$$$@)
 {
-	local *HANDLE = shift;
-	my ($filename, $bar_graph, $found, $hit, $fn_found, $fn_hit,
-	    $page_link) = @_;
-	my $rate;
-	my $rate_string;
-	my $funcs_string;
-	my $class_lines = "Lo";
-	my $class_funcs = "Hi";
+	my ($handle, $base_dir, $filename, $page_link, @entries) = @_;
 	my $file_code;
+	my $entry;
+	my $esc_filename = escape_html($filename);
 
 	# Add link to source if provided
 	if (defined($page_link) && $page_link ne "") {
-		$file_code = "<a href=\"$page_link\">$filename</a>";
+		$file_code = "<a href=\"$page_link\">$esc_filename</a>";
 	} else {
-		$file_code = $filename;
+		$file_code = $esc_filename;
 	}
 
-	# Get line coverage rate
-	if ($found > 0)
-	{
-		$rate = $hit * 100 / $found;
-		$rate_string = sprintf("%.1f", $rate)."&nbsp;%";
-		
-		$class_lines = $rate_name[classify_rate($found, $hit,
-					  $med_limit, $hi_limit)];
-	}
-	else
-	{
-		$rate_string = "-";
-	}
-
-	# Get function coverage rate
-	if ($fn_found > 0)
-	{
-		$rate = $fn_hit * 100 / $fn_found;
-		$class_funcs = $rate_name[classify_rate($fn_found, $fn_hit,
-					  $fn_med_limit, $fn_hi_limit)];
-		$funcs_string = sprintf("%.1f", $rate)."&nbsp;%";		
-	}
-	else
-	{
-		# Define 0 of 0 functions as 100%
-		$rate = 100;
-		$funcs_string = "-";
-	}
-
-	# *************************************************************
-
-	write_html(*HANDLE, <<END_OF_HTML)
+	# 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>
-	      <td class="coverPer$class_lines">$rate_string</td>
-	      <td class="coverNum$class_lines">$hit / $found</td>
 END_OF_HTML
-	;
-
-        if ($func_coverage)
-        {
-                write_html(*HANDLE, <<END_OF_HTML)
-	      <td class="coverPer$class_funcs">$funcs_string</td>
-	      <td class="coverNum$class_funcs">$fn_hit / $fn_found</td>
+		}
+		# Get rate color and text
+		if ($found == 0) {
+			$rate = "-";
+			$class = "Hi";
+		} else {
+			$rate = rate($hit, $found, "&nbsp;%");
+			$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
-	        ;
-        }
-        write_html(*HANDLE, <<END_OF_HTML)
+	}
+	# End of row
+        write_html($handle, <<END_OF_HTML);
 	    </tr>
 END_OF_HTML
-        ;
-
-	# *************************************************************
 }
 
 
 #
-# write_file_table_detail_heading(filehandle, left_heading, right_heading)
-#
-# Write heading for detail section in file table.
-#
-
-sub write_file_table_detail_heading(*$$$)
-{
-        my $func_rows = "";
-
-        if ($func_coverage)
-        {
-                $func_rows = "<td class=\"testLinesHead\" colspan=2>$_[3]</td>";
-        }
-
-	# *************************************************************
-	write_html($_[0], <<END_OF_HTML)
-	    <tr>
-	      <td class="testNameHead" colspan=2>$_[1]</td>
-	      <td class="testLinesHead" colspan=2>$_[2]</td>
-              $func_rows
-	    </tr>
-
-END_OF_HTML
-	;
-
-	# *************************************************************
-}
-
-
-#
-# write_file_table_detail_entry(filehandle, test_name,
-#               cover_found, cover_hit, func_found, func_hit)
+# write_file_table_detail_entry(filehandle, test_name, ([found, hit], ...))
 #
 # Write entry for detail section in file table.
 #
 
-sub write_file_table_detail_entry(*$$$$$)
+sub write_file_table_detail_entry(*$@)
 {
-	my $rate;
-	my $func_rate;
-	my $name = $_[1];
-	
-	if ($_[2]>0)
-	{
-		$rate = sprintf("%.1f", $_[3]*100/$_[2])."&nbsp;%";
-	}
-	else
-	{
-		$rate = "-";
-	}
+	my ($handle, $test, @entries) = @_;
+	my $entry;
 
-	if ($_[4]>0)
-	{
-		$func_rate = sprintf("%.1f", $_[5]*100/$_[4])."&nbsp;%";
+	if ($test eq "") {
+		$test = "<span style=\"font-style:italic\">&lt;unnamed&gt;</span>";
+	} elsif ($test =~ /^(.*),diff$/) {
+		$test = $1." (converted)";
 	}
-	else
-	{
-		$func_rate = "-";
-	}
-
-	if ($name =~ /^(.*),diff$/)
-	{
-		$name = $1." (converted)";
-	}
-
-	if ($name eq "")
-	{
-		$name = "<span style=\"font-style:italic\">&lt;unnamed&gt;</span>";
-	}
-
-	# *************************************************************
-
-	write_html($_[0], <<END_OF_HTML)
+	# Testname
+	write_html($handle, <<END_OF_HTML);
 	    <tr>
-	      <td class="testName" colspan=2>$name</td>
+	      <td class="testName" colspan=2>$test</td>
+END_OF_HTML
+	# Test data
+	foreach $entry (@entries) {
+		my ($found, $hit) = @{$entry};
+		my $rate = rate($hit, $found, "&nbsp;%");
+
+		write_html($handle, <<END_OF_HTML);
 	      <td class="testPer">$rate</td>
-	      <td class="testNum">$_[3]&nbsp;/&nbsp;$_[2]&nbsp;lines</td>
+	      <td class="testNum">$hit&nbsp;/&nbsp;$found</td>
 END_OF_HTML
-	;
-        if ($func_coverage)
-        {
-                write_html($_[0], <<END_OF_HTML)
-	      <td class="testPer">$func_rate</td>
-	      <td class="testNum">$_[5]&nbsp;/&nbsp;$_[4]</td>
-END_OF_HTML
-	        ;
-        }
-        write_html($_[0], <<END_OF_HTML)
+	}
+
+        write_html($handle, <<END_OF_HTML);
 	    </tr>
 
 END_OF_HTML
-        ;
 
 	# *************************************************************
 }
@@ -3322,6 +3674,17 @@
 }
 
 
+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)
 #
@@ -3330,6 +3693,15 @@
 
 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)
@@ -3338,7 +3710,9 @@
 	      <td><br></td>
 	    </tr>
 	    <tr>
-	      <td><pre class="source">
+	      <td>
+<pre class="sourceHeading">${lineno_heading}${branch_heading}${line_heading} ${source_heading}</pre>
+<pre class="source">
 END_OF_HTML
 	;
 
@@ -3347,51 +3721,251 @@
 
 
 #
+# 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,
-#                   add_anchor)
+#                   brdata, add_anchor)
 #
 # Write formatted source code line. Return a line in a format as needed
 # by gen_png()
 #
 
-sub write_source_line(*$$$$$)
+sub write_source_line(*$$$$$$)
 {
+	my ($handle, $line, $source, $count, $converted, $brdata,
+	    $add_anchor) = @_;
 	my $source_format;
-	my $count;
+	my $count_format;
 	my $result;
 	my $anchor_start = "";
 	my $anchor_end = "";
+	my $count_field_width = $line_field_width - 1;
+	my @br_html;
+	my $html;
 
-	if (!(defined$_[3]))
-	{
+	# Get branch HTML data for this line
+	@br_html = get_branch_html($brdata) if ($br_coverage);
+
+	if (!defined($count)) {
 		$result		= "";
 		$source_format	= "";
-		$count		= " "x15;
+		$count_format	= " "x$count_field_width;
 	}
-	elsif ($_[3] == 0)
-	{
-		$result		= $_[3];
+	elsif ($count == 0) {
+		$result		= $count;
 		$source_format	= '<span class="lineNoCov">';
-		$count		= sprintf("%15d", $_[3]);
+		$count_format	= format_count($count, $count_field_width);
 	}
-	elsif ($_[4] && defined($highlight))
-	{
-		$result		= "*".$_[3];
+	elsif ($converted && defined($highlight)) {
+		$result		= "*".$count;
 		$source_format	= '<span class="lineDiffCov">';
-		$count		= sprintf("%15d", $_[3]);
+		$count_format	= format_count($count, $count_field_width);
 	}
-	else
-	{
-		$result		= $_[3];
+	else {
+		$result		= $count;
 		$source_format	= '<span class="lineCov">';
-		$count		= sprintf("%15d", $_[3]);
+		$count_format	= format_count($count, $count_field_width);
 	}
-
-	$result .= ":".$_[2];
+	$result .= ":".$source;
 
 	# Write out a line number navigation anchor every $nav_resolution
 	# lines if necessary
-	if ($_[5])
+	if ($add_anchor)
 	{
 		$anchor_start	= "<a name=\"$_[1]\">";
 		$anchor_end	= "</a>";
@@ -3400,13 +3974,23 @@
 
 	# *************************************************************
 
-	write_html($_[0],
-		   $anchor_start.
-		   '<span class="lineNum">'.sprintf("%8d", $_[1]).
-		   " </span>$source_format$count : ".
-		   escape_html($_[2]).($source_format?"</span>":"").
-		   $anchor_end."\n");
+	$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);
@@ -3460,8 +4044,8 @@
 
 	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>
+	    <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
@@ -3491,7 +4075,7 @@
 	<html lang="en">
 
 	<head>
-	  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+	  <meta http-equiv="Content-Type" content="text/html; charset=$charset">
 	  <title>$_[3]</title>
 	  <link rel="stylesheet" type="text/css" href="$_[1]gcov.css">
 	</head>
@@ -3554,7 +4138,7 @@
 
 	<head>
 	  <title>$_[3]</title>
-	  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+	  <meta http-equiv="Content-Type" content="text/html; charset=$charset">
 	  <link rel="stylesheet" type="text/css" href="$_[1]gcov.css">
 	</head>
 
@@ -3601,51 +4185,14 @@
 }
 
 
-# rate_to_col(found, hit)
-#
-# Return Lo, Med or Hi, depending on the coverage rate.
-#
-
-sub rate_to_col($$)
+sub max($$)
 {
-	my ($found, $hit) = @_;
-	my $rate;
+	my ($a, $b) = @_;
 
-	if ($found == 0) {
-		return "Hi";
-	}
-	$rate = 100 * $hit / $found;
-	if ($rate < $med_limit) {
-		return "Lo";
-	} elsif ($rate < $hi_limit) {
-		return "Med";
-	}
-	return "Hi";
+	return $a if ($a > $b);
+	return $b;
 }
 
-# format_rate(found, hit)
-#
-# Return formatted percent string for coverage rate.
-#
-
-sub format_rate($$)
-{	
-	return $_[0] == 0 ? "-" : sprintf("%.1f", $_[1] * 100 / $_[0])." %";
-}
-
-sub get_legend_code($$$)
-{
-	my ($text, $med, $hi) = @_;
-	my $result;
-
-	$result = <<EOF;
-	            $text<br>
-	            <span class="coverLegendLo">0% to $med%</span>
-	            <span class="coverLegendMed">$med% to $hi%</span>
-	            <span class="coverLegendHi">$hi% to 100%</span>
-EOF
-	return $result;
-}
 
 #
 # write_header(filehandle, type, trunc_file_name, rel_file_name, lines_found,
@@ -3656,7 +4203,7 @@
 # header, test case description header, function view header)
 #
 
-sub write_header(*$$$$$$$$)
+sub write_header(*$$$$$$$$$$)
 {
 	local *HTML_HANDLE = $_[0];
 	my $type = $_[1];
@@ -3666,32 +4213,43 @@
 	my $lines_hit = $_[5];
 	my $fn_found = $_[6];
 	my $fn_hit = $_[7];
-	my $sort_type = $_[8];
+	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;
+	my $esc_trunc_name = escape_html($trunc_name);
 
 	$base_name = basename($rel_filename);
 
 	# Prepare text for "current view" field
-	if ($type == 0)
+	if ($type == $HDR_DIR)
 	{
 		# Main overview
 		$base_dir = "";
 		$view = $overview_title;
 	}
-	elsif ($type == 1)
+	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";
+			"$overview_title</a> - $esc_trunc_name";
 	}
-	elsif ($type == 2 || $type == 4)
+	elsif ($type == $HDR_SOURCE || $type == $HDR_FUNC)
 	{
 		# File view
 		my $dir_name = dirname($rel_filename);
+		my $esc_base_name = escape_html($base_name);
+		my $esc_dir_name = escape_html($dir_name);
 
 		$base_dir = get_relative_base_path($dir_name);
 		if ($frames)
@@ -3701,26 +4259,28 @@
 			$view = "<a href=\"$base_dir"."index.$html_ext\" ".
 				"target=\"_parent\">$overview_title</a> - ".
 				"<a href=\"index.$html_ext\" target=\"_parent\">".
-				"$dir_name</a> - $base_name";
+				"$esc_dir_name</a> - $esc_base_name";
 		}
 		else
 		{
 			$view = "<a href=\"$base_dir"."index.$html_ext\">".
 				"$overview_title</a> - ".
 				"<a href=\"index.$html_ext\">".
-				"$dir_name</a> - $base_name";
+				"$esc_dir_name</a> - $esc_base_name";
 		}
 
 		# Add function suffix
 		if ($func_coverage) {
-			if ($type == 2) {
+			$view .= "<span style=\"font-size: 80%;\">";
+			if ($type == $HDR_SOURCE) {
 				$view .= " (source / <a href=\"$base_name.func.$html_ext\">functions</a>)";
-			} elsif ($type == 4) {
+			} elsif ($type == $HDR_FUNC) {
 				$view .= " (<a href=\"$base_name.gcov.$html_ext\">source</a> / functions)";
 			}
+			$view .= "</span>";
 		}
 	}
-	elsif ($type == 3)
+	elsif ($type == $HDR_TESTDESC)
 	{
 		# Test description header
 		$base_dir = "";
@@ -3732,84 +4292,126 @@
 	$test = escape_html($test_title);
 
 	# Append link to test description page if available
-	if (%test_description && ($type != 3))
+	if (%test_description && ($type != $HDR_TESTDESC))
 	{
-		if ($frames && ($type == 2 || $type == 4))
+		if ($frames && ($type == $HDR_SOURCE || $type == $HDR_FUNC))
 		{
 			# Need to break frameset when clicking this link
-			$test .= " ( <a href=\"$base_dir".
+			$test .= " ( <span style=\"font-size:80%;\">".
+				 "<a href=\"$base_dir".
 				 "descriptions.$html_ext\" target=\"_parent\">".
-				 "view descriptions</a> )";
+				 "view descriptions</a></span> )";
 		}
 		else
 		{
-			$test .= " ( <a href=\"$base_dir".
+			$test .= " ( <span style=\"font-size:80%;\">".
+				 "<a href=\"$base_dir".
 				 "descriptions.$html_ext\">".
-				 "view descriptions</a> )";
+				 "view descriptions</a></span> )";
 		}
 	}
 
 	# Write header
 	write_header_prolog(*HTML_HANDLE, $base_dir);
-	write_header_line(*HTML_HANDLE, 0, "Current view:", $view,
-			  "Found", "Hit", "Coverage");
-	write_header_line(*HTML_HANDLE, 1, "Test:", $test, "Lines:",
-			  $lines_found, $lines_hit,
-			  $rate_name[classify_rate($lines_found, $lines_hit,
-						   $med_limit, $hi_limit)],
-			  format_rate($lines_found, $lines_hit));
-	if ($func_coverage) {
-		write_header_line(*HTML_HANDLE, 1, "Date:", $date, "Functions:",
-				  $fn_found, $fn_hit,
-				  $rate_name[classify_rate($fn_found,
-							   $fn_hit,
-							   $fn_med_limit,
-							   $fn_hi_limit)],
-				  format_rate($fn_found, $fn_hit));
-	} else {
-		write_header_line(*HTML_HANDLE, 4, "Date:", $date);
-	}
-	if ($legend) {
-		if ($type == 0 || $type == 1) {
-			my $line_code = get_legend_code("Line coverage:",
-						$med_limit, $hi_limit);
-			my $func_code = "";
 
-			if ($func_coverage) {
-				$func_code = get_legend_code(
-						"Function coverage:",
-						$fn_med_limit,
-						$fn_hi_limit);
-			}
-			write_header_line(*HTML_HANDLE, 2, "Colors:",
-				$line_code, $func_code);
-		} elsif ($type == 2 || $type == 4) {
-			write_header_line(*HTML_HANDLE, 3, "Colors:",
-					  "not hit", "hit");
+	# 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: &lt; $med_limit %</span>
+            <span class="coverLegendCovMed" title="Coverage rates between $med_limit % and $hi_limit % are classified as medium">medium: &gt;= $med_limit %</span>
+            <span class="coverLegendCovHi" title="Coverage rates of $hi_limit % and more are classified as high">high: &gt;= $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 = rate($lines_hit, $lines_found, " %");
+	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 = rate($fn_hit, $fn_found, " %");
+		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 = rate($br_hit, $br_found, " %");
+		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);
 }
 
 
 #
-# split_filename(filename)
-#
-# Return (path, filename, extension) for a given FILENAME.
-#
-
-sub split_filename($)
-{
-	if (!$_[0]) { return(); }
-	my @path_components = split('/', $_[0]);
-	my @file_components = split('\.', pop(@path_components));
-	my $extension = pop(@file_components);
-
-	return (join("/",@path_components), join(".",@file_components),
-		$extension);
-}
-
-#
 # get_sorted_keys(hash_ref, sort_type)
 #
 
@@ -3817,15 +4419,18 @@
 {
 	my ($hash, $type) = @_;
 
-	if ($type == 0) {
+	if ($type == $SORT_FILE) {
 		# Sort by name
 		return sort(keys(%{$hash}));
-	} elsif ($type == 1) {
+	} elsif ($type == $SORT_LINE) {
 		# Sort by line coverage
-		return sort({$hash->{$a}[5] <=> $hash->{$b}[5]} keys(%{$hash}));
-	} elsif ($type == 2) {
+		return sort({$hash->{$a}[7] <=> $hash->{$b}[7]} keys(%{$hash}));
+	} elsif ($type == $SORT_FUNC) {
 		# Sort by function coverage;
-		return sort({$hash->{$a}[6] <=> $hash->{$b}[6]}	keys(%{$hash}));
+		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}));
 	}
 }
 
@@ -3858,7 +4463,7 @@
 	my $link;
 
 	if ($sort_button) {
-		if ($type == 1) {
+		if ($type == $HEAD_NO_DETAIL) {
 			$link = "index.$html_ext";
 		} else {
 			$link = "index-detail.$html_ext";
@@ -3875,12 +4480,12 @@
 	my $result = $text;
 	my $sort_link;
 
-	if ($type == 1) {
+	if ($type == $HEAD_NO_DETAIL) {
 		# Just text
 		if ($sort_button) {
 			$sort_link = "index-sort-l.$html_ext";
 		}
-	} elsif ($type == 2) {
+	} elsif ($type == $HEAD_DETAIL_HIDDEN) {
 		# Text + link to detail view
 		$result .= ' ( <a class="detail" href="index-detail'.
 			   $fileview_sortname[$sort_type].'.'.$html_ext.
@@ -3910,7 +4515,7 @@
 	my $link;
 
 	if ($sort_button) {
-		if ($type == 1) {
+		if ($type == $HEAD_NO_DETAIL) {
 			$link = "index-sort-f.$html_ext";
 		} else {
 			$link = "index-detail-sort-f.$html_ext";
@@ -3920,9 +4525,26 @@
 	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,
-#                  fileview, sort_type)
+#                  testbrhash, fileview, sort_type)
 #
 # Write a complete file table. OVERVIEW is a reference to a hash containing
 # the following mapping:
@@ -3940,70 +4562,107 @@
 # otherwise.
 #
 
-sub write_file_table(*$$$$$$)
+sub write_file_table(*$$$$$$$)
 {
 	local *HTML_HANDLE = $_[0];
 	my $base_dir = $_[1];
 	my $overview = $_[2];
 	my $testhash = $_[3];
 	my $testfnchash = $_[4];
-	my $fileview = $_[5];
-	my $sort_type = $_[6];
+	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 $testcount;
-	my $testfnccount;
+	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 ? 2 : 1,
+		$file_code = get_file_code($detailed ? $HEAD_DETAIL_HIDDEN :
+					$HEAD_NO_DETAIL,
 					$fileview ? "Filename" : "Directory",
-					$sort && $sort_type != 0, $base_dir);
-		$line_code = get_line_code($detailed ? 3 : 2, $sort_type,
+					$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 != 1, $base_dir);
-		$func_code = get_func_code($detailed ? 2 : 1, "Functions",
-					$sort && $sort_type != 2, $base_dir);
+					$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(1,
+		$file_code = get_file_code($HEAD_NO_DETAIL,
 					$fileview ? "Filename" : "Directory",
-					$sort && $sort_type != 0, $base_dir);
-		$line_code = get_line_code(1, $sort_type, "Line Coverage",
-					$sort && $sort_type != 1, $base_dir);
-		$func_code = get_func_code(1, "Functions",
-					$sort && $sort_type != 2, $base_dir);
+					$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, $line_code,
-				$func_code);
+	write_file_table_prolog(*HTML_HANDLE, $file_code, @head_columns);
 
 	foreach $filename (get_sorted_keys($overview, $sort_type))
 	{
-		($found, $hit, $fn_found, $fn_hit, $page_link)
-			= @{$overview->{$filename}};
-		$bar_graph = get_bar_graph_code($base_dir, $found, $hit);
+		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};
-
-		write_file_table_entry(*HTML_HANDLE, $filename, $bar_graph,
-				       $found, $hit, $fn_found, $fn_hit,
-				       $page_link);
+		$testbrdata = $testbrhash->{$filename};
 
 		# Check whether we should write test specific coverage
 		# as well
@@ -4011,18 +4670,15 @@
 
 		# Filter out those tests that actually affect this file
 		%affecting_tests = %{ get_affecting_tests($testdata,
-					$testfncdata) };
+					$testfncdata, $testbrdata) };
 
 		# Does any of the tests affect this file at all?
 		if (!%affecting_tests) { next; }
 
-		# Write test details for this entry
-		write_file_table_detail_heading(*HTML_HANDLE, "Test name",
-						"Lines hit", "Functions hit");
-
 		foreach $testname (keys(%affecting_tests))
 		{
-			($found, $hit, $fn_found, $fn_hit) =
+			my @results;
+			($found, $hit, $fn_found, $fn_hit, $br_found, $br_hit) =
 				split(",", $affecting_tests{$testname});
 
 			# Insert link to description of available
@@ -4033,8 +4689,11 @@
 					    "$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,
-				$found, $hit, $fn_found, $fn_hit);
+				@results);
 		}
 	}
 
@@ -4095,39 +4754,224 @@
 
 
 #
-# get_affecting_tests(testdata, testfncdata)
+# 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($$)
+sub get_affecting_tests($$$)
 {
-	my $testdata = $_[0];
-	my $testfncdata = $_[1];
+	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";
+			$result{$testname} = "$found,$hit,$fn_found,$fn_hit,".
+					     "$br_found,$br_hit";
 		}
 	}
 
@@ -4149,7 +4993,7 @@
 
 #
 # write_source(filehandle, source_filename, count_data, checksum_data,
-#              converted_data, func_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().
@@ -4157,7 +5001,7 @@
 # Die on error.
 #
 
-sub write_source($$$$$$)
+sub write_source($$$$$$$)
 {
 	local *HTML_HANDLE = $_[0];
 	local *SOURCE_HANDLE;
@@ -4168,24 +5012,51 @@
 	my $checkdata = $_[3];
 	my $converted = $_[4];
 	my $funcdata  = $_[5];
+	my $sumbrcount = $_[6];
 	my $datafunc = get_hash_reverse($funcdata);
 	my $add_anchor;
+	my @file;
 
 	if ($_[2])
 	{
 		%count_data = %{$_[2]};
 	}
 
-	open(SOURCE_HANDLE, "<".$source_filename)
-          # or die("ERROR: cannot open $source_filename for reading!\n");
-          or open(SOURCE_HANDLE, "</dev/null");
+	if (!open(SOURCE_HANDLE, "<", $source_filename)) {
+		my @lines;
+		my $last_line = 0;
+
+		if (!$ignore[$ERROR_SOURCE]) {
+			die("ERROR: cannot read $source_filename\n");
+		}
+
+		# Continue without source file
+		warn("WARNING: cannot read $source_filename!\n");
+
+		@lines = sort( { $a <=> $b }  keys(%count_data));
+		if (@lines) {
+			$last_line = $lines[scalar(@lines) - 1];
+		}
+		return ( ":" ) if ($last_line < 1);
+
+		# Simulate gcov behavior
+		for ($line_number = 1; $line_number <= $last_line;
+		     $line_number++) {
+			push(@file, "/* EOF */");
+		}
+	} else {
+		@file = <SOURCE_HANDLE>;
+	}
 	
 	write_source_prolog(*HTML_HANDLE);
-
-	for ($line_number = 1; <SOURCE_HANDLE> ; $line_number++)
-	{
+	$line_number = 0;
+	foreach (@file) {
+		$line_number++;
 		chomp($_);
 
+		# Also remove CR from line-end
+		s/\015$//;
+
 		# Source code matches coverage data?
 		if (defined($checkdata->{$line_number}) &&
 		    ($checkdata->{$line_number} ne md5_base64($_)))
@@ -4212,7 +5083,7 @@
 		      write_source_line(HTML_HANDLE, $line_number,
 					$_, $count_data{$line_number},
 					$converted->{$line_number},
-					$add_anchor));
+					$sumbrcount->{$line_number}, $add_anchor));
 	}
 
 	close(SOURCE_HANDLE);
@@ -4271,7 +5142,8 @@
 
 #
 # write_function_table(filehandle, source_file, sumcount, funcdata,
-#		       sumfnccount, testfncdata)
+#		       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.
@@ -4279,7 +5151,7 @@
 # Die on error.
 #
 
-sub write_function_table(*$$$$$$$$)
+sub write_function_table(*$$$$$$$$$$)
 {
 	local *HTML_HANDLE = $_[0];
 	my $source = $_[1];
@@ -4287,9 +5159,11 @@
 	my $funcdata = $_[3];
 	my $sumfncdata = $_[4];
 	my $testfncdata = $_[5];
-	my $name = $_[6];
-	my $base = $_[7];
-	my $type = $_[8];
+	my $sumbrcount = $_[6];
+	my $testbrdata = $_[7];
+	my $name = $_[8];
+	my $base = $_[9];
+	my $type = $_[10];
 	my $func;
 	my $func_code;
 	my $count_code;
@@ -4310,11 +5184,23 @@
 	
 	# 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 = escape_html($func);
+		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;
 		}
@@ -4403,14 +5289,16 @@
 
 sub subtract_fnccounts($$)
 {
-	my %data = %{$_[0]};
-	my %base = %{$_[1]};
+	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};
@@ -4453,18 +5341,24 @@
 	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))
 	{
@@ -4480,9 +5374,11 @@
 
 		# Get set entries for data and baseline
 		($data_testdata, undef, $data_funcdata, $data_checkdata,
-		 $data_testfncdata) = get_info_entry($data);
+		 $data_testfncdata, undef, $data_testbrdata) =
+			get_info_entry($data);
 		(undef, $base_count, undef, $base_checkdata, undef,
-		 $base_sumfnccount) = get_info_entry($base);
+		 $base_sumfnccount, undef, $base_sumbrcount) =
+			get_info_entry($base);
 
 		# Check for compatible checksums
 		merge_checksums($data_checkdata, $base_checkdata, $filename);
@@ -4490,6 +5386,7 @@
 		# sumcount has to be calculated anew
 		$sumcount = {};
 		$sumfnccount = {};
+		$sumbrcount = {};
 
 		# For each test case, subtract test specific counts
 		foreach $testname (keys(%{$data_testdata}))
@@ -4497,12 +5394,17 @@
 			# 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)
@@ -4511,6 +5413,8 @@
 				$data_testdata->{$testname} = $data_count;
 				$data_testfncdata->{$testname} =
 					$data_testfnccount;
+				$data_testbrdata->{$testname} =
+					$data_testbrcount;
 			}
 			else
 			{
@@ -4518,19 +5422,24 @@
 				# 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_fnccounts($sumfnccount, $data_testfnccount);
+				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,
-			       $found, $hit, $fn_found, $fn_hit);
+			       $data_testbrdata, $sumbrcount, $found, $hit,
+			       $fn_found, $fn_hit, $br_found, $br_hit);
 
 		$data_hash{$filename} = $data;
 	}
@@ -4627,12 +5536,12 @@
 	local *OLD_STDOUT;
 
 	# Save old stdout and stderr handles
-	($mode & 1) && open(OLD_STDOUT, ">>&STDOUT");
-	($mode & 2) && open(OLD_STDERR, ">>&STDERR");
+	($mode & 1) && open(OLD_STDOUT, ">>&", "STDOUT");
+	($mode & 2) && open(OLD_STDERR, ">>&", "STDERR");
 
 	# Redirect to /dev/null
-	($mode & 1) && open(STDOUT, ">/dev/null");
-	($mode & 2) && open(STDERR, ">/dev/null");
+	($mode & 1) && open(STDOUT, ">", "/dev/null");
+	($mode & 2) && open(STDERR, ">", "/dev/null");
 
 	system(@_);
 	$result = $?;
@@ -4642,8 +5551,8 @@
 	($mode & 2) && close(STDERR);
 
 	# Restore old handles
-	($mode & 1) && open(STDOUT, ">>&OLD_STDOUT");
-	($mode & 2) && open(STDERR, ">>&OLD_STDERR");
+	($mode & 1) && open(STDOUT, ">>&", "OLD_STDOUT");
+	($mode & 2) && open(STDERR, ">>&", "OLD_STDERR");
 
 	return $result;
 }
@@ -4664,7 +5573,7 @@
 	my $value;
 	local *HANDLE;
 
-	if (!open(HANDLE, "<$filename"))
+	if (!open(HANDLE, "<", $filename))
 	{
 		warn("WARNING: cannot read configuration file $filename\n");
 		return undef;
@@ -4703,8 +5612,8 @@
 #   key_string => var_ref
 #
 # where KEY_STRING is a keyword and VAR_REF is a reference to an associated
-# variable. If the global configuration hash CONFIG contains a value for
-# keyword KEY_STRING, VAR_REF will be assigned the value for that keyword. 
+# variable. If the global configuration hashes CONFIG or OPT_RC contain a value
+# for keyword KEY_STRING, VAR_REF will be assigned the value for that keyword. 
 #
 
 sub apply_config($)
@@ -4713,8 +5622,9 @@
 
 	foreach (keys(%{$ref}))
 	{
-		if (defined($config->{$_}))
-		{
+		if (defined($opt_rc{$_})) {
+			${$ref->{$_}} = $opt_rc{$_};
+		} elsif (defined($config->{$_})) {
 			${$ref->{$_}} = $config->{$_};
 		}
 	}
@@ -4737,7 +5647,7 @@
 	{
 		local *HANDLE;
 
-		open(HANDLE, "<".$filename)
+		open(HANDLE, "<", $filename)
 			or die("ERROR: cannot open html prolog $filename!\n");
 		while (<HANDLE>)
 		{
@@ -4753,7 +5663,7 @@
 <html lang="en">
 
 <head>
-  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+  <meta http-equiv="Content-Type" content="text/html; charset=$charset">
   <title>\@pagetitle\@</title>
   <link rel="stylesheet" type="text/css" href="\@basedir\@gcov.css">
 </head>
@@ -4783,7 +5693,7 @@
 	{
 		local *HANDLE;
 
-		open(HANDLE, "<".$filename)
+		open(HANDLE, "<", $filename)
 			or die("ERROR: cannot open html epilog $filename!\n");
 		while (<HANDLE>)
 		{
@@ -4818,3 +5728,72 @@
 
 	die("$tool_name: $msg");
 }
+
+#
+# parse_ignore_errors(@ignore_errors)
+#
+# Parse user input about which errors to ignore.
+#
+
+sub parse_ignore_errors(@)
+{
+	my (@ignore_errors) = @_;
+	my @items;
+	my $item;
+
+	return if (!@ignore_errors);
+
+	foreach $item (@ignore_errors) {
+		$item =~ s/\s//g;
+		if ($item =~ /,/) {
+			# Split and add comma-separated parameters
+			push(@items, split(/,/, $item));
+		} else {
+			# Add single parameter
+			push(@items, $item);
+		}
+	}
+	foreach $item (@items) {
+		my $item_id = $ERROR_ID{lc($item)};
+
+		if (!defined($item_id)) {
+			die("ERROR: unknown argument for --ignore-errors: ".
+			    "$item\n");
+		}
+		$ignore[$item_id] = 1;
+	}
+}
+
+#
+# rate(hit, found[, suffix, precision, width])
+#
+# Return the coverage rate [0..100] for HIT and FOUND values. 0 is only
+# returned when HIT is 0. 100 is only returned when HIT equals FOUND.
+# PRECISION specifies the precision of the result. SUFFIX defines a
+# string that is appended to the result if FOUND is non-zero. Spaces
+# are added to the start of the resulting string until it is at least WIDTH
+# characters wide.
+#
+
+sub rate($$;$$$)
+{
+        my ($hit, $found, $suffix, $precision, $width) = @_;
+        my $rate; 
+
+	# Assign defaults if necessary
+        $precision	= 1	if (!defined($precision));
+	$suffix		= ""	if (!defined($suffix));
+	$width		= 0	if (!defined($width));
+        
+        return sprintf("%*s", $width, "-") if (!defined($found) || $found == 0);
+        $rate = sprintf("%.*f", $precision, $hit * 100 / $found);
+
+	# Adjust rates if necessary
+        if ($rate == 0 && $hit > 0) {
+		$rate = sprintf("%.*f", $precision, 1 / 10 ** $precision);
+        } elsif ($rate == 100 && $hit != $found) {
+		$rate = sprintf("%.*f", $precision, 100 - 1 / 10 ** $precision);
+	}
+
+	return sprintf("%*s", $width, $rate.$suffix);
+}
diff --git a/bin/geninfo b/bin/geninfo
index 055641b..9325f9d 100755
--- a/bin/geninfo
+++ b/bin/geninfo
@@ -1,6 +1,6 @@
 #!/usr/bin/perl -w
 #
-#   Copyright (c) International Business Machines  Corp., 2002,2007
+#   Copyright (c) International Business Machines  Corp., 2002,2012
 #
 #   This program is free software;  you can redistribute it and/or modify
 #   it under the terms of the GNU General Public License as published by
@@ -51,16 +51,22 @@
 
 use strict;
 use File::Basename; 
+use File::Spec::Functions qw /abs2rel catdir file_name_is_absolute splitdir
+			      splitpath catpath/;
 use Getopt::Long;
 use Digest::MD5 qw(md5_base64);
-
+if( $^O eq "msys" )
+{
+	require File::Spec::Win32;
+}
 
 # Constants
-our $lcov_version	= "LCOV version 1.7";
+our $lcov_version	= 'LCOV version 1.10';
 our $lcov_url		= "http://ltp.sourceforge.net/coverage/lcov.php";
 our $gcov_tool		= "gcov";
 our $tool_name		= basename($0);
 
+our $GCOV_VERSION_4_7_0	= 0x40700;
 our $GCOV_VERSION_3_4_0	= 0x30400;
 our $GCOV_VERSION_3_3_0	= 0x30300;
 our $GCNO_FUNCTION_TAG	= 0x01000000;
@@ -68,41 +74,140 @@
 our $GCNO_FILE_MAGIC	= 0x67636e6f;
 our $BBG_FILE_MAGIC	= 0x67626267;
 
-our $COMPAT_HAMMER	= "hammer";
-
+# Error classes which users may specify to ignore during processing
 our $ERROR_GCOV		= 0;
 our $ERROR_SOURCE	= 1;
+our $ERROR_GRAPH	= 2;
+our %ERROR_ID = (
+	"gcov" => $ERROR_GCOV,
+	"source" => $ERROR_SOURCE,
+	"graph" => $ERROR_GRAPH,
+);
+
+our $EXCL_START = "LCOV_EXCL_START";
+our $EXCL_STOP = "LCOV_EXCL_STOP";
+our $EXCL_LINE = "LCOV_EXCL_LINE";
+
+# Compatibility mode values
+our $COMPAT_VALUE_OFF	= 0;
+our $COMPAT_VALUE_ON	= 1;
+our $COMPAT_VALUE_AUTO	= 2;
+
+# Compatibility mode value names
+our %COMPAT_NAME_TO_VALUE = (
+	"off"	=> $COMPAT_VALUE_OFF,
+	"on"	=> $COMPAT_VALUE_ON,
+	"auto"	=> $COMPAT_VALUE_AUTO,
+);
+
+# Compatiblity modes
+our $COMPAT_MODE_LIBTOOL	= 1 << 0;
+our $COMPAT_MODE_HAMMER		= 1 << 1;
+our $COMPAT_MODE_SPLIT_CRC	= 1 << 2;
+
+# Compatibility mode names
+our %COMPAT_NAME_TO_MODE = (
+	"libtool"	=> $COMPAT_MODE_LIBTOOL,
+	"hammer"	=> $COMPAT_MODE_HAMMER,
+	"split_crc"	=> $COMPAT_MODE_SPLIT_CRC,
+	"android_4_4_0"	=> $COMPAT_MODE_SPLIT_CRC,
+);
+
+# Map modes to names
+our %COMPAT_MODE_TO_NAME = (
+	$COMPAT_MODE_LIBTOOL	=> "libtool",
+	$COMPAT_MODE_HAMMER	=> "hammer",
+	$COMPAT_MODE_SPLIT_CRC	=> "split_crc",
+);
+
+# Compatibility mode default values
+our %COMPAT_MODE_DEFAULTS = (
+	$COMPAT_MODE_LIBTOOL	=> $COMPAT_VALUE_ON,
+	$COMPAT_MODE_HAMMER	=> $COMPAT_VALUE_AUTO,
+	$COMPAT_MODE_SPLIT_CRC	=> $COMPAT_VALUE_AUTO,
+);
+
+# Compatibility mode auto-detection routines
+sub compat_hammer_autodetect();
+our %COMPAT_MODE_AUTO = (
+	$COMPAT_MODE_HAMMER	=> \&compat_hammer_autodetect,
+	$COMPAT_MODE_SPLIT_CRC	=> 1,	# will be done later
+);
+
+our $BR_LINE		= 0;
+our $BR_BLOCK		= 1;
+our $BR_BRANCH		= 2;
+our $BR_TAKEN		= 3;
+our $BR_VEC_ENTRIES	= 4;
+our $BR_VEC_WIDTH	= 32;
+
+our $UNNAMED_BLOCK	= 9999;
 
 # Prototypes
 sub print_usage(*);
 sub gen_info($);
-sub process_dafile($);
+sub process_dafile($$);
 sub match_filename($@);
 sub solve_ambiguous_match($$$);
 sub split_filename($);
 sub solve_relative_path($$);
-sub get_dir($);
 sub read_gcov_header($);
 sub read_gcov_file($);
-sub read_bb_file($$);
-sub read_string(*$);
-sub read_gcno_file($$);
-sub read_gcno_string(*$);
-sub read_hammer_bbg_file($$);
-sub read_hammer_bbg_string(*$);
-sub unpack_int32($$);
 sub info(@);
 sub get_gcov_version();
 sub system_no_output($@);
 sub read_config($);
 sub apply_config($);
-sub gen_initial_info($);
-sub process_graphfile($);
+sub get_exclusion_data($);
+sub apply_exclusion_data($$);
+sub process_graphfile($$);
+sub filter_fn_name($);
 sub warn_handler($);
 sub die_handler($);
+sub graph_error($$);
+sub graph_expect($);
+sub graph_read(*$;$$);
+sub graph_skip(*$;$);
+sub sort_uniq(@);
+sub sort_uniq_lex(@);
+sub graph_cleanup($);
+sub graph_find_base($);
+sub graph_from_bb($$$);
+sub graph_add_order($$$);
+sub read_bb_word(*;$);
+sub read_bb_value(*;$);
+sub read_bb_string(*$);
+sub read_bb($);
+sub read_bbg_word(*;$);
+sub read_bbg_value(*;$);
+sub read_bbg_string(*);
+sub read_bbg_lines_record(*$$$$$);
+sub read_bbg($);
+sub read_gcno_word(*;$$);
+sub read_gcno_value(*$;$$);
+sub read_gcno_string(*$);
+sub read_gcno_lines_record(*$$$$$$);
+sub determine_gcno_split_crc($$$);
+sub read_gcno_function_record(*$$$$);
+sub read_gcno($);
+sub get_gcov_capabilities();
+sub get_overall_line($$$$);
+sub print_overall_rate($$$$$$$$$);
+sub br_gvec_len($);
+sub br_gvec_get($$);
+sub debug($);
+sub int_handler();
+sub parse_ignore_errors(@);
+sub is_external($);
+sub compat_name($);
+sub parse_compat_modes($);
+sub is_compat($);
+sub is_compat_auto($);
+
 
 # Global variables
 our $gcov_version;
+our $gcov_version_string;
 our $graph_file_extension;
 our $data_file_extension;
 our @data_directory;
@@ -115,18 +220,35 @@
 our $follow;
 our $checksum;
 our $no_checksum;
-our $preserve_paths;
-our $compat_libtool;
-our $no_compat_libtool;
+our $opt_compat_libtool;
+our $opt_no_compat_libtool;
+our $rc_adjust_src_path;# Regexp specifying parts to remove from source path
+our $adjust_src_pattern;
+our $adjust_src_replace;
 our $adjust_testname;
 our $config;		# Configuration file contents
-our $compatibility;	# Compatibility version flag - used to indicate
-			# non-standard GCOV data format versions
 our @ignore_errors;	# List of errors to ignore (parameter)
 our @ignore;		# List of errors to ignore (array)
 our $initial;
 our $no_recursion = 0;
 our $maxdepth;
+our $no_markers = 0;
+our $opt_derive_func_data = 0;
+our $opt_external = 1;
+our $opt_no_external;
+our $debug = 0;
+our $gcov_caps;
+our @gcov_options;
+our @internal_dirs;
+our $opt_config_file;
+our $opt_gcov_all_blocks = 1;
+our $opt_compat;
+our %opt_rc;
+our %compat_value;
+our $gcno_split_crc;
+our $func_coverage = 1;
+our $br_coverage = 0;
+our $rc_auto_base = 1;
 
 our $cwd = `pwd`;
 chomp($cwd);
@@ -141,8 +263,22 @@
 $SIG{__WARN__} = \&warn_handler;
 $SIG{__DIE__} = \&die_handler;
 
+# Prettify version string
+$lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
+
+# Set LANG so that gcov output will be in a unified format
+$ENV{"LANG"} = "C";
+
+# Check command line for a configuration file name
+Getopt::Long::Configure("pass_through", "no_auto_abbrev");
+GetOptions("config-file=s" => \$opt_config_file,
+	   "rc=s%" => \%opt_rc);
+Getopt::Long::Configure("default");
+
 # Read configuration file if available
-if (-r $ENV{"HOME"}."/.lcovrc")
+if (defined($opt_config_file)) {
+	$config = read_config($opt_config_file);
+} elsif (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
 {
 	$config = read_config($ENV{"HOME"}."/.lcovrc");
 }
@@ -151,15 +287,23 @@
 	$config = read_config("/etc/lcovrc");
 }
 
-if ($config)
+if ($config || %opt_rc)
 {
-	# Copy configuration file values to variables
+	# Copy configuration file and --rc values to variables
 	apply_config({
 		"geninfo_gcov_tool"		=> \$gcov_tool,
 		"geninfo_adjust_testname"	=> \$adjust_testname,
 		"geninfo_checksum"		=> \$checksum,
 		"geninfo_no_checksum"		=> \$no_checksum, # deprecated
-		"geninfo_compat_libtool"	=> \$compat_libtool});
+		"geninfo_compat_libtool"	=> \$opt_compat_libtool,
+		"geninfo_external"		=> \$opt_external,
+		"geninfo_gcov_all_blocks"	=> \$opt_gcov_all_blocks,
+		"geninfo_compat"		=> \$opt_compat,
+		"geninfo_adjust_src_path"	=> \$rc_adjust_src_path,
+		"geninfo_auto_base"		=> \$rc_auto_base,
+		"lcov_function_coverage"	=> \$func_coverage,
+		"lcov_branch_coverage"		=> \$br_coverage,
+	});
 
 	# Merge options
 	if (defined($no_checksum))
@@ -167,24 +311,53 @@
 		$checksum = ($no_checksum ? 0 : 1);
 		$no_checksum = undef;
 	}
+
+	# Check regexp
+	if (defined($rc_adjust_src_path)) {
+		my ($pattern, $replace) = split(/\s*=>\s*/,
+						$rc_adjust_src_path);
+		local $SIG{__DIE__};
+		eval '$adjust_src_pattern = qr>'.$pattern.'>;';
+		if (!defined($adjust_src_pattern)) {
+			my $msg = $@;
+
+			chomp($msg);
+			$msg =~ s/at \(eval.*$//;
+			warn("WARNING: invalid pattern in ".
+			     "geninfo_adjust_src_path: $msg\n");
+		} elsif (!defined($replace)) {
+			# If no replacement is specified, simply remove pattern
+			$adjust_src_replace = "";
+		} else {
+			$adjust_src_replace = $replace;
+		}
+	}
 }
 
 # Parse command line options
-if (!GetOptions("test-name=s" => \$test_name,
-		"output-filename=s" => \$output_filename,
+if (!GetOptions("test-name|t=s" => \$test_name,
+		"output-filename|o=s" => \$output_filename,
 		"checksum" => \$checksum,
 		"no-checksum" => \$no_checksum,
-		"base-directory=s" => \$base_directory,
-		"version" =>\$version,
-		"quiet" => \$quiet,
-		"help|?" => \$help,
-		"follow" => \$follow,
-		"compat-libtool" => \$compat_libtool,
-		"no-compat-libtool" => \$no_compat_libtool,
+		"base-directory|b=s" => \$base_directory,
+		"version|v" =>\$version,
+		"quiet|q" => \$quiet,
+		"help|h|?" => \$help,
+		"follow|f" => \$follow,
+		"compat-libtool" => \$opt_compat_libtool,
+		"no-compat-libtool" => \$opt_no_compat_libtool,
 		"gcov-tool=s" => \$gcov_tool,
 		"ignore-errors=s" => \@ignore_errors,
 		"initial|i" => \$initial,
 		"no-recursion" => \$no_recursion,
+		"no-markers" => \$no_markers,
+		"derive-func-data" => \$opt_derive_func_data,
+		"debug" => \$debug,
+		"external" => \$opt_external,
+		"no-external" => \$opt_no_external,
+		"compat=s" => \$opt_compat,
+		"config-file=s" => \$opt_config_file,
+		"rc=s%" => \%opt_rc,
 		))
 {
 	print(STDERR "Use $tool_name --help to get usage information\n");
@@ -199,10 +372,15 @@
 		$no_checksum = undef;
 	}
 
-	if (defined($no_compat_libtool))
+	if (defined($opt_no_compat_libtool))
 	{
-		$compat_libtool = ($no_compat_libtool ? 0 : 1);
-		$no_compat_libtool = undef;
+		$opt_compat_libtool = ($opt_no_compat_libtool ? 0 : 1);
+		$opt_no_compat_libtool = undef;
+	}
+
+	if (defined($opt_no_external)) {
+		$opt_external = 0;
+		$opt_no_external = undef;
 	}
 }
 
@@ -222,6 +400,30 @@
 	exit(0);
 }
 
+# Check gcov tool
+if (system_no_output(3, $gcov_tool, "--help") == -1)
+{
+	die("ERROR: need tool $gcov_tool!\n");
+}
+
+($gcov_version, $gcov_version_string) = get_gcov_version();
+
+# Determine gcov options
+$gcov_caps = get_gcov_capabilities();
+push(@gcov_options, "-b") if ($gcov_caps->{'branch-probabilities'} &&
+			      ($br_coverage || $func_coverage));
+push(@gcov_options, "-c") if ($gcov_caps->{'branch-counts'} &&
+			      $br_coverage);
+push(@gcov_options, "-a") if ($gcov_caps->{'all-blocks'} &&
+			      $opt_gcov_all_blocks && $br_coverage);
+push(@gcov_options, "-p") if ($gcov_caps->{'preserve-paths'});
+
+# Determine compatibility modes
+parse_compat_modes($opt_compat);
+
+# Determine which errors the user wants us to ignore
+parse_ignore_errors(@ignore_errors);
+
 # Make sure test names only contain valid characters
 if ($test_name =~ s/\W/_/g)
 {
@@ -263,17 +465,6 @@
 	$checksum = 0;
 }
 
-# Determine libtool compatibility mode
-if (defined($compat_libtool))
-{
-	$compat_libtool = ($compat_libtool? 1 : 0);
-}
-else
-{
-	# Default is on
-	$compat_libtool = 1;
-}
-
 # Determine max depth for recursion
 if ($no_recursion)
 {
@@ -302,41 +493,9 @@
 	}
 }
 
-if (@ignore_errors)
-{
-	my @expanded;
-	my $error;
-
-	# Expand comma-separated entries
-	foreach (@ignore_errors) {
-		if (/,/)
-		{
-			push(@expanded, split(",", $_));
-		}
-		else
-		{
-			push(@expanded, $_);
-		}
-	}
-
-	foreach (@expanded)
-	{
-		/^gcov$/ && do { $ignore[$ERROR_GCOV] = 1; next; } ;
-		/^source$/ && do { $ignore[$ERROR_SOURCE] = 1; next; };
-		die("ERROR: unknown argument for --ignore-errors: $_\n");
-	}
-}
-
-if (system_no_output(3, $gcov_tool, "--help") == -1)
-{
-	die("ERROR: need tool $gcov_tool!\n");
-}
-
-$gcov_version = get_gcov_version();
-
 if ($gcov_version < $GCOV_VERSION_3_4_0)
 {
-	if (defined($compatibility) && $compatibility eq $COMPAT_HAMMER)
+	if (is_compat($COMPAT_MODE_HAMMER))
 	{
 		$data_file_extension = ".da";
 		$graph_file_extension = ".bbg";
@@ -353,19 +512,13 @@
 	$graph_file_extension = ".gcno";
 }	
 
-# Check for availability of --preserve-paths option of gcov
-if (`$gcov_tool --help` =~ /--preserve-paths/)
-{
-	$preserve_paths = "--preserve-paths";
-}
-
 # Check output filename
 if (defined($output_filename) && ($output_filename ne "-"))
 {
 	# Initially create output filename, data is appended
 	# for each data file processed
 	local *DUMMY_HANDLE;
-	open(DUMMY_HANDLE, ">$output_filename")
+	open(DUMMY_HANDLE, ">", $output_filename)
 		or die("ERROR: cannot create $output_filename!\n");
 	close(DUMMY_HANDLE);
 
@@ -377,20 +530,20 @@
 	}
 }
 
-# Do something
-if ($initial)
-{
-	foreach (@data_directory)
-	{
-		gen_initial_info($_);
-	}
+# Build list of directories to identify external files
+foreach my $entry(@data_directory, $base_directory) {
+	next if (!defined($entry));
+	push(@internal_dirs, solve_relative_path($cwd, $entry));
 }
-else
-{
-	foreach (@data_directory)
-	{
-		gen_info($_);
-	}
+
+# Do something
+foreach my $entry (@data_directory) {
+	gen_info($entry);
+}
+
+if ($initial && $br_coverage) {
+	warn("Note: --initial does not generate branch coverage ".
+	     "data\n");
 }
 info("Finished .info-file creation\n");
 
@@ -426,15 +579,54 @@
       --(no-)checksum               Enable (disable) line checksumming
       --(no-)compat-libtool         Enable (disable) libtool compatibility mode
       --gcov-tool TOOL              Specify gcov tool location
-      --ignore-errors ERROR         Continue after ERROR (gcov, source)
-      --no-recursion                Exlude subdirectories from processing
-      --function-coverage           Capture function call counts
+      --ignore-errors ERROR         Continue after ERROR (gcov, source, graph)
+      --no-recursion                Exclude subdirectories from processing
+      --no-markers                  Ignore exclusion markers in source code
+      --derive-func-data            Generate function data from line data
+      --(no-)external               Include (ignore) data for external files
+      --config-file FILENAME        Specify configuration file location
+      --rc SETTING=VALUE            Override configuration file setting
+      --compat MODE=on|off|auto     Set compat MODE (libtool, hammer, split_crc)
 
 For more information see: $lcov_url
 END_OF_USAGE
 	;
 }
 
+#
+# get_common_prefix(min_dir, filenames)
+#
+# Return the longest path prefix shared by all filenames. MIN_DIR specifies
+# the minimum number of directories that a filename may have after removing
+# the prefix.
+#
+
+sub get_common_prefix($@)
+{
+	my ($min_dir, @files) = @_;
+	my $file;
+	my @prefix;
+	my $i;
+
+	foreach $file (@files) {
+		my ($v, $d, $f) = splitpath($file);
+		my @comp = splitdir($d);
+
+		if (!@prefix) {
+			@prefix = @comp;
+			next;
+		}
+		for ($i = 0; $i < scalar(@comp) && $i < scalar(@prefix); $i++) {
+			if ($comp[$i] ne $prefix[$i] ||
+			    ((scalar(@comp) - ($i + 1)) <= $min_dir)) {
+				delete(@prefix[$i..scalar(@prefix)]);
+				last;
+			}
+		}
+	}
+
+	return catdir(@prefix);
+}
 
 #
 # gen_info(directory)
@@ -473,52 +665,190 @@
 {
 	my $directory = $_[0];
 	my @file_list;
+	my $file;
+	my $prefix;
+	my $type;
+	my $ext;
+
+	if ($initial) {
+		$type = "graph";
+		$ext = $graph_file_extension;
+	} else {
+		$type = "data";
+		$ext = $data_file_extension;
+	}
 
 	if (-d $directory)
 	{
-		info("Scanning $directory for $data_file_extension ".
-		     "files ...\n");	
+		info("Scanning $directory for $ext files ...\n");
 
-		@file_list = `find "$directory" $maxdepth $follow -name \\*$data_file_extension -type f 2>/dev/null`;
+		@file_list = `find "$directory" $maxdepth $follow -name \\*$ext -type f 2>/dev/null`;
 		chomp(@file_list);
-		@file_list or die("ERROR: no $data_file_extension files found ".
-				  "in $directory!\n");
-		info("Found %d data files in %s\n", $#file_list+1, $directory);
+		@file_list or
+			die("ERROR: no $ext files found in $directory!\n");
+		$prefix = get_common_prefix(1, @file_list);
+		info("Found %d %s files in %s\n", $#file_list+1, $type,
+		     $directory);
 	}
 	else
 	{
 		@file_list = ($directory);
+		$prefix = "";
 	}
 
 	# Process all files in list
-	foreach (@file_list) { process_dafile($_); }
+	foreach $file (@file_list) {
+		# Process file
+		if ($initial) {
+			process_graphfile($file, $prefix);
+		} else {
+			process_dafile($file, $prefix);
+		}
+	}
 }
 
 
 #
-# process_dafile(da_filename)
+# derive_data(contentdata, funcdata, bbdata)
+#
+# Calculate function coverage data by combining line coverage data and the
+# list of lines belonging to a function.
+#
+# contentdata: [ instr1, count1, source1, instr2, count2, source2, ... ]
+# instr<n>: Instrumentation flag for line n
+# count<n>: Execution count for line n
+# source<n>: Source code for line n
+#
+# funcdata: [ count1, func1, count2, func2, ... ]
+# count<n>: Execution count for function number n
+# func<n>: Function name for function number n
+#
+# bbdata: function_name -> [ line1, line2, ... ]
+# line<n>: Line number belonging to the corresponding function
+#
+
+sub derive_data($$$)
+{
+	my ($contentdata, $funcdata, $bbdata) = @_;
+	my @gcov_content = @{$contentdata};
+	my @gcov_functions = @{$funcdata};
+	my %fn_count;
+	my %ln_fn;
+	my $line;
+	my $maxline;
+	my %fn_name;
+	my $fn;
+	my $count;
+
+	if (!defined($bbdata)) {
+		return @gcov_functions;
+	}
+
+	# First add existing function data
+	while (@gcov_functions) {
+		$count = shift(@gcov_functions);
+		$fn = shift(@gcov_functions);
+
+		$fn_count{$fn} = $count;
+	}
+
+	# Convert line coverage data to function data
+	foreach $fn (keys(%{$bbdata})) {
+		my $line_data = $bbdata->{$fn};
+		my $line;
+		my $fninstr = 0;
+
+		if ($fn eq "") {
+			next;
+		}
+		# Find the lowest line count for this function
+		$count = 0;
+		foreach $line (@$line_data) {
+			my $linstr = $gcov_content[ ( $line - 1 ) * 3 + 0 ];
+			my $lcount = $gcov_content[ ( $line - 1 ) * 3 + 1 ];
+
+			next if (!$linstr);
+			$fninstr = 1;
+			if (($lcount > 0) &&
+			    (($count == 0) || ($lcount < $count))) {
+				$count = $lcount;
+			}
+		}
+		next if (!$fninstr);
+		$fn_count{$fn} = $count;
+	}
+
+
+	# Check if we got data for all functions
+	foreach $fn (keys(%fn_name)) {
+		if ($fn eq "") {
+			next;
+		}
+		if (defined($fn_count{$fn})) {
+			next;
+		}
+		warn("WARNING: no derived data found for function $fn\n");
+	}
+
+	# Convert hash to list in @gcov_functions format
+	foreach $fn (sort(keys(%fn_count))) {
+		push(@gcov_functions, $fn_count{$fn}, $fn);
+	}
+
+	return @gcov_functions;
+}
+
+#
+# get_filenames(directory, pattern)
+#
+# Return a list of filenames found in directory which match the specified
+# pattern.
+#
+# Die on error.
+#
+
+sub get_filenames($$)
+{
+	my ($dirname, $pattern) = @_;
+	my @result;
+	my $directory;
+	local *DIR;
+
+	opendir(DIR, $dirname) or
+		die("ERROR: cannot read directory $dirname\n");
+	while ($directory = readdir(DIR)) {
+		push(@result, $directory) if ($directory =~ /$pattern/);
+	}
+	closedir(DIR);
+
+	return @result;
+}
+
+#
+# process_dafile(da_filename, dir)
 #
 # Create a .info file for a single data file.
 #
 # Die on error.
 #
 
-sub process_dafile($)
+sub process_dafile($$)
 {
-	info("Processing %s\n", $_[0]);
-
+	my ($file, $dir) = @_;
 	my $da_filename;	# Name of data file to process
 	my $da_dir;		# Directory of data file
 	my $source_dir;		# Directory of source file
 	my $da_basename;	# data filename without ".da/.gcda" extension
 	my $bb_filename;	# Name of respective graph file
-	my %bb_content;		# Contents of graph file
+	my $bb_basename;	# Basename of the original graph file
+	my $graph;		# Contents of graph file
+	my $instr;		# Contents of graph file part 2
 	my $gcov_error;		# Error code of gcov tool
 	my $object_dir;		# Directory containing all object files
 	my $source_filename;	# Name of a source code file
 	my $gcov_file;		# Name of a .gcov file
 	my @gcov_content;	# Content of a .gcov file
-	my @gcov_branches;	# Branch content of a .gcov file
+	my $gcov_branches;	# Branch content of a .gcov file
 	my @gcov_functions;	# Function calls of a .gcov file
 	my @gcov_list;		# List of generated .gcov files
 	my $line_number;	# Line number count
@@ -526,28 +856,31 @@
 	my $lines_found;	# Number of instrumented lines found
 	my $funcs_hit;		# Number of instrumented functions hit
 	my $funcs_found;	# Number of instrumented functions found
+	my $br_hit;
+	my $br_found;
 	my $source;		# gcov source header information
 	my $object;		# gcov object header information
 	my @matches;		# List of absolute paths matching filename
 	my @unprocessed;	# List of unprocessed source code files
 	my $base_dir;		# Base directory for current file
+	my @tmp_links;		# Temporary links to be cleaned up
 	my @result;
 	my $index;
 	my $da_renamed;		# If data file is to be renamed
 	local *INFO_HANDLE;
 
+	info("Processing %s\n", abs2rel($file, $dir));
 	# Get path to data file in absolute and normalized form (begins with /,
 	# contains no more ../ or ./)
-	$da_filename = solve_relative_path($cwd, $_[0]);
+	$da_filename = solve_relative_path($cwd, $file);
 
 	# Get directory and basename of data file
 	($da_dir, $da_basename) = split_filename($da_filename);
 
-	# avoid files from .libs dirs 	 
-	if ($compat_libtool && $da_dir =~ m/(.*)\/\.libs$/) {
-		$source_dir = $1;
-	} else {
-		$source_dir = $da_dir;
+	$source_dir = $da_dir;
+	if (is_compat($COMPAT_MODE_LIBTOOL)) {
+		# Avoid files from .libs dirs 	 
+		$source_dir =~ s/\.libs$//;
 	}
 
 	if (-z $da_filename)
@@ -577,7 +910,8 @@
 	}
 
 	# Construct name of graph file
-	$bb_filename = $da_dir."/".$da_basename.$graph_file_extension;
+	$bb_basename = $da_basename.$graph_file_extension;
+	$bb_filename = "$da_dir/$bb_basename";
 
 	# Find out the real location of graph file in case we're just looking at
 	# a link
@@ -601,21 +935,27 @@
 	# information about functions and their source code positions.
 	if ($gcov_version < $GCOV_VERSION_3_4_0)
 	{
-		if (defined($compatibility) && $compatibility eq $COMPAT_HAMMER)
+		if (is_compat($COMPAT_MODE_HAMMER))
 		{
-			%bb_content = read_hammer_bbg_file($bb_filename,
-							   $base_dir);
+			($instr, $graph) = read_bbg($bb_filename);
 		}
 		else
 		{
-			%bb_content = read_bb_file($bb_filename, $base_dir);
+			($instr, $graph) = read_bb($bb_filename);
 		}
 	} 
 	else
 	{
-		%bb_content = read_gcno_file($bb_filename, $base_dir);
+		($instr, $graph) = read_gcno($bb_filename);
 	} 
 
+	# Try to find base directory automatically if requested by user
+	if ($rc_auto_base) {
+		$base_dir = find_base_from_graph($base_dir, $instr, $graph);
+	}
+
+	($instr, $graph) = adjust_graph_filenames($base_dir, $instr, $graph);
+
 	# Set $object_dir to real location of object files. This may differ
 	# from $da_dir if the graph file is just a link to the "real" object
 	# file location.
@@ -630,9 +970,21 @@
 		       "$object_dir/$da_basename$data_file_extension")
 			and die ("ERROR: cannot create link $object_dir/".
 				 "$da_basename$data_file_extension!\n");
+		push(@tmp_links,
+		     "$object_dir/$da_basename$data_file_extension");
+		# Need to create link to graph file if basename of link
+		# and file are different (CONFIG_MODVERSION compat)
+		if ((basename($bb_filename) ne $bb_basename) &&
+		    (! -e "$object_dir/$bb_basename")) {
+			symlink($bb_filename, "$object_dir/$bb_basename") or
+				warn("WARNING: cannot create link ".
+				     "$object_dir/$bb_basename\n");
+			push(@tmp_links, "$object_dir/$bb_basename");
+		}
 	}
 
 	# Change to directory containing data files and apply GCOV
+	debug("chdir($base_dir)\n");
         chdir($base_dir);
 
 	if ($da_renamed)
@@ -644,19 +996,8 @@
 	}
 
 	# Execute gcov command and suppress standard output
-	if ($preserve_paths)
-	{
-		$gcov_error = system_no_output(1, $gcov_tool, $da_filename,
-					       "-o", $object_dir,
-					       "--preserve-paths",
-					       "-b");
-	}
-	else
-	{
-		$gcov_error = system_no_output(1, $gcov_tool, $da_filename,
-					       "-o", $object_dir,
-					       "-b");
-	}
+	$gcov_error = system_no_output(1, $gcov_tool, $da_filename,
+				       "-o", $object_dir, @gcov_options);
 
 	if ($da_renamed)
 	{
@@ -664,10 +1005,9 @@
 			and die ("ERROR: cannot rename $da_filename.ori");
 	}
 
-	# Clean up link
-	if ($object_dir ne $da_dir)
-	{
-		unlink($object_dir."/".$da_basename.$data_file_extension);
+	# Clean up temporary links
+	foreach (@tmp_links) {
+		unlink($_);
 	}
 
 	if ($gcov_error)
@@ -681,7 +1021,7 @@
 	}
 
 	# Collect data from resulting .gcov files and create .info file
-	@gcov_list = glob("*.gcov");
+	@gcov_list = get_filenames('.', '\.gcov$');
 
 	# Check for files
 	if (!@gcov_list)
@@ -700,7 +1040,7 @@
 		else
 		{
 			# Append to output file
-			open(INFO_HANDLE, ">>$output_filename")
+			open(INFO_HANDLE, ">>", $output_filename)
 				or die("ERROR: cannot write to ".
 				       "$output_filename!\n");
 		}
@@ -708,7 +1048,7 @@
 	else
 	{
 		# Open .info file for output
-		open(INFO_HANDLE, ">$da_filename.info")
+		open(INFO_HANDLE, ">", "$da_filename.info")
 			or die("ERROR: cannot create $da_filename.info!\n");
 	}
 
@@ -717,20 +1057,35 @@
 
 	# Traverse the list of generated .gcov files and combine them into a
 	# single .info file
-	@unprocessed = keys(%bb_content);
-	foreach $gcov_file (@gcov_list)
+	@unprocessed = keys(%{$instr});
+	foreach $gcov_file (sort(@gcov_list))
 	{
+		my $i;
+		my $num;
+
+		# Skip gcov file for gcc built-in code
+		next if ($gcov_file eq "<built-in>.gcov");
+
 		($source, $object) = read_gcov_header($gcov_file);
 
-		if (defined($source))
-		{
-			$source = solve_relative_path($base_dir, $source);
+		if (!defined($source)) {
+			# Derive source file name from gcov file name if
+			# header format could not be parsed
+			$source = $gcov_file;
+			$source =~ s/\.gcov$//;
+		}
+
+		$source = solve_relative_path($base_dir, $source);
+
+		if (defined($adjust_src_pattern)) {
+			# Apply transformation as specified by user
+			$source =~ s/$adjust_src_pattern/$adjust_src_replace/g;
 		}
 
 		# gcov will happily create output even if there's no source code
 		# available - this interferes with checksum creation so we need
 		# to pull the emergency brake here.
-		if (defined($source) && ! -r $source && $checksum)
+		if (! -r $source && $checksum)
 		{
 			if ($ignore[$ERROR_SOURCE])
 			{
@@ -741,8 +1096,7 @@
 			die("ERROR: could not read source file $source\n");
 		}
 
-		@matches = match_filename(defined($source) ? $source :
-					  $gcov_file, keys(%bb_content));
+		@matches = match_filename($source, keys(%{$instr}));
 
 		# Skip files that are not mentioned in the graph file
 		if (!@matches)
@@ -756,8 +1110,14 @@
 
 		# Read in contents of gcov file
 		@result = read_gcov_file($gcov_file);
+		if (!defined($result[0])) {
+			warn("WARNING: skipping unreadable file ".
+			     $gcov_file."\n");
+			unlink($gcov_file);
+			next;
+		}
 		@gcov_content = @{$result[0]};
-		@gcov_branches = @{$result[1]};
+		$gcov_branches = $result[1];
 		@gcov_functions = @{$result[2]};
 
 		# Skip empty files
@@ -790,22 +1150,61 @@
 			}
 		}
 
+		# Skip external files if requested
+		if (!$opt_external) {
+			if (is_external($source_filename)) {
+				info("  ignoring data for external file ".
+				     "$source_filename\n");
+				unlink($gcov_file);
+				next;
+			}
+		}
+
 		# Write absolute path of source file
 		printf(INFO_HANDLE "SF:%s\n", $source_filename);
 
-		# Write function-related information
-		if (defined($bb_content{$source_filename}))
-		{
-			foreach (split(",",$bb_content{$source_filename}))
-			{
-				my ($fn, $line) = split("=", $_);
+		# If requested, derive function coverage data from
+		# line coverage data of the first line of a function
+		if ($opt_derive_func_data) {
+			@gcov_functions =
+				derive_data(\@gcov_content, \@gcov_functions,
+					    $graph->{$source_filename});
+		}
 
+		# Write function-related information
+		if (defined($graph->{$source_filename}))
+		{
+			my $fn_data = $graph->{$source_filename};
+			my $fn;
+
+			foreach $fn (sort
+				{$fn_data->{$a}->[0] <=> $fn_data->{$b}->[0]}
+				keys(%{$fn_data})) {
+				my $ln_data = $fn_data->{$fn};
+				my $line = $ln_data->[0];
+
+				# Skip empty function
 				if ($fn eq "") {
 					next;
 				}
+				# Remove excluded functions
+				if (!$no_markers) {
+					my $gfn;
+					my $found = 0;
+
+					foreach $gfn (@gcov_functions) {
+						if ($gfn eq $fn) {
+							$found = 1;
+							last;
+						}
+					}
+					if (!$found) {
+						next;
+					}
+				}
 
 				# Normalize function name
-				$fn =~ s/\W/_/g;
+				$fn = filter_fn_name($fn);
 
 				print(INFO_HANDLE "FN:$line,$fn\n");
 			}
@@ -820,18 +1219,42 @@
 		$funcs_hit = 0;
 		while (@gcov_functions)
 		{
-			printf(INFO_HANDLE "FNDA:%s,%s\n",
-				       $gcov_functions[0],
-				       $gcov_functions[1]);
-				$funcs_found++;
-			$funcs_hit++ if $gcov_functions[0];
-			splice(@gcov_functions,0,2);
+			my $count = shift(@gcov_functions);
+			my $fn = shift(@gcov_functions);
+
+			$fn = filter_fn_name($fn);
+			printf(INFO_HANDLE "FNDA:$count,$fn\n");
+			$funcs_found++;
+			$funcs_hit++ if ($count > 0);
 		}
 		if ($funcs_found > 0) {
 			printf(INFO_HANDLE "FNF:%s\n", $funcs_found);
 			printf(INFO_HANDLE "FNH:%s\n", $funcs_hit);
 		}
 
+		# Write coverage information for each instrumented branch:
+		#
+		#   BRDA:<line number>,<block number>,<branch number>,<taken>
+		#
+		# where 'taken' is the number of times the branch was taken
+		# or '-' if the block to which the branch belongs was never
+		# executed
+		$br_found = 0;
+		$br_hit = 0;
+		$num = br_gvec_len($gcov_branches);
+		for ($i = 0; $i < $num; $i++) {
+			my ($line, $block, $branch, $taken) =
+				br_gvec_get($gcov_branches, $i);
+
+			print(INFO_HANDLE "BRDA:$line,$block,$branch,$taken\n");
+			$br_found++;
+			$br_hit++ if ($taken ne '-' && $taken > 0);
+		}
+		if ($br_found > 0) {
+			printf(INFO_HANDLE "BRF:%s\n", $br_found);
+			printf(INFO_HANDLE "BRH:%s\n", $br_hit);
+		}
+
 		# Reset line counters
 		$line_number = 0;
 		$lines_found = 0;
@@ -862,27 +1285,6 @@
 			splice(@gcov_content,0,3);
 		}
 
-		#--
-		#-- BA: <code-line>, <branch-coverage>
-		#--
-		#-- print one BA line for every branch of a
-		#-- conditional.  <branch-coverage> values
-		#-- are:
-		#--     0 - not executed
-		#--     1 - executed but not taken
-		#--     2 - executed and taken
-		#--
-		while (@gcov_branches)
-		{
-			if ($gcov_branches[0])
-			{
-				printf(INFO_HANDLE "BA:%s,%s\n",
-				       $gcov_branches[0],
-				       $gcov_branches[1]);
-			}
-			splice(@gcov_branches,0,2);
-		}
-
 		# Write line statistics and section separator
 		printf(INFO_HANDLE "LF:%s\n", $lines_found);
 		printf(INFO_HANDLE "LH:%s\n", $lines_hit);
@@ -922,8 +1324,40 @@
 {
 	my $path = $_[0];
 	my $dir = $_[1];
+	my $volume;
+	my $directories;
+	my $filename;
+	my @dirs;			# holds path elements
 	my $result;
 
+	# Convert from Windows path to msys path
+	if( $^O eq "msys" )
+	{
+		# search for a windows drive letter at the beginning
+		($volume, $directories, $filename) = File::Spec::Win32->splitpath( $dir );
+		if( $volume ne '' )
+		{
+			my $uppercase_volume;
+			# transform c/d\../e/f\g to Windows style c\d\..\e\f\g
+			$dir = File::Spec::Win32->canonpath( $dir );
+			# use Win32 module to retrieve path components
+			# $uppercase_volume is not used any further
+			( $uppercase_volume, $directories, $filename ) = File::Spec::Win32->splitpath( $dir );
+			@dirs = File::Spec::Win32->splitdir( $directories );
+			
+			# prepend volume, since in msys C: is always mounted to /c
+			$volume =~ s|^([a-zA-Z]+):|/\L$1\E|;
+			unshift( @dirs, $volume );
+			
+			# transform to Unix style '/' path
+			$directories = File::Spec->catdir( @dirs );
+			$dir = File::Spec->catpath( '', $directories, $filename );
+		} else {
+			# eliminate '\' path separators
+			$dir = File::Spec->canonpath( $dir );
+		}
+	}
+
 	$result = $dir;
 	# Prepend path if not absolute
 	if ($dir =~ /^[^\/]/)
@@ -936,6 +1370,10 @@
 
 	# Remove .
 	$result =~ s/\/\.\//\//g;
+	$result =~ s/\/\.$/\//g;
+
+	# Remove trailing /
+	$result =~ s/\/$//g;
 
 	# Solve ..
 	while ($result =~ s/\/[^\/]+\/\.\.\//\//)
@@ -958,28 +1396,42 @@
 
 sub match_filename($@)
 {
-	my $filename = shift;
-	my @list = @_;
+	my ($filename, @list) = @_;
+	my ($vol, $dir, $file) = splitpath($filename);
+	my @comp = splitdir($dir);
+	my $comps = scalar(@comp);
+	my $entry;
 	my @result;
 
-	$filename =~ s/^(.*).gcov$/$1/;
+entry:
+	foreach $entry (@list) {
+		my ($evol, $edir, $efile) = splitpath($entry);
+		my @ecomp;
+		my $ecomps;
+		my $i;
 
-	if ($filename =~ /^\/(.*)$/)
-	{
-		$filename = "$1";
-	}
-
-	foreach (@list)
-	{
-		if (/\/\Q$filename\E(.*)$/ && $1 eq "")
-		{
-			@result = (@result, $_);
+		# Filename component must match
+		if ($efile ne $file) {
+			next;
 		}
+		# Check directory components last to first for match
+		@ecomp = splitdir($edir);
+		$ecomps = scalar(@ecomp);
+		if ($ecomps < $comps) {
+			next;
+		}
+		for ($i = 0; $i < $comps; $i++) {
+			if ($comp[$comps - $i - 1] ne
+			    $ecomp[$ecomps - $i - 1]) {
+				next entry;
+			}
+		}
+		push(@result, $entry),
 	}
+
 	return @result;
 }
 
-
 #
 # solve_ambiguous_match(rel_filename, matches_ref, gcov_content_ref)
 #
@@ -1006,7 +1458,7 @@
 	{
 
 		# Compare file contents
-		open(SOURCE, $filename)
+		open(SOURCE, "<", $filename)
 			or die("ERROR: cannot read $filename!\n");
 
 		$no_match = 0;
@@ -1014,6 +1466,9 @@
 		{
 			chomp;
 
+			# Also remove CR from line-end
+			s/\015$//;
+
 			if ($_ ne @$content[$index])
 			{
 				$no_match = 1;
@@ -1052,21 +1507,6 @@
 
 
 #
-# get_dir(filename);
-#
-# Return the directory component of a given FILENAME.
-#
-
-sub get_dir($)
-{
-	my @components = split("/", $_[0]);
-	pop(@components);
-
-	return join("/", @components);
-}
-
-
-#
 # read_gcov_header(gcov_filename)
 #
 # Parse file GCOV_FILENAME and return a list containing the following
@@ -1088,7 +1528,7 @@
 	my $object;
 	local *INPUT;
 
-	if (!open(INPUT, $_[0]))
+	if (!open(INPUT, "<", $_[0]))
 	{
 		if ($ignore_errors[$ERROR_GCOV])
 		{
@@ -1102,6 +1542,9 @@
 	{
 		chomp($_);
 
+		# Also remove CR from line-end
+		s/\015$//;
+
 		if (/^\s+-:\s+0:Source:(.*)$/)
 		{
 			# Source: header entry
@@ -1125,6 +1568,84 @@
 
 
 #
+# br_gvec_len(vector)
+#
+# Return the number of entries in the branch coverage vector.
+#
+
+sub br_gvec_len($)
+{
+	my ($vec) = @_;
+
+	return 0 if (!defined($vec));
+	return (length($vec) * 8 / $BR_VEC_WIDTH) / $BR_VEC_ENTRIES;
+}
+
+
+#
+# br_gvec_get(vector, number)
+#
+# Return an entry from the branch coverage vector.
+#
+
+sub br_gvec_get($$)
+{
+	my ($vec, $num) = @_;
+	my $line;
+	my $block;
+	my $branch;
+	my $taken;
+	my $offset = $num * $BR_VEC_ENTRIES;
+
+	# Retrieve data from vector
+	$line	= vec($vec, $offset + $BR_LINE, $BR_VEC_WIDTH);
+	$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
+	if ($taken == 0) {
+		$taken = "-";
+	} else {
+		$taken--;
+	}
+
+	return ($line, $block, $branch, $taken);
+}
+
+
+#
+# br_gvec_push(vector, line, block, branch, taken)
+#
+# Add an entry to the branch coverage vector.
+#
+
+sub br_gvec_push($$$$$)
+{
+	my ($vec, $line, $block, $branch, $taken) = @_;
+	my $offset;
+
+	$vec = "" if (!defined($vec));
+	$offset = br_gvec_len($vec) * $BR_VEC_ENTRIES;
+
+	# Encode taken value into an integer
+	if ($taken eq "-") {
+		$taken = 0;
+	} else {
+		$taken++;
+	}
+
+	# Add to vector
+	vec($vec, $offset + $BR_LINE, $BR_VEC_WIDTH) = $line;
+	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;
+}
+
+
+#
 # read_gcov_file(gcov_filename)
 #
 # Parse file GCOV_FILENAME (.gcov file format) and return the list:
@@ -1137,8 +1658,8 @@
 # $result[($line_number-1)*3+1] = execution count for line $line_number
 # $result[($line_number-1)*3+2] = source code text for line $line_number
 #
-# gcov_branch is a list of 2 elements
-# (linenumber, branch result) for each branch
+# gcov_branch is a vector of 4 4-byte long elements for each branch:
+# line number, block number, branch number, count + 1 or 0
 #
 # gcov_func is a list of 2 elements
 # (number of calls, function name) for each function
@@ -1150,13 +1671,23 @@
 {
 	my $filename = $_[0];
 	my @result = ();
-	my @branches = ();
+	my $branches = "";
 	my @functions = ();
 	my $number;
+	my $exclude_flag = 0;
+	my $exclude_line = 0;
+	my $last_block = $UNNAMED_BLOCK;
+	my $last_line = 0;
 	local *INPUT;
 
-	open(INPUT, $filename)
-		or die("ERROR: cannot read $filename!\n");
+	if (!open(INPUT, "<", $filename)) {
+		if ($ignore_errors[$ERROR_GCOV])
+		{
+			warn("WARNING: cannot read $filename!\n");
+			return (undef, undef, undef);
+		}
+		die("ERROR: cannot read $filename!\n");
+	}
 
 	if ($gcov_version < $GCOV_VERSION_3_3_0)
 	{
@@ -1165,29 +1696,19 @@
 		{
 			chomp($_);
 
-			if (/^\t\t(.*)$/)
-			{
-				# Uninstrumented line
-				push(@result, 0);
-				push(@result, 0);
-				push(@result, $1);
-			}
-			elsif (/^branch/)
-			{
-				# Branch execution data
-				push(@branches, scalar(@result) / 3);
-				if (/^branch \d+ never executed$/)
-				{
-					push(@branches, 0);
-				}
-				elsif (/^branch \d+ taken = 0%/)
-				{
-					push(@branches, 1);
-				}
-				else
-				{
-					push(@branches, 2);
-				}
+			# Also remove CR from line-end
+			s/\015$//;
+
+			if (/^branch\s+(\d+)\s+taken\s+=\s+(\d+)/) {
+				next if (!$br_coverage);
+				next if ($exclude_line);
+				$branches = br_gvec_push($branches, $last_line,
+						$last_block, $1, $2);
+			} elsif (/^branch\s+(\d+)\s+never\s+executed/) {
+				next if (!$br_coverage);
+				next if ($exclude_line);
+				$branches = br_gvec_push($branches, $last_line,
+						$last_block, $1, '-');
 			}
 			elsif (/^call/ || /^function/)
 			{
@@ -1195,15 +1716,43 @@
 			}
 			else
 			{
+				$last_line++;
+				# Check for exclusion markers
+				if (!$no_markers) {
+					if (/$EXCL_STOP/) {
+						$exclude_flag = 0;
+					} elsif (/$EXCL_START/) {
+						$exclude_flag = 1;
+					}
+					if (/$EXCL_LINE/ || $exclude_flag) {
+						$exclude_line = 1;
+					} else {
+						$exclude_line = 0;
+					}
+				}
 				# Source code execution data
+				if (/^\t\t(.*)$/)
+				{
+					# Uninstrumented line
+					push(@result, 0);
+					push(@result, 0);
+					push(@result, $1);
+					next;
+				}
 				$number = (split(" ",substr($_, 0, 16)))[0];
 
 				# Check for zero count which is indicated
 				# by ######
 				if ($number eq "######") { $number = 0;	}
 
-				push(@result, 1);
-				push(@result, $number);
+				if ($exclude_line) {
+					# Register uninstrumented line instead
+					push(@result, 0);
+					push(@result, 0);
+				} else {
+					push(@result, 1);
+					push(@result, $number);
+				}
 				push(@result, substr($_, 16));
 			}
 		}
@@ -1215,25 +1764,31 @@
 		{
 			chomp($_);
 
-			if (/^branch\s+\d+\s+(\S+)\s+(\S+)/)
-			{
-				# Branch execution data
-				push(@branches, scalar(@result) / 3);
-				if ($1 eq "never")
-				{
-					push(@branches, 0);
-				}
-				elsif ($2 eq "0%")
-				{
-					push(@branches, 1);
-				}
-				else
-				{
-					push(@branches, 2);
-				}
+			# Also remove CR from line-end
+			s/\015$//;
+
+			if (/^\s*(\d+|\$+):\s*(\d+)-block\s+(\d+)\s*$/) {
+				# Block information - used to group related
+				# branches
+				$last_line = $2;
+				$last_block = $3;
+			} elsif (/^branch\s+(\d+)\s+taken\s+(\d+)/) {
+				next if (!$br_coverage);
+				next if ($exclude_line);
+				$branches = br_gvec_push($branches, $last_line,
+						$last_block, $1, $2);
+			} elsif (/^branch\s+(\d+)\s+never\s+executed/) {
+				next if (!$br_coverage);
+				next if ($exclude_line);
+				$branches = br_gvec_push($branches, $last_line,
+						$last_block, $1, '-');
 			}
-			elsif (/^function\s+(\S+)\s+called\s+(\d+)/)
+			elsif (/^function\s+(.+)\s+called\s+(\d+)\s+/)
 			{
+				next if (!$func_coverage);
+				if ($exclude_line) {
+					next;
+				}
 				push(@functions, $2, $1);
 			}
 			elsif (/^call/)
@@ -1242,580 +1797,59 @@
 			}
 			elsif (/^\s*([^:]+):\s*([^:]+):(.*)$/)
 			{
+				my ($count, $line, $code) = ($1, $2, $3);
+
+				$last_line = $line;
+				$last_block = $UNNAMED_BLOCK;
+				# Check for exclusion markers
+				if (!$no_markers) {
+					if (/$EXCL_STOP/) {
+						$exclude_flag = 0;
+					} elsif (/$EXCL_START/) {
+						$exclude_flag = 1;
+					}
+					if (/$EXCL_LINE/ || $exclude_flag) {
+						$exclude_line = 1;
+					} else {
+						$exclude_line = 0;
+					}
+				}
 				# <exec count>:<line number>:<source code>
-				if ($2 eq "0")
+				if ($line eq "0")
 				{
 					# Extra data
 				}
-				elsif ($1 eq "-")
+				elsif ($count eq "-")
 				{
 					# Uninstrumented line
 					push(@result, 0);
 					push(@result, 0);
-					push(@result, $3);
+					push(@result, $code);
 				}
 				else
 				{
-					# Source code execution data
-					$number = $1;
-
-					# Check for zero count
-					if ($number eq "#####")	{ $number = 0; }
-
-					push(@result, 1);
-					push(@result, $number);
-					push(@result, $3);
+					if ($exclude_line) {
+						push(@result, 0);
+						push(@result, 0);
+					} else {
+						# Check for zero count
+						if ($count eq "#####") {
+							$count = 0;
+						}
+						push(@result, 1);
+						push(@result, $count);
+					}
+					push(@result, $code);
 				}
 			}
 		}
 	}
 
 	close(INPUT);
-	return(\@result, \@branches, \@functions);
-}
-
-
-#
-# read_bb_file(bb_filename, base_dir)
-#
-# Read .bb file BB_FILENAME and return a hash containing the following
-# mapping:
-#
-#   filename -> comma-separated list of pairs (function name=starting
-#               line number) to indicate the starting line of a function or
-#               =name to indicate an instrumented line
-#
-# for each entry in the .bb file. Filenames are absolute, i.e. relative
-# filenames are prefixed with BASE_DIR.
-#
-# Die on error.
-#
-
-sub read_bb_file($$)
-{
-	my $bb_filename		= $_[0];
-	my $base_dir = $_[1];
-	my %result;
-	my $filename;
-	my $function_name;
-	my $minus_one		= sprintf("%d", 0x80000001);
-	my $minus_two		= sprintf("%d", 0x80000002);
-	my $value;
-	my $packed_word;
-	local *INPUT;
-
-	open(INPUT, $bb_filename)
-		or die("ERROR: cannot read $bb_filename!\n");
-
-	binmode(INPUT);
-
-	# Read data in words of 4 bytes
-	while (read(INPUT, $packed_word, 4) == 4)
-	{
-		# Decode integer in intel byteorder
-		$value = unpack_int32($packed_word, 0);
-
-		# Note: the .bb file format is documented in GCC info pages
-		if ($value == $minus_one)
-		{
-			# Filename follows
-			$filename = read_string(*INPUT, $minus_one)
-				or die("ERROR: incomplete filename in ".
-				       "$bb_filename!\n");
-
-			# Make path absolute
-			$filename = solve_relative_path($base_dir, $filename);
-
-			# Insert into hash if not yet present.
-			# This is necessary because functions declared as
-			# "inline" are not listed as actual functions in
-			# .bb files
-			if (!$result{$filename})
-			{
-				$result{$filename}="";
-			}
-		}
-		elsif ($value == $minus_two)
-		{
-			# Function name follows
-			$function_name = read_string(*INPUT, $minus_two)
-				 or die("ERROR: incomplete function ".
-					"name in $bb_filename!\n");
-			$function_name =~ s/\W/_/g;
-		}
-		elsif ($value > 0)
-		{
-			if (defined($filename))
-			{
-				$result{$filename} .=
-					($result{$filename} ? "," : "").
-					"=$value";
-			}
-			else
-			{
-				warn("WARNING: unassigned line".
-				     " number in .bb file ".
-				     "$bb_filename\n");
-			}
-			if ($function_name)
-			{
-				# Got a full entry filename, funcname, lineno
-				# Add to resulting hash
-
-				$result{$filename}.=
-				  ($result{$filename} ? "," : "").
-				  join("=",($function_name,$value));
-				undef($function_name);
-			}
-		}
+	if ($exclude_flag) {
+		warn("WARNING: unterminated exclusion section in $filename\n");
 	}
-	close(INPUT);
-
-	if (!scalar(keys(%result)))
-	{
-		die("ERROR: no data found in $bb_filename!\n");
-	}
-	return %result;
-}
-
-
-#
-# read_string(handle, delimiter);
-#
-# Read and return a string in 4-byte chunks from HANDLE until DELIMITER
-# is found.
-#
-# Return empty string on error.
-#
-
-sub read_string(*$)
-{
-	my $HANDLE	= $_[0];
-	my $delimiter	= $_[1];
-	my $string	= "";
-	my $packed_word;
-	my $value;
-
-	while (read($HANDLE,$packed_word,4) == 4)
-	{
-		$value = unpack_int32($packed_word, 0);
-
-		if ($value == $delimiter)
-		{
-			# Remove trailing nil bytes
-			$/="\0";
-			while (chomp($string)) {};
-			$/="\n";
-			return($string);
-		}
-
-		$string = $string.$packed_word;
-	}
-	return("");
-}
-
-
-#
-# read_gcno_file(bb_filename, base_dir)
-#
-# Read .gcno file BB_FILENAME and return a hash containing the following
-# mapping:
-#
-#   filename -> comma-separated list of pairs (function name=starting
-#               line number) to indicate the starting line of a function or
-#               =name to indicate an instrumented line
-#
-# for each entry in the .gcno file. Filenames are absolute, i.e. relative
-# filenames are prefixed with BASE_DIR.
-#
-# Die on error.
-#
-
-sub read_gcno_file($$)
-{
-	my $gcno_filename	= $_[0];
-	my $base_dir = $_[1];
-	my %result;
-	my $filename;
-	my $function_name;
-	my $lineno;
-	my $length;
-	my $value;
-	my $endianness;
-	my $blocks;
-	my $packed_word;
-	my $string;
-	local *INPUT;
-
-	open(INPUT, $gcno_filename)
-		or die("ERROR: cannot read $gcno_filename!\n");
-
-	binmode(INPUT);
-	
-	read(INPUT, $packed_word, 4) == 4
-		or die("ERROR: Invalid gcno file format\n");
-
-	$value = unpack_int32($packed_word, 0);
-	$endianness = !($value == $GCNO_FILE_MAGIC);
-
-	unpack_int32($packed_word, $endianness) == $GCNO_FILE_MAGIC
-		or die("ERROR: gcno file magic does not match\n");
-
-	seek(INPUT, 8, 1);
-
-	# Read data in words of 4 bytes
-	while (read(INPUT, $packed_word, 4) == 4)
-	{
-		# Decode integer in intel byteorder
-		$value = unpack_int32($packed_word, $endianness);
-
-		if ($value == $GCNO_FUNCTION_TAG)
-		{
-			# skip length, ident and checksum
-			seek(INPUT, 12, 1);
-			(undef, $function_name) =
-				read_gcno_string(*INPUT, $endianness);
-			$function_name =~ s/\W/_/g;
-			(undef, $filename) =
-				read_gcno_string(*INPUT, $endianness);
-			$filename = solve_relative_path($base_dir, $filename);
-
-			read(INPUT, $packed_word, 4);
-			$lineno = unpack_int32($packed_word, $endianness);
-
-			$result{$filename}.=
-			    ($result{$filename} ? "," : "").
-				join("=",($function_name,$lineno));
-		}
-		elsif ($value == $GCNO_LINES_TAG)
-		{
-			# Check for names of files containing inlined code
-			# included in this file
-			read(INPUT, $packed_word, 4);
-			$length = unpack_int32($packed_word, $endianness);
-			if ($length > 0)
-			{
-				# Block number
-				read(INPUT, $packed_word, 4);
-				$length--;
-			}
-			while ($length > 0)
-			{
-				read(INPUT, $packed_word, 4);
-				$lineno = unpack_int32($packed_word,
-						       $endianness);
-				$length--;
-				if ($lineno != 0)
-				{
-					if (defined($filename))
-					{
-						$result{$filename} .=
-							($result{$filename} ? "," : "").
-							"=$lineno";
-					}
-					else
-					{
-						warn("WARNING: unassigned line".
-						     " number in .gcno file ".
-						     "$gcno_filename\n");
-					}
-					next;
-				}
-				last if ($length == 0);
-				($blocks, $string) =
-					read_gcno_string(*INPUT, $endianness);
-				if (defined($string))
-				{
-					$filename = $string;
-				}
-				if ($blocks > 1)
-				{
-					$filename = solve_relative_path(
-							$base_dir, $filename);
-					if (!defined($result{$filename}))
-					{
-						$result{$filename} = "";
-					}
-				}
-				$length -= $blocks;
-			}
-		}
-		else
-		{
-			read(INPUT, $packed_word, 4);
-			$length = unpack_int32($packed_word, $endianness);
-			seek(INPUT, 4 * $length, 1);
-		}
-	}
-	close(INPUT);
-
-	if (!scalar(keys(%result)))
-	{
-		die("ERROR: no data found in $gcno_filename!\n");
-	}
-	return %result;
-}
-
-
-#
-# read_gcno_string(handle, endianness);
-#
-# Read a string in 4-byte chunks from HANDLE.
-#
-# Return (number of 4-byte chunks read, string).
-#
-
-sub read_gcno_string(*$)
-{
-	my $handle		= $_[0];
-	my $endianness		= $_[1];
-	my $number_of_blocks	= 0;
-	my $string		= "";
-	my $packed_word;
-
-	read($handle, $packed_word, 4) == 4
-		or die("ERROR: reading string\n");
-
-	$number_of_blocks = unpack_int32($packed_word, $endianness);
-
-	if ($number_of_blocks == 0)
-	{
-		return (1, undef);
-	}
-
-	if (read($handle, $packed_word, 4 * $number_of_blocks) !=
-	     4 * $number_of_blocks)
-	{
-		my $msg = "invalid string size ".(4 * $number_of_blocks)." in ".
-			  "gcno file at position ".tell($handle)."\n";
-		if ($ignore[$ERROR_SOURCE])
-		{
-			warn("WARNING: $msg");
-			return (1, undef);
-		}
-		else
-		{
-			die("ERROR: $msg");
-		}
-	}
-
-	$string = $string . $packed_word;
-
-	# Remove trailing nil bytes
-	$/="\0";
-	while (chomp($string)) {};
-	$/="\n";
-
-	return(1 + $number_of_blocks, $string);
-}
-
-
-#
-# read_hammer_bbg_file(bb_filename, base_dir)
-#
-# Read .bbg file BB_FILENAME and return a hash containing the following
-# mapping:
-#
-#   filename -> comma-separated list of pairs (function name=starting
-#               line number) to indicate the starting line of a function or
-#               =name to indicate an instrumented line
-#
-# for each entry in the .bbg file. Filenames are absolute, i.e. relative
-# filenames are prefixed with BASE_DIR.
-#
-# Die on error.
-#
-
-sub read_hammer_bbg_file($$)
-{
-	my $bbg_filename = $_[0];
-	my $base_dir = $_[1];
-	my %result;
-	my $filename;
-	my $function_name;
-	my $first_line;
-	my $lineno;
-	my $length;
-	my $value;
-	my $endianness;
-	my $blocks;
-	my $packed_word;
-	local *INPUT;
-
-	open(INPUT, $bbg_filename)
-		or die("ERROR: cannot read $bbg_filename!\n");
-
-	binmode(INPUT);
-	
-	# Read magic
-	read(INPUT, $packed_word, 4) == 4
-		or die("ERROR: invalid bbg file format\n");
-
-	$endianness = 1;
-
-	unpack_int32($packed_word, $endianness) == $BBG_FILE_MAGIC
-		or die("ERROR: bbg file magic does not match\n");
-
-	# Skip version
-	seek(INPUT, 4, 1);
-
-	# Read data in words of 4 bytes
-	while (read(INPUT, $packed_word, 4) == 4)
-	{
-		# Get record tag
-		$value = unpack_int32($packed_word, $endianness);
-
-		# Get record length
-		read(INPUT, $packed_word, 4);
-		$length = unpack_int32($packed_word, $endianness);
-
-		if ($value == $GCNO_FUNCTION_TAG)
-		{
-			# Get function name
-			($value, $function_name) =
-				read_hammer_bbg_string(*INPUT, $endianness);
-			$function_name =~ s/\W/_/g;
-			$filename = undef;
-			$first_line = undef;
-
-			seek(INPUT, $length - $value * 4, 1);
-		}
-		elsif ($value == $GCNO_LINES_TAG)
-		{
-			# Get linenumber and filename
-			# Skip block number
-			seek(INPUT, 4, 1);
-			$length -= 4;
-
-			while ($length > 0)
-			{
-				read(INPUT, $packed_word, 4);
-				$lineno = unpack_int32($packed_word,
-						       $endianness);
-				$length -= 4;
-				if ($lineno != 0)
-				{
-					if (!defined($first_line))
-					{
-						$first_line = $lineno;
-					}
-					if (defined($filename))
-					{
-						$result{$filename} .=
-							($result{$filename} ? "," : "").
-							"=$lineno";
-					}
-					else
-					{
-						warn("WARNING: unassigned line".
-						     " number in .bbg file ".
-						     "$bbg_filename\n");
-					}
-					next;
-				}
-				($blocks, $value) =
-					read_hammer_bbg_string(
-						*INPUT, $endianness);
-				# Add all filenames to result list
-				if (defined($value))
-				{
-					$value = solve_relative_path(
-							$base_dir, $value);
-					if (!defined($result{$value}))
-					{
-						$result{$value} = undef;
-					}
-					if (!defined($filename))
-					{
-						$filename = $value;
-					}
-				}
-				$length -= $blocks * 4;
-
-				# Got a complete data set?
-				if (defined($filename) &&
-				    defined($first_line) &&
-				    defined($function_name))
-				{
-					# Add it to our result hash
-					if (defined($result{$filename}))
-					{
-						$result{$filename} .=
-						",$function_name=$first_line";
-					}
-					else
-					{
-						$result{$filename} =
-						"$function_name=$first_line";
-					}
-					$function_name = undef;
-					$filename = undef;
-					$first_line = undef;
-				}
-			}
-		}
-		else
-		{
-			# Skip other records
-			seek(INPUT, $length, 1);
-		}
-	}
-	close(INPUT);
-
-	if (!scalar(keys(%result)))
-	{
-		die("ERROR: no data found in $bbg_filename!\n");
-	}
-	return %result;
-}
-
-
-#
-# read_hammer_bbg_string(handle, endianness);
-#
-# Read a string in 4-byte chunks from HANDLE.
-#
-# Return (number of 4-byte chunks read, string).
-#
-
-sub read_hammer_bbg_string(*$)
-{
-	my $handle		= $_[0];
-	my $endianness		= $_[1];
-	my $length		= 0;
-	my $string		= "";
-	my $packed_word;
-	my $pad;
-
-	read($handle, $packed_word, 4) == 4
-		or die("ERROR: reading string\n");
-
-	$length = unpack_int32($packed_word, $endianness);
-	$pad = 4 - $length % 4;
-
-	if ($length == 0)
-	{
-		return (1, undef);
-	}
-
-	read($handle, $string, $length) ==
-		$length or die("ERROR: reading string\n");
-	seek($handle, $pad, 1);
-
-	return(1 + ($length + $pad) / 4, $string);
-}
-
-#
-# unpack_int32(word, endianness)
-#
-# Interpret 4-byte binary string WORD as signed 32 bit integer in
-# endian encoding defined by ENDIANNESS (0=little, 1=big) and return its
-# value.
-#
-
-sub unpack_int32($$)
-{
-	return sprintf("%d", unpack($_[1] ? "N" : "V",$_[0]));
+	return(\@result, $branches, \@functions);
 }
 
 
@@ -1831,7 +1865,7 @@
 	my $version_string;
 	my $result;
 
-	open(GCOV_PIPE, "$gcov_tool -v |")
+	open(GCOV_PIPE, "-|", "$gcov_tool -v")
 		or die("ERROR: cannot retrieve gcov version!\n");
 	$version_string = <GCOV_PIPE>;
 	close(GCOV_PIPE);
@@ -1850,13 +1884,7 @@
 			$result = $1 << 16 | $2 << 8;
 		}
 	}
-        if ($version_string =~ /suse/i && $result == 0x30303 ||
-            $version_string =~ /mandrake/i && $result == 0x30302)
-	{
-		info("Using compatibility mode for GCC 3.3 (hammer)\n");
-		$compatibility = $COMPAT_HAMMER;
-	}
-	return $result;
+	return ($result, $version_string);
 }
 
 
@@ -1919,13 +1947,14 @@
 	local *OLD_STDOUT;
 
 	# Save old stdout and stderr handles
-	($mode & 1) && open(OLD_STDOUT, ">>&STDOUT");
-	($mode & 2) && open(OLD_STDERR, ">>&STDERR");
+	($mode & 1) && open(OLD_STDOUT, ">>&", "STDOUT");
+	($mode & 2) && open(OLD_STDERR, ">>&", "STDERR");
 
 	# Redirect to /dev/null
-	($mode & 1) && open(STDOUT, ">/dev/null");
-	($mode & 2) && open(STDERR, ">/dev/null");
+	($mode & 1) && open(STDOUT, ">", "/dev/null");
+	($mode & 2) && open(STDERR, ">", "/dev/null");
  
+	debug("system(".join(' ', @_).")\n");
 	system(@_);
 	$result = $?;
 
@@ -1934,8 +1963,8 @@
 	($mode & 2) && close(STDERR);
 
 	# Restore old handles
-	($mode & 1) && open(STDOUT, ">>&OLD_STDOUT");
-	($mode & 2) && open(STDERR, ">>&OLD_STDERR");
+	($mode & 1) && open(STDOUT, ">>&", "OLD_STDOUT");
+	($mode & 2) && open(STDERR, ">>&", "OLD_STDERR");
  
 	return $result;
 }
@@ -1956,7 +1985,7 @@
 	my $value;
 	local *HANDLE;
 
-	if (!open(HANDLE, "<$filename"))
+	if (!open(HANDLE, "<", $filename))
 	{
 		warn("WARNING: cannot read configuration file $filename\n");
 		return undef;
@@ -1995,8 +2024,8 @@
 #   key_string => var_ref
 #
 # where KEY_STRING is a keyword and VAR_REF is a reference to an associated
-# variable. If the global configuration hash CONFIG contains a value for
-# keyword KEY_STRING, VAR_REF will be assigned the value for that keyword. 
+# variable. If the global configuration hashes CONFIG or OPT_RC contain a value
+# for keyword KEY_STRING, VAR_REF will be assigned the value for that keyword. 
 #
 
 sub apply_config($)
@@ -2005,51 +2034,175 @@
 
 	foreach (keys(%{$ref}))
 	{
-		if (defined($config->{$_}))
-		{
+		if (defined($opt_rc{$_})) {
+			${$ref->{$_}} = $opt_rc{$_};
+		} elsif (defined($config->{$_})) {
 			${$ref->{$_}} = $config->{$_};
 		}
 	}
 }
 
 
-sub gen_initial_info($)
+#
+# get_exclusion_data(filename)
+#
+# Scan specified source code file for exclusion markers and return
+#   linenumber -> 1
+# for all lines which should be excluded.
+#
+
+sub get_exclusion_data($)
 {
-	my $directory = $_[0];
-	my @file_list;
+	my ($filename) = @_;
+	my %list;
+	my $flag = 0;
+	local *HANDLE;
 
-	if (-d $directory)
-	{
-		info("Scanning $directory for $graph_file_extension ".
-		     "files ...\n");	
-
-		@file_list = `find "$directory" $maxdepth $follow -name \\*$graph_file_extension -type f 2>/dev/null`;
-		chomp(@file_list);
-		@file_list or die("ERROR: no $graph_file_extension files ".
-				  "found in $directory!\n");
-		info("Found %d graph files in %s\n", $#file_list+1, $directory);
+	if (!open(HANDLE, "<", $filename)) {
+		warn("WARNING: could not open $filename\n");
+		return undef;
 	}
-	else
-	{
-		@file_list = ($directory);
+	while (<HANDLE>) {
+		if (/$EXCL_STOP/) {
+			$flag = 0;
+		} elsif (/$EXCL_START/) {
+			$flag = 1;
+		}
+		if (/$EXCL_LINE/ || $flag) {
+			$list{$.} = 1;
+		}
+	}
+	close(HANDLE);
+
+	if ($flag) {
+		warn("WARNING: unterminated exclusion section in $filename\n");
 	}
 
-	# Process all files in list
-	foreach (@file_list) { process_graphfile($_); }
+	return \%list;
 }
 
-sub process_graphfile($)
+
+#
+# apply_exclusion_data(instr, graph)
+#
+# Remove lines from instr and graph data structures which are marked
+# for exclusion in the source code file.
+#
+# Return adjusted (instr, graph).
+#
+# graph         : file name -> function data
+# function data : function name -> line data
+# line data     : [ line1, line2, ... ]
+#
+# instr     : filename -> line data
+# line data : [ line1, line2, ... ]
+#
+
+sub apply_exclusion_data($$)
 {
-	my $graph_filename = $_[0];
+	my ($instr, $graph) = @_;
+	my $filename;
+	my %excl_data;
+	my $excl_read_failed = 0;
+
+	# Collect exclusion marker data
+	foreach $filename (sort_uniq_lex(keys(%{$graph}), keys(%{$instr}))) {
+		my $excl = get_exclusion_data($filename);
+
+		# Skip and note if file could not be read
+		if (!defined($excl)) {
+			$excl_read_failed = 1;
+			next;
+		}
+
+		# Add to collection if there are markers
+		$excl_data{$filename} = $excl if (keys(%{$excl}) > 0);
+	}
+
+	# Warn if not all source files could be read
+	if ($excl_read_failed) {
+		warn("WARNING: some exclusion markers may be ignored\n");
+	}
+
+	# Skip if no markers were found
+	return ($instr, $graph) if (keys(%excl_data) == 0);
+
+	# Apply exclusion marker data to graph
+	foreach $filename (keys(%excl_data)) {
+		my $function_data = $graph->{$filename};
+		my $excl = $excl_data{$filename};
+		my $function;
+
+		next if (!defined($function_data));
+
+		foreach $function (keys(%{$function_data})) {
+			my $line_data = $function_data->{$function};
+			my $line;
+			my @new_data;
+
+			# To be consistent with exclusion parser in non-initial
+			# case we need to remove a function if the first line
+			# was excluded
+			if ($excl->{$line_data->[0]}) {
+				delete($function_data->{$function});
+				next;
+			}
+			# Copy only lines which are not excluded
+			foreach $line (@{$line_data}) {
+				push(@new_data, $line) if (!$excl->{$line});
+			}
+
+			# Store modified list
+			if (scalar(@new_data) > 0) {
+				$function_data->{$function} = \@new_data;
+			} else {
+				# All of this function was excluded
+				delete($function_data->{$function});
+			}
+		}
+
+		# Check if all functions of this file were excluded
+		if (keys(%{$function_data}) == 0) {
+			delete($graph->{$filename});
+		}
+	}
+
+	# Apply exclusion marker data to instr
+	foreach $filename (keys(%excl_data)) {
+		my $line_data = $instr->{$filename};
+		my $excl = $excl_data{$filename};
+		my $line;
+		my @new_data;
+
+		next if (!defined($line_data));
+
+		# Copy only lines which are not excluded
+		foreach $line (@{$line_data}) {
+			push(@new_data, $line) if (!$excl->{$line});
+		}
+
+		# Store modified list
+		$instr->{$filename} = \@new_data;
+	}
+
+	return ($instr, $graph);
+}
+
+
+sub process_graphfile($$)
+{
+	my ($file, $dir) = @_;
+	my $graph_filename = $file;
 	my $graph_dir;
 	my $graph_basename;
 	my $source_dir;
 	my $base_dir;
-	my %graph_data;
+	my $graph;
+	my $instr;
 	my $filename;
 	local *INFO_HANDLE;
 
-	info("Processing $_[0]\n");
+	info("Processing %s\n", abs2rel($file, $dir));
 
 	# Get path to data file in absolute and normalized form (begins with /,
 	# contains no more ../ or ./)
@@ -2058,11 +2211,10 @@
 	# Get directory and basename of data file
 	($graph_dir, $graph_basename) = split_filename($graph_filename);
 
-	# avoid files from .libs dirs 	 
-	if ($compat_libtool && $graph_dir =~ m/(.*)\/\.libs$/) {
-		$source_dir = $1;
-	} else {
-		$source_dir = $graph_dir;
+	$source_dir = $graph_dir;
+	if (is_compat($COMPAT_MODE_LIBTOOL)) {
+		# Avoid files from .libs dirs 	 
+		$source_dir =~ s/\.libs$//;
 	}
 
 	# Construct base_dir for current file
@@ -2077,19 +2229,30 @@
 
 	if ($gcov_version < $GCOV_VERSION_3_4_0)
 	{
-		if (defined($compatibility) && $compatibility eq $COMPAT_HAMMER)
+		if (is_compat($COMPAT_MODE_HAMMER))
 		{
-			%graph_data = read_hammer_bbg_file($graph_filename,
-							   $base_dir);
+			($instr, $graph) = read_bbg($graph_filename);
 		}
 		else
 		{
-			%graph_data = read_bb_file($graph_filename, $base_dir);
+			($instr, $graph) = read_bb($graph_filename);
 		}
 	} 
 	else
 	{
-		%graph_data = read_gcno_file($graph_filename, $base_dir);
+		($instr, $graph) = read_gcno($graph_filename);
+	}
+
+	# Try to find base directory automatically if requested by user
+	if ($rc_auto_base) {
+		$base_dir = find_base_from_graph($base_dir, $instr, $graph);
+	}
+
+	($instr, $graph) = adjust_graph_filenames($base_dir, $instr, $graph);
+
+	if (!$no_markers) {
+		# Apply exclusion marker data to graph file data
+		($instr, $graph) = apply_exclusion_data($instr, $graph);
 	}
 
 	# Check whether we're writing to a single file
@@ -2102,7 +2265,7 @@
 		else
 		{
 			# Append to output file
-			open(INFO_HANDLE, ">>$output_filename")
+			open(INFO_HANDLE, ">>", $output_filename)
 				or die("ERROR: cannot write to ".
 				       "$output_filename!\n");
 		}
@@ -2110,51 +2273,51 @@
 	else
 	{
 		# Open .info file for output
-		open(INFO_HANDLE, ">$graph_filename.info")
+		open(INFO_HANDLE, ">", "$graph_filename.info")
 			or die("ERROR: cannot create $graph_filename.info!\n");
 	}
 
 	# Write test name
 	printf(INFO_HANDLE "TN:%s\n", $test_name);
-	foreach $filename (keys(%graph_data))
+	foreach $filename (sort(keys(%{$instr})))
 	{
-		my %lines;
-		my $count = 0;
-		my @functions;
+		my $funcdata = $graph->{$filename};
+		my $line;
+		my $linedata;
 
 		print(INFO_HANDLE "SF:$filename\n");
 
-		# Write function related data
-		foreach (split(",",$graph_data{$filename}))
-		{
-			my ($fn, $line) = split("=", $_);
+		if (defined($funcdata) && $func_coverage) {
+			my @functions = sort {$funcdata->{$a}->[0] <=>
+					      $funcdata->{$b}->[0]}
+					     keys(%{$funcdata});
+			my $func;
 
-			if ($fn eq "")
-			{
-				$lines{$line} = "";
-				next;
+			# Gather list of instrumented lines and functions
+			foreach $func (@functions) {
+				$linedata = $funcdata->{$func};
+
+				# Print function name and starting line
+				print(INFO_HANDLE "FN:".$linedata->[0].
+				      ",".filter_fn_name($func)."\n");
 			}
-
-			# Normalize function name
-			$fn =~ s/\W/_/g;
-
-			print(INFO_HANDLE "FN:$line,$fn\n");
-			push(@functions, $fn);
+			# Print zero function coverage data
+			foreach $func (@functions) {
+				print(INFO_HANDLE "FNDA:0,".
+				      filter_fn_name($func)."\n");
+			}
+			# Print function summary
+			print(INFO_HANDLE "FNF:".scalar(@functions)."\n");
+			print(INFO_HANDLE "FNH:0\n");
 		}
-		foreach (@functions) {
-			print(INFO_HANDLE "FNDA:$_,0\n");
+		# Print zero line coverage data
+		foreach $line (@{$instr->{$filename}}) {
+			print(INFO_HANDLE "DA:$line,0\n");
 		}
-		print(INFO_HANDLE "FNF:".scalar(@functions)."\n");
-		print(INFO_HANDLE "FNH:0\n");
-
-		# Write line related data
-		foreach (sort {$a <=> $b } keys(%lines))
-		{
-			print(INFO_HANDLE "DA:$_,0\n");
-			$count++;
-		}
+		# Print line summary
+		print(INFO_HANDLE "LF:".scalar(@{$instr->{$filename}})."\n");
 		print(INFO_HANDLE "LH:0\n");
-		print(INFO_HANDLE "LF:$count\n");
+
 		print(INFO_HANDLE "end_of_record\n");
 	}
 	if (!($output_filename && ($output_filename eq "-")))
@@ -2163,6 +2326,16 @@
 	}
 }
 
+sub filter_fn_name($)
+{
+	my ($fn) = @_;
+
+	# Remove characters used internally as function name delimiters
+	$fn =~ s/[,=]/_/g;
+
+	return $fn;
+}
+
 sub warn_handler($)
 {
 	my ($msg) = @_;
@@ -2176,3 +2349,1309 @@
 
 	die("$tool_name: $msg");
 }
+
+
+#
+# graph_error(filename, message)
+#
+# Print message about error in graph file. If ignore_graph_error is set, return.
+# Otherwise abort.
+#
+
+sub graph_error($$)
+{
+	my ($filename, $msg) = @_;
+
+	if ($ignore[$ERROR_GRAPH]) {
+		warn("WARNING: $filename: $msg - skipping\n");
+		return;
+	}
+	die("ERROR: $filename: $msg\n");
+}
+
+#
+# graph_expect(description)
+#
+# If debug is set to a non-zero value, print the specified description of what
+# is expected to be read next from the graph file.
+#
+
+sub graph_expect($)
+{
+	my ($msg) = @_;
+
+	if (!$debug || !defined($msg)) {
+		return;
+	}
+
+	print(STDERR "DEBUG: expecting $msg\n");
+}
+
+#
+# graph_read(handle, bytes[, description, peek])
+#
+# Read and return the specified number of bytes from handle. Return undef
+# if the number of bytes could not be read. If PEEK is non-zero, reset
+# file position after read.
+#
+
+sub graph_read(*$;$$)
+{
+	my ($handle, $length, $desc, $peek) = @_;
+	my $data;
+	my $result;
+	my $pos;
+
+	graph_expect($desc);
+	if ($peek) {
+		$pos = tell($handle);
+		if ($pos == -1) {
+			warn("Could not get current file position: $!\n");
+			return undef;
+		}
+	}
+	$result = read($handle, $data, $length);
+	if ($debug) {
+		my $op = $peek ? "peek" : "read";
+		my $ascii = "";
+		my $hex = "";
+		my $i;
+
+		print(STDERR "DEBUG: $op($length)=$result: ");
+		for ($i = 0; $i < length($data); $i++) {
+			my $c = substr($data, $i, 1);;
+			my $n = ord($c);
+
+			$hex .= sprintf("%02x ", $n);
+			if ($n >= 32 && $n <= 127) {
+				$ascii .= $c;
+			} else {
+				$ascii .= ".";
+			}
+		}
+		print(STDERR "$hex |$ascii|");
+		print(STDERR "\n");
+	}
+	if ($peek) {
+		if (!seek($handle, $pos, 0)) {
+			warn("Could not set file position: $!\n");
+			return undef;
+		}
+	}
+	if ($result != $length) {
+		return undef;
+	}
+	return $data;
+}
+
+#
+# graph_skip(handle, bytes[, description])
+#
+# Read and discard the specified number of bytes from handle. Return non-zero
+# if bytes could be read, zero otherwise.
+#
+
+sub graph_skip(*$;$)
+{
+	my ($handle, $length, $desc) = @_;
+
+	if (defined(graph_read($handle, $length, $desc))) {
+		return 1;
+	}
+	return 0;
+}
+
+#
+# sort_uniq(list)
+#
+# Return list in numerically ascending order and without duplicate entries.
+#
+
+sub sort_uniq(@)
+{
+	my (@list) = @_;
+	my %hash;
+
+	foreach (@list) {
+		$hash{$_} = 1;
+	}
+	return sort { $a <=> $b } keys(%hash);
+}
+
+#
+# sort_uniq_lex(list)
+#
+# Return list in lexically ascending order and without duplicate entries.
+#
+
+sub sort_uniq_lex(@)
+{
+	my (@list) = @_;
+	my %hash;
+
+	foreach (@list) {
+		$hash{$_} = 1;
+	}
+	return sort keys(%hash);
+}
+
+#
+# parent_dir(dir)
+#
+# Return parent directory for DIR. DIR must not contain relative path
+# components.
+#
+
+sub parent_dir($)
+{
+	my ($dir) = @_;
+	my ($v, $d, $f) = splitpath($dir, 1);
+	my @dirs = splitdir($d);
+
+	pop(@dirs);
+
+	return catpath($v, catdir(@dirs), $f);
+}
+
+#
+# find_base_from_graph(base_dir, instr, graph)
+#
+# Try to determine the base directory of the graph file specified by INSTR
+# and GRAPH. The base directory is the base for all relative filenames in
+# the graph file. It is defined by the current working directory at time
+# of compiling the source file.
+#
+# This function implements a heuristic which relies on the following
+# assumptions:
+# - all files used for compilation are still present at their location
+# - the base directory is either BASE_DIR or one of its parent directories
+# - files by the same name are not present in multiple parent directories
+#
+
+sub find_base_from_graph($$$)
+{
+	my ($base_dir, $instr, $graph) = @_;
+	my $old_base;
+	my $best_miss;
+	my $best_base;
+	my %rel_files;
+
+	# Determine list of relative paths
+	foreach my $filename (keys(%{$instr}), keys(%{$graph})) {
+		next if (file_name_is_absolute($filename));
+
+		$rel_files{$filename} = 1;
+	}
+
+	# Early exit if there are no relative paths
+	return $base_dir if (!%rel_files);
+
+	do {
+		my $miss = 0;
+
+		foreach my $filename (keys(%rel_files)) {
+			if (!-e solve_relative_path($base_dir, $filename)) {
+				$miss++;
+			}
+		}
+
+		debug("base_dir=$base_dir miss=$miss\n");
+
+		# Exit if we find an exact match with no misses
+		return $base_dir if ($miss == 0);
+
+		# No exact match, aim for the one with the least source file
+		# misses
+		if (!defined($best_base) || $miss < $best_miss) {
+			$best_base = $base_dir;
+			$best_miss = $miss;
+		}
+
+		# Repeat until there's no more parent directory
+		$old_base = $base_dir;
+		$base_dir = parent_dir($base_dir);
+	} while ($old_base ne $base_dir);
+
+	return $best_base;
+}
+
+#
+# adjust_graph_filenames(base_dir, instr, graph)
+#
+# Make relative paths in INSTR and GRAPH absolute and apply
+# geninfo_adjust_src_path setting to graph file data.
+#
+
+sub adjust_graph_filenames($$$)
+{
+	my ($base_dir, $instr, $graph) = @_;
+
+	foreach my $filename (keys(%{$instr})) {
+		my $old_filename = $filename;
+
+		# Convert to absolute canonical form
+		$filename = solve_relative_path($base_dir, $filename);
+
+		# Apply adjustment
+		if (defined($adjust_src_pattern)) {
+			$filename =~ s/$adjust_src_pattern/$adjust_src_replace/g;
+		}
+
+		if ($filename ne $old_filename) {
+			$instr->{$filename} = delete($instr->{$old_filename});
+		}
+	}
+
+	foreach my $filename (keys(%{$graph})) {
+		my $old_filename = $filename;
+
+		# Make absolute
+		# Convert to absolute canonical form
+		$filename = solve_relative_path($base_dir, $filename);
+
+		# Apply adjustment
+		if (defined($adjust_src_pattern)) {
+			$filename =~ s/$adjust_src_pattern/$adjust_src_replace/g;
+		}
+
+		if ($filename ne $old_filename) {
+			$graph->{$filename} = delete($graph->{$old_filename});
+		}
+	}
+
+	return ($instr, $graph);
+}
+
+#
+# graph_cleanup(graph)
+#
+# Remove entries for functions with no lines. Remove duplicate line numbers.
+# Sort list of line numbers numerically ascending.
+#
+
+sub graph_cleanup($)
+{
+	my ($graph) = @_;
+	my $filename;
+
+	foreach $filename (keys(%{$graph})) {
+		my $per_file = $graph->{$filename};
+		my $function;
+
+		foreach $function (keys(%{$per_file})) {
+			my $lines = $per_file->{$function};
+
+			if (scalar(@$lines) == 0) {
+				# Remove empty function
+				delete($per_file->{$function});
+				next;
+			}
+			# Normalize list
+			$per_file->{$function} = [ sort_uniq(@$lines) ];
+		}
+		if (scalar(keys(%{$per_file})) == 0) {
+			# Remove empty file
+			delete($graph->{$filename});
+		}
+	}
+}
+
+#
+# graph_find_base(bb)
+#
+# Try to identify the filename which is the base source file for the
+# specified bb data.
+#
+
+sub graph_find_base($)
+{
+	my ($bb) = @_;
+	my %file_count;
+	my $basefile;
+	my $file;
+	my $func;
+	my $filedata;
+	my $count;
+	my $num;
+
+	# Identify base name for this bb data.
+	foreach $func (keys(%{$bb})) {
+		$filedata = $bb->{$func};
+
+		foreach $file (keys(%{$filedata})) {
+			$count = $file_count{$file};
+
+			# Count file occurrence
+			$file_count{$file} = defined($count) ? $count + 1 : 1;
+		}
+	}
+	$count = 0;
+	$num = 0;
+	foreach $file (keys(%file_count)) {
+		if ($file_count{$file} > $count) {
+			# The file that contains code for the most functions
+			# is likely the base file
+			$count = $file_count{$file};
+			$num = 1;
+			$basefile = $file;
+		} elsif ($file_count{$file} == $count) {
+			# If more than one file could be the basefile, we
+			# don't have a basefile
+			$basefile = undef;
+		}
+	}
+
+	return $basefile;
+}
+
+#
+# graph_from_bb(bb, fileorder, bb_filename)
+#
+# Convert data from bb to the graph format and list of instrumented lines.
+# Returns (instr, graph).
+#
+# bb         : function name -> file data
+#            : undef -> file order
+# file data  : filename -> line data
+# line data  : [ line1, line2, ... ]
+#
+# file order : function name -> [ filename1, filename2, ... ]
+#
+# graph         : file name -> function data
+# function data : function name -> line data
+# line data     : [ line1, line2, ... ]
+#
+# instr     : filename -> line data
+# line data : [ line1, line2, ... ]
+#
+
+sub graph_from_bb($$$)
+{
+	my ($bb, $fileorder, $bb_filename) = @_;
+	my $graph = {};
+	my $instr = {};
+	my $basefile;
+	my $file;
+	my $func;
+	my $filedata;
+	my $linedata;
+	my $order;
+
+	$basefile = graph_find_base($bb);
+	# Create graph structure
+	foreach $func (keys(%{$bb})) {
+		$filedata = $bb->{$func};
+		$order = $fileorder->{$func};
+
+		# Account for lines in functions
+		if (defined($basefile) && defined($filedata->{$basefile})) {
+			# If the basefile contributes to this function,
+			# account this function to the basefile.
+			$graph->{$basefile}->{$func} = $filedata->{$basefile};
+		} else {
+			# If the basefile does not contribute to this function,
+			# account this function to the first file contributing
+			# lines.
+			$graph->{$order->[0]}->{$func} =
+				$filedata->{$order->[0]};
+		}
+
+		foreach $file (keys(%{$filedata})) {
+			# Account for instrumented lines
+			$linedata = $filedata->{$file};
+			push(@{$instr->{$file}}, @$linedata);
+		}
+	}
+	# Clean up array of instrumented lines
+	foreach $file (keys(%{$instr})) {
+		$instr->{$file} = [ sort_uniq(@{$instr->{$file}}) ];
+	}
+
+	return ($instr, $graph);
+}
+
+#
+# graph_add_order(fileorder, function, filename)
+#
+# Add an entry for filename to the fileorder data set for function.
+#
+
+sub graph_add_order($$$)
+{
+	my ($fileorder, $function, $filename) = @_;
+	my $item;
+	my $list;
+
+	$list = $fileorder->{$function};
+	foreach $item (@$list) {
+		if ($item eq $filename) {
+			return;
+		}
+	}
+	push(@$list, $filename);
+	$fileorder->{$function} = $list;
+}
+
+#
+# read_bb_word(handle[, description])
+#
+# Read and return a word in .bb format from handle.
+#
+
+sub read_bb_word(*;$)
+{
+	my ($handle, $desc) = @_;
+
+	return graph_read($handle, 4, $desc);
+}
+
+#
+# read_bb_value(handle[, description])
+#
+# Read a word in .bb format from handle and return the word and its integer
+# value.
+#
+
+sub read_bb_value(*;$)
+{
+	my ($handle, $desc) = @_;
+	my $word;
+
+	$word = read_bb_word($handle, $desc);
+	return undef if (!defined($word));
+
+	return ($word, unpack("V", $word));
+}
+
+#
+# read_bb_string(handle, delimiter)
+#
+# Read and return a string in .bb format from handle up to the specified
+# delimiter value.
+#
+
+sub read_bb_string(*$)
+{
+	my ($handle, $delimiter) = @_;
+	my $word;
+	my $value;
+	my $string = "";
+
+	graph_expect("string");
+	do {
+		($word, $value) = read_bb_value($handle, "string or delimiter");
+		return undef if (!defined($value));
+		if ($value != $delimiter) {
+			$string .= $word;
+		}
+	} while ($value != $delimiter);
+	$string =~ s/\0//g;
+
+	return $string;
+}
+
+#
+# read_bb(filename)
+#
+# Read the contents of the specified .bb file and return (instr, graph), where:
+#
+#   instr     : filename -> line data
+#   line data : [ line1, line2, ... ]
+#
+#   graph     :     filename -> file_data
+#   file_data : function name -> line_data
+#   line_data : [ line1, line2, ... ]
+#
+# See the gcov info pages of gcc 2.95 for a description of the .bb file format.
+#
+
+sub read_bb($)
+{
+	my ($bb_filename) = @_;
+	my $minus_one = 0x80000001;
+	my $minus_two = 0x80000002;
+	my $value;
+	my $filename;
+	my $function;
+	my $bb = {};
+	my $fileorder = {};
+	my $instr;
+	my $graph;
+	local *HANDLE;
+
+	open(HANDLE, "<", $bb_filename) or goto open_error;
+	binmode(HANDLE);
+	while (!eof(HANDLE)) {
+		$value = read_bb_value(*HANDLE, "data word");
+		goto incomplete if (!defined($value));
+		if ($value == $minus_one) {
+			# Source file name
+			graph_expect("filename");
+			$filename = read_bb_string(*HANDLE, $minus_one);
+			goto incomplete if (!defined($filename));
+		} elsif ($value == $minus_two) {
+			# Function name
+			graph_expect("function name");
+			$function = read_bb_string(*HANDLE, $minus_two);
+			goto incomplete if (!defined($function));
+		} elsif ($value > 0) {
+			# Line number
+			if (!defined($filename) || !defined($function)) {
+				warn("WARNING: unassigned line number ".
+				     "$value\n");
+				next;
+			}
+			push(@{$bb->{$function}->{$filename}}, $value);
+			graph_add_order($fileorder, $function, $filename);
+		}
+	}
+	close(HANDLE);
+	($instr, $graph) = graph_from_bb($bb, $fileorder, $bb_filename);
+	graph_cleanup($graph);
+
+	return ($instr, $graph);
+
+open_error:
+	graph_error($bb_filename, "could not open file");
+	return undef;
+incomplete:
+	graph_error($bb_filename, "reached unexpected end of file");
+	return undef;
+}
+
+#
+# read_bbg_word(handle[, description])
+#
+# Read and return a word in .bbg format.
+#
+
+sub read_bbg_word(*;$)
+{
+	my ($handle, $desc) = @_;
+
+	return graph_read($handle, 4, $desc);
+}
+
+#
+# read_bbg_value(handle[, description])
+#
+# Read a word in .bbg format from handle and return its integer value.
+#
+
+sub read_bbg_value(*;$)
+{
+	my ($handle, $desc) = @_;
+	my $word;
+
+	$word = read_bbg_word($handle, $desc);
+	return undef if (!defined($word));
+
+	return unpack("N", $word);
+}
+
+#
+# read_bbg_string(handle)
+#
+# Read and return a string in .bbg format.
+#
+
+sub read_bbg_string(*)
+{
+	my ($handle, $desc) = @_;
+	my $length;
+	my $string;
+
+	graph_expect("string");
+	# Read string length
+	$length = read_bbg_value($handle, "string length");
+	return undef if (!defined($length));
+	if ($length == 0) {
+		return "";
+	}
+	# Read string
+	$string = graph_read($handle, $length, "string");
+	return undef if (!defined($string));
+	# Skip padding
+	graph_skip($handle, 4 - $length % 4, "string padding") or return undef;
+
+	return $string;
+}
+
+#
+# read_bbg_lines_record(handle, bbg_filename, bb, fileorder, filename,
+#                       function)
+#
+# Read a bbg format lines record from handle and add the relevant data to
+# bb and fileorder. Return filename on success, undef on error.
+#
+
+sub read_bbg_lines_record(*$$$$$)
+{
+	my ($handle, $bbg_filename, $bb, $fileorder, $filename, $function) = @_;
+	my $string;
+	my $lineno;
+
+	graph_expect("lines record");
+	# Skip basic block index
+	graph_skip($handle, 4, "basic block index") or return undef;
+	while (1) {
+		# Read line number
+		$lineno = read_bbg_value($handle, "line number");
+		return undef if (!defined($lineno));
+		if ($lineno == 0) {
+			# Got a marker for a new filename
+			graph_expect("filename");
+			$string = read_bbg_string($handle);
+			return undef if (!defined($string));
+			# Check for end of record
+			if ($string eq "") {
+				return $filename;
+			}
+			$filename = $string;
+			if (!exists($bb->{$function}->{$filename})) {
+				$bb->{$function}->{$filename} = [];
+			}
+			next;
+		}
+		# Got an actual line number
+		if (!defined($filename)) {
+			warn("WARNING: unassigned line number in ".
+			     "$bbg_filename\n");
+			next;
+		}
+		push(@{$bb->{$function}->{$filename}}, $lineno);
+		graph_add_order($fileorder, $function, $filename);
+	}
+}
+
+#
+# read_bbg(filename)
+#
+# Read the contents of the specified .bbg file and return the following mapping:
+#   graph:     filename -> file_data
+#   file_data: function name -> line_data
+#   line_data: [ line1, line2, ... ]
+#
+# See the gcov-io.h file in the SLES 9 gcc 3.3.3 source code for a description
+# of the .bbg format.
+#
+
+sub read_bbg($)
+{
+	my ($bbg_filename) = @_;
+	my $file_magic = 0x67626267;
+	my $tag_function = 0x01000000;
+	my $tag_lines = 0x01450000;
+	my $word;
+	my $tag;
+	my $length;
+	my $function;
+	my $filename;
+	my $bb = {};
+	my $fileorder = {};
+	my $instr;
+	my $graph;
+	local *HANDLE;
+
+	open(HANDLE, "<", $bbg_filename) or goto open_error;
+	binmode(HANDLE);
+	# Read magic
+	$word = read_bbg_value(*HANDLE, "file magic");
+	goto incomplete if (!defined($word));
+	# Check magic
+	if ($word != $file_magic) {
+		goto magic_error;
+	}
+	# Skip version
+	graph_skip(*HANDLE, 4, "version") or goto incomplete;
+	while (!eof(HANDLE)) {
+		# Read record tag
+		$tag = read_bbg_value(*HANDLE, "record tag");
+		goto incomplete if (!defined($tag));
+		# Read record length
+		$length = read_bbg_value(*HANDLE, "record length");
+		goto incomplete if (!defined($tag));
+		if ($tag == $tag_function) {
+			graph_expect("function record");
+			# Read function name
+			graph_expect("function name");
+			$function = read_bbg_string(*HANDLE);
+			goto incomplete if (!defined($function));
+			$filename = undef;
+			# Skip function checksum
+			graph_skip(*HANDLE, 4, "function checksum")
+				or goto incomplete;
+		} elsif ($tag == $tag_lines) {
+			# Read lines record
+			$filename = read_bbg_lines_record(HANDLE, $bbg_filename,
+					  $bb, $fileorder, $filename,
+					  $function);
+			goto incomplete if (!defined($filename));
+		} else {
+			# Skip record contents
+			graph_skip(*HANDLE, $length, "unhandled record")
+				or goto incomplete;
+		}
+	}
+	close(HANDLE);
+	($instr, $graph) = graph_from_bb($bb, $fileorder, $bbg_filename);
+	graph_cleanup($graph);
+
+	return ($instr, $graph);
+
+open_error:
+	graph_error($bbg_filename, "could not open file");
+	return undef;
+incomplete:
+	graph_error($bbg_filename, "reached unexpected end of file");
+	return undef;
+magic_error:
+	graph_error($bbg_filename, "found unrecognized bbg file magic");
+	return undef;
+}
+
+#
+# read_gcno_word(handle[, description, peek])
+#
+# Read and return a word in .gcno format.
+#
+
+sub read_gcno_word(*;$$)
+{
+	my ($handle, $desc, $peek) = @_;
+
+	return graph_read($handle, 4, $desc, $peek);
+}
+
+#
+# read_gcno_value(handle, big_endian[, description, peek])
+#
+# Read a word in .gcno format from handle and return its integer value
+# according to the specified endianness. If PEEK is non-zero, reset file
+# position after read.
+#
+
+sub read_gcno_value(*$;$$)
+{
+	my ($handle, $big_endian, $desc, $peek) = @_;
+	my $word;
+	my $pos;
+
+	$word = read_gcno_word($handle, $desc, $peek);
+	return undef if (!defined($word));
+	if ($big_endian) {
+		return unpack("N", $word);
+	} else {
+		return unpack("V", $word);
+	}
+}
+
+#
+# read_gcno_string(handle, big_endian)
+#
+# Read and return a string in .gcno format.
+#
+
+sub read_gcno_string(*$)
+{
+	my ($handle, $big_endian) = @_;
+	my $length;
+	my $string;
+
+	graph_expect("string");
+	# Read string length
+	$length = read_gcno_value($handle, $big_endian, "string length");
+	return undef if (!defined($length));
+	if ($length == 0) {
+		return "";
+	}
+	$length *= 4;
+	# Read string
+	$string = graph_read($handle, $length, "string and padding");
+	return undef if (!defined($string));
+	$string =~ s/\0//g;
+
+	return $string;
+}
+
+#
+# read_gcno_lines_record(handle, gcno_filename, bb, fileorder, filename,
+#                        function, big_endian)
+#
+# Read a gcno format lines record from handle and add the relevant data to
+# bb and fileorder. Return filename on success, undef on error.
+#
+
+sub read_gcno_lines_record(*$$$$$$)
+{
+	my ($handle, $gcno_filename, $bb, $fileorder, $filename, $function,
+	    $big_endian) = @_;
+	my $string;
+	my $lineno;
+
+	graph_expect("lines record");
+	# Skip basic block index
+	graph_skip($handle, 4, "basic block index") or return undef;
+	while (1) {
+		# Read line number
+		$lineno = read_gcno_value($handle, $big_endian, "line number");
+		return undef if (!defined($lineno));
+		if ($lineno == 0) {
+			# Got a marker for a new filename
+			graph_expect("filename");
+			$string = read_gcno_string($handle, $big_endian);
+			return undef if (!defined($string));
+			# Check for end of record
+			if ($string eq "") {
+				return $filename;
+			}
+			$filename = $string;
+			if (!exists($bb->{$function}->{$filename})) {
+				$bb->{$function}->{$filename} = [];
+			}
+			next;
+		}
+		# Got an actual line number
+		if (!defined($filename)) {
+			warn("WARNING: unassigned line number in ".
+			     "$gcno_filename\n");
+			next;
+		}
+		# Add to list
+		push(@{$bb->{$function}->{$filename}}, $lineno);
+		graph_add_order($fileorder, $function, $filename);
+	}
+}
+
+#
+# determine_gcno_split_crc(handle, big_endian, rec_length)
+#
+# Determine if HANDLE refers to a .gcno file with a split checksum function
+# record format. Return non-zero in case of split checksum format, zero
+# otherwise, undef in case of read error.
+#
+
+sub determine_gcno_split_crc($$$)
+{
+	my ($handle, $big_endian, $rec_length) = @_;
+	my $strlen;
+	my $overlong_string;
+
+	return 1 if ($gcov_version >= $GCOV_VERSION_4_7_0);
+	return 1 if (is_compat($COMPAT_MODE_SPLIT_CRC));
+
+	# Heuristic:
+	# Decide format based on contents of next word in record:
+	# - pre-gcc 4.7
+	#   This is the function name length / 4 which should be
+	#   less than the remaining record length
+	# - gcc 4.7
+	#   This is a checksum, likely with high-order bits set,
+	#   resulting in a large number
+	$strlen = read_gcno_value($handle, $big_endian, undef, 1);
+	return undef if (!defined($strlen));
+	$overlong_string = 1 if ($strlen * 4 >= $rec_length - 12);
+
+	if ($overlong_string) {
+		if (is_compat_auto($COMPAT_MODE_SPLIT_CRC)) {
+			info("Auto-detected compatibility mode for split ".
+			     "checksum .gcno file format\n");
+
+			return 1;
+		} else {
+			# Sanity check
+			warn("Found overlong string in function record: ".
+			     "try '--compat split_crc'\n");
+		}
+	}
+
+	return 0;
+}
+
+#
+# read_gcno_function_record(handle, graph, big_endian, rec_length)
+#
+# Read a gcno format function record from handle and add the relevant data
+# to graph. Return (filename, function) on success, undef on error. 
+#
+
+sub read_gcno_function_record(*$$$$)
+{
+	my ($handle, $bb, $fileorder, $big_endian, $rec_length) = @_;
+	my $filename;
+	my $function;
+	my $lineno;
+	my $lines;
+
+	graph_expect("function record");
+	# Skip ident and checksum
+	graph_skip($handle, 8, "function ident and checksum") or return undef;
+	# Determine if this is a function record with split checksums
+	if (!defined($gcno_split_crc)) {
+		$gcno_split_crc = determine_gcno_split_crc($handle, $big_endian,
+							   $rec_length);
+		return undef if (!defined($gcno_split_crc));
+	}
+	# Skip cfg checksum word in case of split checksums
+	graph_skip($handle, 4, "function cfg checksum") if ($gcno_split_crc);
+	# Read function name
+	graph_expect("function name");
+	$function = read_gcno_string($handle, $big_endian);
+	return undef if (!defined($function));
+	# Read filename
+	graph_expect("filename");
+	$filename = read_gcno_string($handle, $big_endian);
+	return undef if (!defined($filename));
+	# Read first line number
+	$lineno = read_gcno_value($handle, $big_endian, "initial line number");
+	return undef if (!defined($lineno));
+	# Add to list
+	push(@{$bb->{$function}->{$filename}}, $lineno);
+	graph_add_order($fileorder, $function, $filename);
+
+	return ($filename, $function);
+}
+
+#
+# read_gcno(filename)
+#
+# Read the contents of the specified .gcno file and return the following
+# mapping:
+#   graph:    filename -> file_data
+#   file_data: function name -> line_data
+#   line_data: [ line1, line2, ... ]
+#
+# See the gcov-io.h file in the gcc 3.3 source code for a description of
+# the .gcno format.
+#
+
+sub read_gcno($)
+{
+	my ($gcno_filename) = @_;
+	my $file_magic = 0x67636e6f;
+	my $tag_function = 0x01000000;
+	my $tag_lines = 0x01450000;
+	my $big_endian;
+	my $word;
+	my $tag;
+	my $length;
+	my $filename;
+	my $function;
+	my $bb = {};
+	my $fileorder = {};
+	my $instr;
+	my $graph;
+	local *HANDLE;
+
+	open(HANDLE, "<", $gcno_filename) or goto open_error;
+	binmode(HANDLE);
+	# Read magic
+	$word = read_gcno_word(*HANDLE, "file magic");
+	goto incomplete if (!defined($word));
+	# Determine file endianness
+	if (unpack("N", $word) == $file_magic) {
+		$big_endian = 1;
+	} elsif (unpack("V", $word) == $file_magic) {
+		$big_endian = 0;
+	} else {
+		goto magic_error;
+	}
+	# Skip version and stamp
+	graph_skip(*HANDLE, 8, "version and stamp") or goto incomplete;
+	while (!eof(HANDLE)) {
+		my $next_pos;
+		my $curr_pos;
+
+		# Read record tag
+		$tag = read_gcno_value(*HANDLE, $big_endian, "record tag");
+		goto incomplete if (!defined($tag));
+		# Read record length
+		$length = read_gcno_value(*HANDLE, $big_endian,
+					  "record length");
+		goto incomplete if (!defined($length));
+		# Convert length to bytes
+		$length *= 4;
+		# Calculate start of next record
+		$next_pos = tell(HANDLE);
+		goto tell_error if ($next_pos == -1);
+		$next_pos += $length;
+		# Process record
+		if ($tag == $tag_function) {
+			($filename, $function) = read_gcno_function_record(
+				*HANDLE, $bb, $fileorder, $big_endian,
+				$length);
+			goto incomplete if (!defined($function));
+		} elsif ($tag == $tag_lines) {
+			# Read lines record
+			$filename = read_gcno_lines_record(*HANDLE,
+					$gcno_filename, $bb, $fileorder,
+					$filename, $function,
+					$big_endian);
+			goto incomplete if (!defined($filename));
+		} else {
+			# Skip record contents
+			graph_skip(*HANDLE, $length, "unhandled record")
+				or goto incomplete;
+		}
+		# Ensure that we are at the start of the next record
+		$curr_pos = tell(HANDLE);
+		goto tell_error if ($curr_pos == -1);
+		next if ($curr_pos == $next_pos);
+		goto record_error if ($curr_pos > $next_pos);
+		graph_skip(*HANDLE, $next_pos - $curr_pos,
+			   "unhandled record content")
+			or goto incomplete;
+	}
+	close(HANDLE);
+	($instr, $graph) = graph_from_bb($bb, $fileorder, $gcno_filename);
+	graph_cleanup($graph);
+
+	return ($instr, $graph);
+
+open_error:
+	graph_error($gcno_filename, "could not open file");
+	return undef;
+incomplete:
+	graph_error($gcno_filename, "reached unexpected end of file");
+	return undef;
+magic_error:
+	graph_error($gcno_filename, "found unrecognized gcno file magic");
+	return undef;
+tell_error:
+	graph_error($gcno_filename, "could not determine file position");
+	return undef;
+record_error:
+	graph_error($gcno_filename, "found unrecognized record format");
+	return undef;
+}
+
+sub debug($)
+{
+	my ($msg) = @_;
+
+	return if (!$debug);
+	print(STDERR "DEBUG: $msg");
+}
+
+#
+# get_gcov_capabilities
+#
+# Determine the list of available gcov options.
+#
+
+sub get_gcov_capabilities()
+{
+	my $help = `$gcov_tool --help`;
+	my %capabilities;
+
+	foreach (split(/\n/, $help)) {
+		next if (!/--(\S+)/);
+		next if ($1 eq 'help');
+		next if ($1 eq 'version');
+		next if ($1 eq 'object-directory');
+
+		$capabilities{$1} = 1;
+		debug("gcov has capability '$1'\n");
+	}
+
+	return \%capabilities;
+}
+
+#
+# parse_ignore_errors(@ignore_errors)
+#
+# Parse user input about which errors to ignore.
+#
+
+sub parse_ignore_errors(@)
+{
+	my (@ignore_errors) = @_;
+	my @items;
+	my $item;
+
+	return if (!@ignore_errors);
+
+	foreach $item (@ignore_errors) {
+		$item =~ s/\s//g;
+		if ($item =~ /,/) {
+			# Split and add comma-separated parameters
+			push(@items, split(/,/, $item));
+		} else {
+			# Add single parameter
+			push(@items, $item);
+		}
+	}
+	foreach $item (@items) {
+		my $item_id = $ERROR_ID{lc($item)};
+
+		if (!defined($item_id)) {
+			die("ERROR: unknown argument for --ignore-errors: ".
+			    "$item\n");
+		}
+		$ignore[$item_id] = 1;
+	}
+}
+
+#
+# is_external(filename)
+#
+# Determine if a file is located outside of the specified data directories.
+#
+
+sub is_external($)
+{
+	my ($filename) = @_;
+	my $dir;
+
+	foreach $dir (@internal_dirs) {
+		return 0 if ($filename =~ /^\Q$dir\/\E/);
+	}
+	return 1;
+}
+
+#
+# compat_name(mode)
+#
+# Return the name of compatibility mode MODE.
+#
+
+sub compat_name($)
+{
+	my ($mode) = @_;
+	my $name = $COMPAT_MODE_TO_NAME{$mode};
+
+	return $name if (defined($name));
+
+	return "<unknown>";
+}
+
+#
+# parse_compat_modes(opt)
+#
+# Determine compatibility mode settings.
+#
+
+sub parse_compat_modes($)
+{
+	my ($opt) = @_;
+	my @opt_list;
+	my %specified;
+
+	# Initialize with defaults
+	%compat_value = %COMPAT_MODE_DEFAULTS;
+
+	# Add old style specifications
+	if (defined($opt_compat_libtool)) {
+		$compat_value{$COMPAT_MODE_LIBTOOL} =
+			$opt_compat_libtool ? $COMPAT_VALUE_ON
+					    : $COMPAT_VALUE_OFF;
+	}
+
+	# Parse settings
+	if (defined($opt)) {
+		@opt_list = split(/\s*,\s*/, $opt);
+	}
+	foreach my $directive (@opt_list) {
+		my ($mode, $value);
+
+		# Either
+		#   mode=off|on|auto or
+		#   mode (implies on)
+		if ($directive !~ /^(\w+)=(\w+)$/ &&
+		    $directive !~ /^(\w+)$/) {
+			die("ERROR: Unknown compatibility mode specification: ".
+			    "$directive!\n");
+		}
+		# Determine mode
+		$mode = $COMPAT_NAME_TO_MODE{lc($1)};
+		if (!defined($mode)) {
+			die("ERROR: Unknown compatibility mode '$1'!\n");
+		}
+		$specified{$mode} = 1;
+		# Determine value
+		if (defined($2)) {
+			$value = $COMPAT_NAME_TO_VALUE{lc($2)};
+			if (!defined($value)) {
+				die("ERROR: Unknown compatibility mode ".
+				    "value '$2'!\n");
+			}
+		} else {
+			$value = $COMPAT_VALUE_ON;
+		}
+		$compat_value{$mode} = $value;
+	}
+	# Perform auto-detection
+	foreach my $mode (sort(keys(%compat_value))) {
+		my $value = $compat_value{$mode};
+		my $is_autodetect = "";
+		my $name = compat_name($mode);
+
+		if ($value == $COMPAT_VALUE_AUTO) {
+			my $autodetect = $COMPAT_MODE_AUTO{$mode};
+
+			if (!defined($autodetect)) {
+				die("ERROR: No auto-detection for ".
+				    "mode '$name' available!\n");
+			}
+
+			if (ref($autodetect) eq "CODE") {
+				$value = &$autodetect();
+				$compat_value{$mode} = $value;
+				$is_autodetect = " (auto-detected)";
+			}
+		}
+
+		if ($specified{$mode}) {
+			if ($value == $COMPAT_VALUE_ON) {
+				info("Enabling compatibility mode ".
+				     "'$name'$is_autodetect\n");
+			} elsif ($value == $COMPAT_VALUE_OFF) {
+				info("Disabling compatibility mode ".
+				     "'$name'$is_autodetect\n");
+			} else {
+				info("Using delayed auto-detection for ".
+				     "compatibility mode ".
+				     "'$name'\n");
+			}
+		}
+	}
+}
+
+sub compat_hammer_autodetect()
+{
+        if ($gcov_version_string =~ /suse/i && $gcov_version == 0x30303 ||
+            $gcov_version_string =~ /mandrake/i && $gcov_version == 0x30302)
+	{
+		info("Auto-detected compatibility mode for GCC 3.3 (hammer)\n");
+		return $COMPAT_VALUE_ON;
+	}
+	return $COMPAT_VALUE_OFF;
+}
+
+#
+# is_compat(mode)
+#
+# Return non-zero if compatibility mode MODE is enabled.
+#
+
+sub is_compat($)
+{
+	my ($mode) = @_;
+
+	return 1 if ($compat_value{$mode} == $COMPAT_VALUE_ON);
+	return 0;
+}
+
+#
+# is_compat_auto(mode)
+#
+# Return non-zero if compatibility mode MODE is set to auto-detect.
+#
+
+sub is_compat_auto($)
+{
+	my ($mode) = @_;
+
+	return 1 if ($compat_value{$mode} == $COMPAT_VALUE_AUTO);
+	return 0;
+}
diff --git a/bin/genpng b/bin/genpng
index b4d90c2..5f3084c 100755
--- a/bin/genpng
+++ b/bin/genpng
@@ -22,7 +22,7 @@
 #   This script creates an overview PNG image of a source code file by
 #   representing each source code character by a single pixel.
 #
-#   Note that the PERL module GD.pm is required for this script to work.
+#   Note that the Perl module GD.pm is required for this script to work.
 #   It may be obtained from http://www.cpan.org
 #
 # History:
@@ -35,7 +35,7 @@
 
 
 # Constants
-our $lcov_version	= "LCOV version 1.7";
+our $lcov_version	= 'LCOV version 1.10';
 our $lcov_url		= "http://ltp.sourceforge.net/coverage/lcov.php";
 our $tool_name		= basename($0);
 
@@ -45,14 +45,17 @@
 sub check_and_load_module($);
 sub genpng_print_usage(*);
 sub genpng_process_file($$$$);
-sub warn_handler($);
-sub die_handler($);
+sub genpng_warn_handler($);
+sub genpng_die_handler($);
 
 
 #
 # Code entry point
 #
 
+# Prettify version string
+$lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
+
 # Check whether required module GD.pm is installed
 if (check_and_load_module("GD"))
 {
@@ -75,8 +78,8 @@
 	my $help;
 	my $version;
 
-	$SIG{__WARN__} = \&warn_handler;
-	$SIG{__DIE__} = \&die_handler;
+	$SIG{__WARN__} = \&genpng_warn_handler;
+	$SIG{__DIE__} = \&genpng_die_handler;
 
 	# Parse command line options
 	if (!GetOptions("tab-size=i" => \$tab_size,
@@ -179,7 +182,7 @@
 	local *HANDLE;
 	my @source;
 
-	open(HANDLE, "<$filename")
+	open(HANDLE, "<", $filename)
 		or die("ERROR: cannot open $filename!\n");
 
 	# Check for .gcov filename extension
@@ -235,7 +238,7 @@
 	my $overview_width = shift(@_);	# Imagewidth for image
 	my $tab_size = shift(@_);	# Replacement string for tab signs
 	my @source = @_;	# Source code as passed via argument 2
-	my $height = scalar(@source);	# Height as define by source size
+	my $height;		# Height as define by source size
 	my $overview;		# Source code overview image data
 	my $col_plain_back;	# Color for overview background
 	my $col_plain_text;	# Color for uninstrumented text
@@ -258,6 +261,11 @@
 	my $replacement;	# Replacement string for tabulator chars
 	local *PNG_HANDLE;	# Handle for output PNG file
 
+	# Handle empty source files
+	if (!@source) {
+		@source = ( "" );
+	}
+	$height = scalar(@source);
 	# Create image
 	$overview = new GD::Image($overview_width, $height)
 		or die("ERROR: cannot allocate overview image!\n");
@@ -359,21 +367,21 @@
 	}
 
 	# Write PNG file
-	open (PNG_HANDLE, ">$filename")
+	open (PNG_HANDLE, ">", $filename)
 		or die("ERROR: cannot write png file $filename!\n");
 	binmode(*PNG_HANDLE);
 	print(PNG_HANDLE $overview->png());
 	close(PNG_HANDLE);
 }
 
-sub warn_handler($)
+sub genpng_warn_handler($)
 {
 	my ($msg) = @_;
 
 	warn("$tool_name: $msg");
 }
 
-sub die_handler($)
+sub genpng_die_handler($)
 {
 	my ($msg) = @_;
 
diff --git a/bin/install.sh b/bin/install.sh
index 9d4a9da..27140f9 100755
--- a/bin/install.sh
+++ b/bin/install.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 #
-# install.sh [--uninstall] sourcefile targetfile
+# install.sh [--uninstall] sourcefile targetfile [install options]
 #
 
 
@@ -9,15 +9,17 @@
   UNINSTALL=true
   SOURCE=$2
   TARGET=$3
+  shift 3
 else
   UNINSTALL=false
   SOURCE=$1
   TARGET=$2
+  shift 2
 fi
 
 # Check usage
 if test -z "$SOURCE" || test -z "$TARGET" ; then
-  echo Usage: install.sh [--uninstall] source target >&2
+  echo Usage: install.sh [--uninstall] source target [install options] >&2
   exit 1
 fi
 
@@ -30,8 +32,9 @@
 {
   local SOURCE=$1
   local TARGET=$2
+  local PARAMS=$3
 
-  install -D $SOURCE $TARGET
+  install -p -D $PARAMS $SOURCE $TARGET
 }
 
 
@@ -62,7 +65,7 @@
 if $UNINSTALL ; then
   do_uninstall $SOURCE $TARGET
 else
-  do_install $SOURCE $TARGET
+  do_install $SOURCE $TARGET "$*"
 fi
 
 exit 0
diff --git a/bin/lcov b/bin/lcov
index 6304d75..1a7f525 100755
--- a/bin/lcov
+++ b/bin/lcov
@@ -1,6 +1,6 @@
 #!/usr/bin/perl -w
 #
-#   Copyright (c) International Business Machines  Corp., 2002,2007
+#   Copyright (c) International Business Machines  Corp., 2002,2012
 #
 #   This program is free software;  you can redistribute it and/or modify
 #   it under the terms of the GNU General Public License as published by
@@ -60,36 +60,43 @@
 #
 
 use strict;
-use File::Basename; 
+use File::Basename;
+use File::Path;
+use File::Find;
+use File::Temp qw /tempdir/;
+use File::Spec::Functions qw /abs2rel canonpath catdir catfile catpath
+			      file_name_is_absolute rootdir splitdir splitpath/;
 use Getopt::Long;
+use Cwd qw /abs_path getcwd/;
 
 
 # Global constants
-our $lcov_version	= "LCOV version 1.7";
+our $lcov_version	= 'LCOV version 1.10';
 our $lcov_url		= "http://ltp.sourceforge.net/coverage/lcov.php";
 our $tool_name		= basename($0);
 
-# Names of the GCOV kernel module
-our @gcovmod = ("gcov-prof", "gcov-proc");
-
 # Directory containing gcov kernel files
-our $gcov_dir = "/proc/gcov";
-
-# The location of the insmod tool
-our $insmod_tool	= "/sbin/insmod";
-
-# The location of the modprobe tool
-our $modprobe_tool	= "/sbin/modprobe";
-
-# The location of the rmmod tool
-our $rmmod_tool		= "/sbin/rmmod";
+our $gcov_dir;
 
 # Where to create temporary directories
-our $tmp_dir		= "/tmp";
+our $tmp_dir;
 
-# How to prefix a temporary directory name
-our $tmp_prefix		= "tmpdir";
+# Internal constants
+our $GKV_PROC = 0;	# gcov-kernel data in /proc via external patch
+our $GKV_SYS = 1;	# gcov-kernel data in /sys via vanilla 2.6.31+
+our @GKV_NAME = ( "external", "upstream" );
+our $pkg_gkv_file = ".gcov_kernel_version";
+our $pkg_build_file = ".build_directory";
 
+our $BR_BLOCK		= 0;
+our $BR_BRANCH		= 1;
+our $BR_TAKEN		= 2;
+our $BR_VEC_ENTRIES	= 3;
+our $BR_VEC_WIDTH	= 32;
+
+# Branch data combination types
+our $BR_SUB = 0;
+our $BR_ADD = 1;
 
 # Prototypes
 sub print_usage(*);
@@ -98,10 +105,12 @@
 sub userspace_capture();
 sub kernel_reset();
 sub kernel_capture();
+sub kernel_capture_initial();
+sub package_capture();
 sub add_traces();
 sub read_info_file($);
 sub get_info_entry($);
-sub set_info_entry($$$$$$$;$$$$);
+sub set_info_entry($$$$$$$$$;$$$$$$);
 sub add_counts($$);
 sub merge_checksums($$$);
 sub combine_info_entries($$$);
@@ -117,13 +126,21 @@
 sub read_config($);
 sub apply_config($);
 sub info(@);
-sub unload_module($);
-sub check_and_load_kernel_module();
 sub create_temp_dir();
 sub transform_pattern($);
 sub warn_handler($);
 sub die_handler($);
-
+sub abort_handler($);
+sub temp_cleanup();
+sub setup_gkv();
+sub get_overall_line($$$$);
+sub print_overall_rate($$$$$$$$$);
+sub lcov_geninfo(@);
+sub create_package($$$;$);
+sub get_func_found_and_hit($);
+sub br_ivec_get($$);
+sub summary();
+sub rate($$;$$$);
 
 # Global variables & initialization
 our @directory;		# Specifies where to get coverage data from
@@ -142,7 +159,6 @@
 our $version;		# Version option flag
 our $convert_filenames;	# If set, convert filenames when applying diff
 our $strip;		# If set, strip leading directories when applying diff
-our $need_unload;	# If set, unload gcov kernel module
 our $temp_dir_name;	# Name of temporary directory
 our $cwd = `pwd`;	# Current working directory
 our $to_file;		# If set, indicates that output is written to a file
@@ -154,13 +170,38 @@
 our $compat_libtool;	# If set, indicates that libtool mode is to be enabled
 our $no_compat_libtool;	# If set, indicates that libtool mode is to be disabled
 our $gcov_tool;
-our $ignore_errors;
+our @opt_ignore_errors;
 our $initial;
 our $no_recursion = 0;
+our $to_package;
+our $from_package;
 our $maxdepth;
+our $no_markers;
 our $config;		# Configuration file contents
 chomp($cwd);
 our $tool_dir = dirname($0);	# Directory where genhtml tool is installed
+our @temp_dirs;
+our $gcov_gkv;		# gcov kernel support version found on machine
+our $opt_derive_func_data;
+our $opt_debug;
+our $opt_list_full_path;
+our $opt_no_list_full_path;
+our $opt_list_width = 80;
+our $opt_list_truncate_max = 20;
+our $opt_external;
+our $opt_no_external;
+our $opt_config_file;
+our %opt_rc;
+our @opt_summary;
+our $opt_compat;
+our $ln_overall_found;
+our $ln_overall_hit;
+our $fn_overall_found;
+our $fn_overall_hit;
+our $br_overall_found;
+our $br_overall_hit;
+our $func_coverage = 1;
+our $br_coverage = 0;
 
 
 #
@@ -169,6 +210,11 @@
 
 $SIG{__WARN__} = \&warn_handler;
 $SIG{__DIE__} = \&die_handler;
+$SIG{'INT'} = \&abort_handler;
+$SIG{'QUIT'} = \&abort_handler;
+
+# Prettify version string
+$lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
 
 # Add current working directory if $tool_dir is not already an absolute path
 if (! ($tool_dir =~ /^\/(.*)$/))
@@ -176,8 +222,16 @@
 	$tool_dir = "$cwd/$tool_dir";
 }
 
+# Check command line for a configuration file name
+Getopt::Long::Configure("pass_through", "no_auto_abbrev");
+GetOptions("config-file=s" => \$opt_config_file,
+	   "rc=s%" => \%opt_rc);
+Getopt::Long::Configure("default");
+
 # Read configuration file if available
-if (-r $ENV{"HOME"}."/.lcovrc")
+if (defined($opt_config_file)) {
+	$config = read_config($opt_config_file);
+} elsif (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
 {
 	$config = read_config($ENV{"HOME"}."/.lcovrc");
 }
@@ -186,45 +240,61 @@
 	$config = read_config("/etc/lcovrc");
 }
 
-if ($config)
+if ($config || %opt_rc)
 {
-	# Copy configuration file values to variables
+	# Copy configuration file and --rc values to variables
 	apply_config({
 		"lcov_gcov_dir"		=> \$gcov_dir,
-		"lcov_insmod_tool"	=> \$insmod_tool,
-		"lcov_modprobe_tool"	=> \$modprobe_tool,
-		"lcov_rmmod_tool"	=> \$rmmod_tool,
-		"lcov_tmp_dir"		=> \$tmp_dir});
+		"lcov_tmp_dir"		=> \$tmp_dir,
+		"lcov_list_full_path"	=> \$opt_list_full_path,
+		"lcov_list_width"	=> \$opt_list_width,
+		"lcov_list_truncate_max"=> \$opt_list_truncate_max,
+		"lcov_branch_coverage"	=> \$br_coverage,
+		"lcov_function_coverage"=> \$func_coverage,
+	});
 }
 
 # Parse command line options
 if (!GetOptions("directory|d|di=s" => \@directory,
-		"add-tracefile=s" => \@add_tracefile,
-		"list=s" => \$list,
-		"kernel-directory=s" => \@kernel_directory,
-		"extract=s" => \$extract,
-		"remove=s" => \$remove,
+		"add-tracefile|a=s" => \@add_tracefile,
+		"list|l=s" => \$list,
+		"kernel-directory|k=s" => \@kernel_directory,
+		"extract|e=s" => \$extract,
+		"remove|r=s" => \$remove,
 		"diff=s" => \$diff,
 		"convert-filenames" => \$convert_filenames,
 		"strip=i" => \$strip,
 		"capture|c" => \$capture,
-		"output-file=s" => \$output_filename,
-		"test-name=s" => \$test_name,
-		"zerocounters" => \$reset,
-		"quiet" => \$quiet,
-		"help|?" => \$help,
-		"version" => \$version,
-		"follow" => \$follow,
+		"output-file|o=s" => \$output_filename,
+		"test-name|t=s" => \$test_name,
+		"zerocounters|z" => \$reset,
+		"quiet|q" => \$quiet,
+		"help|h|?" => \$help,
+		"version|v" => \$version,
+		"follow|f" => \$follow,
 		"path=s" => \$diff_path,
-		"base-directory=s" => \$base_directory,
+		"base-directory|b=s" => \$base_directory,
 		"checksum" => \$checksum,
 		"no-checksum" => \$no_checksum,
 		"compat-libtool" => \$compat_libtool,
 		"no-compat-libtool" => \$no_compat_libtool,
 		"gcov-tool=s" => \$gcov_tool,
-		"ignore-errors=s" => \$ignore_errors,
+		"ignore-errors=s" => \@opt_ignore_errors,
 		"initial|i" => \$initial,
-		"no-recursion" => \$no_recursion
+		"no-recursion" => \$no_recursion,
+		"to-package=s" => \$to_package,
+		"from-package=s" => \$from_package,
+		"no-markers" => \$no_markers,
+		"derive-func-data" => \$opt_derive_func_data,
+		"debug" => \$opt_debug,
+		"list-full-path" => \$opt_list_full_path,
+		"no-list-full-path" => \$opt_no_list_full_path,
+		"external" => \$opt_external,
+		"no-external" => \$opt_no_external,
+		"summary=s" => \@opt_summary,
+		"compat=s" => \$opt_compat,
+		"config-file=s" => \$opt_config_file,
+		"rc=s%" => \%opt_rc,
 		))
 {
 	print(STDERR "Use $tool_name --help to get usage information\n");
@@ -244,6 +314,17 @@
 		$compat_libtool = ($no_compat_libtool ? 0 : 1);
 		$no_compat_libtool = undef;
 	}
+
+	if (defined($opt_no_list_full_path))
+	{
+		$opt_list_full_path = ($opt_no_list_full_path ? 0 : 1);
+		$opt_no_list_full_path = undef;
+	}
+
+	if (defined($opt_no_external)) {
+		$opt_external = 0;
+		$opt_no_external = undef;
+	}
 }
 
 # Check for help option
@@ -260,6 +341,12 @@
 	exit(0);
 }
 
+# Check list width option
+if ($opt_list_width <= 40) {
+	die("ERROR: lcov_list_width parameter out of range (needs to be ".
+	    "larger than 40)\n");
+}
+
 # Normalize --path text
 $diff_path =~ s/\/$//;
 
@@ -285,9 +372,9 @@
 check_options();
 
 # Only --extract, --remove and --diff allow unnamed parameters
-if (@ARGV && !($extract || $remove || $diff))
+if (@ARGV && !($extract || $remove || $diff || @opt_summary))
 {
-	die("Extra parameter found\n".
+	die("Extra parameter found: '".join(" ", @ARGV)."'\n".
 	    "Use $tool_name --help to get usage information\n");
 }
 
@@ -302,13 +389,10 @@
 		$output_filename = "-";
 	}
 }
-else
-{
-	if ($initial)
-	{
-		die("Option --initial is only valid when capturing data (-c)\n".
-		    "Use $tool_name --help to get usage information\n");
-	}
+
+# Determine kernel directory for gcov data
+if (!$from_package && !@directory && ($capture || $reset)) {
+	($gcov_gkv, $gcov_dir) = setup_gkv();
 }
 
 # Check for requested functionality
@@ -326,27 +410,40 @@
 }
 elsif ($capture)
 {
-	# Differentiate between user space and kernel 
-	if (@directory)
-	{
+	# Capture source can be user space, kernel or package
+	if ($from_package) {
+		package_capture();
+	} elsif (@directory) {
 		userspace_capture();
-	}
-	else
-	{
-		kernel_capture();
+	} else {
+		if ($initial) {
+			if (defined($to_package)) {
+				die("ERROR: --initial cannot be used together ".
+				    "with --to-package\n");
+			}
+			kernel_capture_initial();
+		} else {
+			kernel_capture();
+		}
 	}
 }
 elsif (@add_tracefile)
 {
-	add_traces();
+	($ln_overall_found, $ln_overall_hit,
+	 $fn_overall_found, $fn_overall_hit,
+	 $br_overall_found, $br_overall_hit) = add_traces();
 }
 elsif ($remove)
 {
-	remove();
+	($ln_overall_found, $ln_overall_hit,
+	 $fn_overall_found, $fn_overall_hit,
+	 $br_overall_found, $br_overall_hit) = remove();
 }
 elsif ($extract)
 {
-	extract();
+	($ln_overall_found, $ln_overall_hit,
+	 $fn_overall_found, $fn_overall_hit,
+	 $br_overall_found, $br_overall_hit) = extract();
 }
 elsif ($list)
 {
@@ -359,10 +456,26 @@
 		die("ERROR: option --diff requires one additional argument!\n".
 		    "Use $tool_name --help to get usage information\n");
 	}
-	diff();
+	($ln_overall_found, $ln_overall_hit,
+	 $fn_overall_found, $fn_overall_hit,
+	 $br_overall_found, $br_overall_hit) = diff();
+}
+elsif (@opt_summary)
+{
+	($ln_overall_found, $ln_overall_hit,
+	 $fn_overall_found, $fn_overall_hit,
+	 $br_overall_found, $br_overall_hit) = summary();
 }
 
-info("Done.\n");
+temp_cleanup();
+
+if (defined($ln_overall_found)) {
+	print_overall_rate(1, $ln_overall_found, $ln_overall_hit,
+			   1, $fn_overall_found, $fn_overall_hit,
+			   1, $br_overall_found, $br_overall_hit);
+} else {
+	info("Done.\n") if (!$list && !$capture);
+}
 exit(0);
 
 #
@@ -395,6 +508,7 @@
   -r, --remove FILE PATTERN       Remove files matching PATTERN from FILE
   -l, --list FILE                 List contents of tracefile FILE
       --diff FILE DIFF            Transform tracefile FILE according to DIFF
+      --summary FILE              Show summary coverage data for tracefiles
 
 Options:
   -i, --initial                   Capture initial zero coverage data
@@ -410,8 +524,17 @@
       --(no-)checksum             Enable (disable) line checksumming
       --(no-)compat-libtool       Enable (disable) libtool compatibility mode
       --gcov-tool TOOL            Specify gcov tool location
-      --ignore-errors ERRORS      Continue after ERRORS (gcov, source)
-      --no-recursion              Exlude subdirectories from processing
+      --ignore-errors ERRORS      Continue after ERRORS (gcov, source, graph)
+      --no-recursion              Exclude subdirectories from processing
+      --to-package FILENAME       Store unprocessed coverage data in FILENAME
+      --from-package FILENAME     Capture from unprocessed data in FILENAME
+      --no-markers                Ignore exclusion markers in source code
+      --derive-func-data          Generate function data from line data
+      --list-full-path            Print full path during a list operation
+      --(no-)external             Include (ignore) data for external files
+      --config-file FILENAME      Specify configuration file location
+      --rc SETTING=VALUE          Override configuration file setting
+      --compat MODE=on|off|auto   Set compat MODE (libtool, hammer, split_crc)
 
 For more information see: $lcov_url
 END_OF_USAGE
@@ -437,17 +560,18 @@
 	$remove && $i++;
 	$list && $i++;
 	$diff && $i++;
+	@opt_summary && $i++;
 	
 	if ($i == 0)
 	{
-		die("Need one of the options -z, -c, -a, -e, -r, -l or ".
-		    "--diff\n".
+		die("Need one of options -z, -c, -a, -e, -r, -l, ".
+		    "--diff or --summary\n".
 		    "Use $tool_name --help to get usage information\n");
 	}
 	elsif ($i > 1)
 	{
-		die("ERROR: only one of -z, -c, -a, -e, -r, -l or ".
-		    "--diff allowed!\n".
+		die("ERROR: only one of -z, -c, -a, -e, -r, -l, ".
+		    "--diff or --summary allowed!\n".
 		    "Use $tool_name --help to get usage information\n");
 	}
 }
@@ -483,77 +607,31 @@
 #
 # userspace_capture()
 #
-# Capture coverage data found in DIRECTORY and write it to OUTPUT_FILENAME
-# if specified, otherwise to STDOUT.
+# Capture coverage data found in DIRECTORY and write it to a package (if
+# TO_PACKAGE specified) or to OUTPUT_FILENAME or STDOUT.
 #
 # Die on error.
 #
 
 sub userspace_capture()
 {
-	my @param;
-	my $file_list = join(" ", @directory);
+	my $dir;
+	my $build;
 
-	info("Capturing coverage data from $file_list\n");
-	@param = ("$tool_dir/geninfo", @directory);
-	if ($output_filename)
-	{
-		@param = (@param, "--output-filename", $output_filename);
+	if (!defined($to_package)) {
+		lcov_geninfo(@directory);
+		return;
 	}
-	if ($test_name)
-	{
-		@param = (@param, "--test-name", $test_name);
+	if (scalar(@directory) != 1) {
+		die("ERROR: -d may be specified only once with --to-package\n");
 	}
-	if ($follow)
-	{
-		@param = (@param, "--follow");
+	$dir = $directory[0];
+	if (defined($base_directory)) {
+		$build = $base_directory;
+	} else {
+		$build = $dir;
 	}
-	if ($quiet)
-	{
-		@param = (@param, "--quiet");
-	}
-	if (defined($checksum))
-	{
-		if ($checksum)
-		{
-			@param = (@param, "--checksum");
-		}
-		else
-		{
-			@param = (@param, "--no-checksum");
-		}
-	}
-	if ($base_directory)
-	{
-		@param = (@param, "--base-directory", $base_directory);
-	}
-	if ($no_compat_libtool)
-	{
-		@param = (@param, "--no-compat-libtool");
-	}
-	elsif ($compat_libtool)
-	{
-		@param = (@param, "--compat-libtool");
-	}
-	if ($gcov_tool)
-	{
-		@param = (@param, "--gcov-tool", $gcov_tool);
-	}
-	if ($ignore_errors)
-	{
-		@param = (@param, "--ignore-errors", $ignore_errors);
-	}
-	if ($initial)
-	{
-		@param = (@param, "--initial");
-	}
-	if ($no_recursion)
-	{
-		@param = (@param, "--no-recursion");
-	}
-
-	system(@param);
-	exit($? >> 8);
+	create_package($to_package, $dir, $build);
 }
 
 
@@ -568,93 +646,158 @@
 sub kernel_reset()
 {
 	local *HANDLE;
-	check_and_load_kernel_module();
+	my $reset_file;
 
 	info("Resetting kernel execution counters\n");
-	open(HANDLE, ">$gcov_dir/vmlinux") or
-		die("ERROR: cannot write to $gcov_dir/vmlinux!\n");
+	if (-e "$gcov_dir/vmlinux") {
+		$reset_file = "$gcov_dir/vmlinux";
+	} elsif (-e "$gcov_dir/reset") {
+		$reset_file = "$gcov_dir/reset";
+	} else {
+		die("ERROR: no reset control found in $gcov_dir\n");
+	}
+	open(HANDLE, ">", $reset_file) or
+		die("ERROR: cannot write to $reset_file!\n");
 	print(HANDLE "0");
 	close(HANDLE);
-
-	# Unload module if we loaded it in the first place
-	if ($need_unload)
-	{
-		unload_module($need_unload);
-	}
 }
 
 
 #
-# kernel_capture()
+# lcov_copy_single(from, to)
+# 
+# Copy single regular file FROM to TO without checking its size. This is
+# required to work with special files generated by the kernel
+# seq_file-interface.
 #
-# Capture kernel coverage data and write it to OUTPUT_FILENAME if specified,
-# otherwise stdout.
 #
-
-sub kernel_capture()
+sub lcov_copy_single($$)
 {
-	my @param;
+	my ($from, $to) = @_;
+	my $content;
+	local $/;
+	local *HANDLE;
 
-	check_and_load_kernel_module();
+	open(HANDLE, "<", $from) or die("ERROR: cannot read $from: $!\n");
+	$content = <HANDLE>;
+	close(HANDLE);
+	open(HANDLE, ">", $to) or die("ERROR: cannot write $from: $!\n");
+	if (defined($content)) {
+		print(HANDLE $content);
+	}
+	close(HANDLE);
+}
 
-	# Make sure the temporary directory is removed upon script termination
-	END
-	{
-		if ($temp_dir_name)
-		{
-			stat($temp_dir_name);
-			if (-r _)
-			{
-				info("Removing temporary directory ".
-				     "$temp_dir_name\n");
+#
+# lcov_find(dir, function, data[, extension, ...)])
+#
+# Search DIR for files and directories whose name matches PATTERN and run
+# FUNCTION for each match. If not pattern is specified, match all names.
+#
+# FUNCTION has the following prototype:
+#   function(dir, relative_name, data)
+#
+# Where:
+#   dir: the base directory for this search
+#   relative_name: the name relative to the base directory of this entry
+#   data: the DATA variable passed to lcov_find
+#
+sub lcov_find($$$;@)
+{
+	my ($dir, $fn, $data, @pattern) = @_;
+	my $result;
+	my $_fn = sub {
+		my $filename = $File::Find::name;
 
-				# Remove temporary directory
-				system("rm", "-rf", $temp_dir_name)
-					and warn("WARNING: cannot remove ".
-						 "temporary directory ".
-						 "$temp_dir_name!\n");
+		if (defined($result)) {
+			return;
+		}		
+		$filename = abs2rel($filename, $dir);
+		foreach (@pattern) {
+			if ($filename =~ /$_/) {
+				goto ok;
 			}
 		}
+		return;
+	ok:
+		$result = &$fn($dir, $filename, $data);
+	};
+	if (scalar(@pattern) == 0) {
+		@pattern = ".*";
 	}
+	find( { wanted => $_fn, no_chdir => 1 }, $dir);
 
-	# Get temporary directory
-	$temp_dir_name = create_temp_dir();
+	return $result;
+}
 
-	info("Copying kernel data to temporary directory $temp_dir_name\n");
+#
+# lcov_copy_fn(from, rel, to)
+#
+# Copy directories, files and links from/rel to to/rel.
+#
 
-	if (!@kernel_directory)
-	{
-		# Copy files from gcov kernel directory
-		system("cp", "-dr", $gcov_dir, $temp_dir_name)
-			and die("ERROR: cannot copy files from $gcov_dir!\n");
+sub lcov_copy_fn($$$)
+{
+	my ($from, $rel, $to) = @_;
+	my $absfrom = canonpath(catfile($from, $rel));
+	my $absto = canonpath(catfile($to, $rel));
+
+	if (-d) {
+		if (! -d $absto) {
+			mkpath($absto) or
+				die("ERROR: cannot create directory $absto\n");
+			chmod(0700, $absto);
+		}
+	} elsif (-l) {
+		# Copy symbolic link
+		my $link = readlink($absfrom);
+
+		if (!defined($link)) {
+			die("ERROR: cannot read link $absfrom: $!\n");
+		}
+		symlink($link, $absto) or
+			die("ERROR: cannot create link $absto: $!\n");
+	} else {
+		lcov_copy_single($absfrom, $absto);
+		chmod(0600, $absto);
 	}
-	else
-	{
-		# Prefix list of kernel sub-directories with the gcov kernel
-		# directory
-		@kernel_directory = map("$gcov_dir/$_", @kernel_directory);
+	return undef;
+}
 
-		# Copy files from gcov kernel directory
-		system("cp", "-dr", @kernel_directory, $temp_dir_name)
-			and die("ERROR: cannot copy files from ".
-				join(" ", @kernel_directory)."!\n");
+#
+# lcov_copy(from, to, subdirs)
+# 
+# Copy all specified SUBDIRS and files from directory FROM to directory TO. For
+# regular files, copy file contents without checking its size. This is required
+# to work with seq_file-generated files.
+#
+
+sub lcov_copy($$;@)
+{
+	my ($from, $to, @subdirs) = @_;
+	my @pattern;
+
+	foreach (@subdirs) {
+		push(@pattern, "^$_");
 	}
+	lcov_find($from, \&lcov_copy_fn, $to, @pattern);
+}
 
-	# Make directories writable
-	system("find", $temp_dir_name, "-type", "d", "-exec", "chmod", "u+w",
-	       "{}", ";")
-		and die("ERROR: cannot modify access rights for ".
-			"$temp_dir_name!\n");
+#
+# lcov_geninfo(directory)
+#
+# Call geninfo for the specified directory and with the parameters specified
+# at the command line.
+#
 
-	# Make files writable
-	system("find", $temp_dir_name, "-type", "f", "-exec", "chmod", "u+w",
-	       "{}", ";")
-		and die("ERROR: cannot modify access rights for ".
-			"$temp_dir_name!\n");
+sub lcov_geninfo(@)
+{
+	my (@dir) = @_;
+	my @param;
 
 	# Capture data
-	info("Capturing coverage data from $temp_dir_name\n");
-	@param = ("$tool_dir/geninfo", $temp_dir_name);
+	info("Capturing coverage data from ".join(" ", @dir)."\n");
+	@param = ("$tool_dir/geninfo", @dir);
 	if ($output_filename)
 	{
 		@param = (@param, "--output-filename", $output_filename);
@@ -698,21 +841,470 @@
 	{
 		@param = (@param, "--gcov-tool", $gcov_tool);
 	}
-	if ($ignore_errors)
-	{
-		@param = (@param, "--ignore-errors", $ignore_errors);
+	foreach (@opt_ignore_errors) {
+		@param = (@param, "--ignore-errors", $_);
+	}
+	if ($no_recursion) {
+		@param = (@param, "--no-recursion");
 	}
 	if ($initial)
 	{
 		@param = (@param, "--initial");
 	}
-	system(@param) and exit($? >> 8);
-
-
-	# Unload module if we loaded it in the first place
-	if ($need_unload)
+	if ($no_markers)
 	{
-		unload_module($need_unload);
+		@param = (@param, "--no-markers");
+	}
+	if ($opt_derive_func_data)
+	{
+		@param = (@param, "--derive-func-data");
+	}
+	if ($opt_debug)
+	{
+		@param = (@param, "--debug");
+	}
+	if (defined($opt_external) && $opt_external)
+	{
+		@param = (@param, "--external");
+	}
+	if (defined($opt_external) && !$opt_external)
+	{
+		@param = (@param, "--no-external");
+	}
+	if (defined($opt_compat)) {
+		@param = (@param, "--compat", $opt_compat);
+	}
+	if (%opt_rc) {
+		foreach my $key (keys(%opt_rc)) {
+			@param = (@param, "--rc", "$key=".$opt_rc{$key});
+		}
+	}
+
+	system(@param) and exit($? >> 8);
+}
+
+#
+# read_file(filename)
+#
+# Return the contents of the file defined by filename.
+#
+
+sub read_file($)
+{
+	my ($filename) = @_;
+	my $content;
+	local $\;
+	local *HANDLE;
+
+	open(HANDLE, "<", $filename) || return undef;
+	$content = <HANDLE>;
+	close(HANDLE);
+
+	return $content;
+}
+
+#
+# get_package(package_file)
+#
+# Unpack unprocessed coverage data files from package_file to a temporary
+# directory and return directory name, build directory and gcov kernel version
+# as found in package.
+#
+
+sub get_package($)
+{
+	my ($file) = @_;
+	my $dir = create_temp_dir();
+	my $gkv;
+	my $build;
+	my $cwd = getcwd();
+	my $count;
+	local *HANDLE;
+
+	info("Reading package $file:\n");
+	info("  data directory .......: $dir\n");
+	$file = abs_path($file);
+	chdir($dir);
+	open(HANDLE, "-|", "tar xvfz '$file' 2>/dev/null")
+		or die("ERROR: could not process package $file\n");
+	while (<HANDLE>) {
+		if (/\.da$/ || /\.gcda$/) {
+			$count++;
+		}
+	}
+	close(HANDLE);
+	$build = read_file("$dir/$pkg_build_file");
+	if (defined($build)) {
+		info("  build directory ......: $build\n");
+	}
+	$gkv = read_file("$dir/$pkg_gkv_file");
+	if (defined($gkv)) {
+		$gkv = int($gkv);
+		if ($gkv != $GKV_PROC && $gkv != $GKV_SYS) {
+			die("ERROR: unsupported gcov kernel version found ".
+			    "($gkv)\n");
+		}
+		info("  content type .........: kernel data\n");
+		info("  gcov kernel version ..: %s\n", $GKV_NAME[$gkv]);
+	} else {
+		info("  content type .........: application data\n");
+	}
+	info("  data files ...........: $count\n");
+	chdir($cwd);
+
+	return ($dir, $build, $gkv);
+}
+
+#
+# write_file(filename, $content)
+#
+# Create a file named filename and write the specified content to it.
+#
+
+sub write_file($$)
+{
+	my ($filename, $content) = @_;
+	local *HANDLE;
+
+	open(HANDLE, ">", $filename) || return 0;
+	print(HANDLE $content);
+	close(HANDLE) || return 0;
+
+	return 1;
+}
+
+# count_package_data(filename)
+#
+# Count the number of coverage data files in the specified package file.
+#
+
+sub count_package_data($)
+{
+	my ($filename) = @_;
+	local *HANDLE;
+	my $count = 0;
+
+	open(HANDLE, "-|", "tar tfz '$filename'") or return undef;
+	while (<HANDLE>) {
+		if (/\.da$/ || /\.gcda$/) {
+			$count++;
+		}
+	}
+	close(HANDLE);
+	return $count;
+}
+
+#
+# create_package(package_file, source_directory, build_directory[,
+# 		 kernel_gcov_version])
+#
+# Store unprocessed coverage data files from source_directory to package_file.
+#
+
+sub create_package($$$;$)
+{
+	my ($file, $dir, $build, $gkv) = @_;
+	my $cwd = getcwd();
+
+	# Print information about the package
+	info("Creating package $file:\n");
+	info("  data directory .......: $dir\n");
+
+	# Handle build directory
+	if (defined($build)) {
+		info("  build directory ......: $build\n");
+		write_file("$dir/$pkg_build_file", $build)
+			or die("ERROR: could not write to ".
+			       "$dir/$pkg_build_file\n");
+	}
+
+	# Handle gcov kernel version data
+	if (defined($gkv)) {
+		info("  content type .........: kernel data\n");
+		info("  gcov kernel version ..: %s\n", $GKV_NAME[$gkv]);
+		write_file("$dir/$pkg_gkv_file", $gkv)
+			or die("ERROR: could not write to ".
+			       "$dir/$pkg_gkv_file\n");
+	} else {
+		info("  content type .........: application data\n");
+	}
+
+	# Create package
+	$file = abs_path($file);
+	chdir($dir);
+	system("tar cfz $file .")
+		and die("ERROR: could not create package $file\n");
+
+	# Remove temporary files
+	unlink("$dir/$pkg_build_file");
+	unlink("$dir/$pkg_gkv_file");
+
+	# Show number of data files
+	if (!$quiet) {
+		my $count = count_package_data($file);
+
+		if (defined($count)) {
+			info("  data files ...........: $count\n");
+		}
+	}
+	chdir($cwd);
+}
+
+sub find_link_fn($$$)
+{
+	my ($from, $rel, $filename) = @_;
+	my $absfile = catfile($from, $rel, $filename);
+
+	if (-l $absfile) {
+		return $absfile;
+	}
+	return undef;
+}
+
+#
+# get_base(dir)
+#
+# Return (BASE, OBJ), where
+#  - BASE: is the path to the kernel base directory relative to dir
+#  - OBJ: is the absolute path to the kernel build directory
+#
+
+sub get_base($)
+{
+	my ($dir) = @_;
+	my $marker = "kernel/gcov/base.gcno";
+	my $markerfile;
+	my $sys;
+	my $obj;
+	my $link;
+
+	$markerfile = lcov_find($dir, \&find_link_fn, $marker);
+	if (!defined($markerfile)) {
+		return (undef, undef);
+	}
+
+	# sys base is parent of parent of markerfile.
+	$sys = abs2rel(dirname(dirname(dirname($markerfile))), $dir);
+
+	# obj base is parent of parent of markerfile link target.
+	$link = readlink($markerfile);
+	if (!defined($link)) {
+		die("ERROR: could not read $markerfile\n");
+	}
+	$obj = dirname(dirname(dirname($link)));
+
+	return ($sys, $obj);
+}
+
+#
+# apply_base_dir(data_dir, base_dir, build_dir, @directories)
+#
+# Make entries in @directories relative to data_dir.
+#
+
+sub apply_base_dir($$$@)
+{
+	my ($data, $base, $build, @dirs) = @_;
+	my $dir;
+	my @result;
+
+	foreach $dir (@dirs) {
+		# Is directory path relative to data directory?
+		if (-d catdir($data, $dir)) {
+			push(@result, $dir);
+			next;
+		}
+		# Relative to the auto-detected base-directory?
+		if (defined($base)) {
+			if (-d catdir($data, $base, $dir)) {
+				push(@result, catdir($base, $dir));
+				next;
+			}
+		}
+		# Relative to the specified base-directory?
+		if (defined($base_directory)) {
+			if (file_name_is_absolute($base_directory)) {
+				$base = abs2rel($base_directory, rootdir());
+			} else {
+				$base = $base_directory;
+			}
+			if (-d catdir($data, $base, $dir)) {
+				push(@result, catdir($base, $dir));
+				next;
+			}
+		}
+		# Relative to the build directory?
+		if (defined($build)) {
+			if (file_name_is_absolute($build)) {
+				$base = abs2rel($build, rootdir());
+			} else {
+				$base = $build;
+			}
+			if (-d catdir($data, $base, $dir)) {
+				push(@result, catdir($base, $dir));
+				next;
+			}
+		}
+		die("ERROR: subdirectory $dir not found\n".
+		    "Please use -b to specify the correct directory\n");
+	}
+	return @result;
+}
+
+#
+# copy_gcov_dir(dir, [@subdirectories])
+#
+# Create a temporary directory and copy all or, if specified, only some
+# subdirectories from dir to that directory. Return the name of the temporary
+# directory.
+#
+
+sub copy_gcov_dir($;@)
+{
+	my ($data, @dirs) = @_;
+	my $tempdir = create_temp_dir();
+
+	info("Copying data to temporary directory $tempdir\n");
+	lcov_copy($data, $tempdir, @dirs);
+
+	return $tempdir;
+}
+
+#
+# kernel_capture_initial
+#
+# Capture initial kernel coverage data, i.e. create a coverage data file from
+# static graph files which contains zero coverage data for all instrumented
+# lines.
+#
+
+sub kernel_capture_initial()
+{
+	my $build;
+	my $source;
+	my @params;
+
+	if (defined($base_directory)) {
+		$build = $base_directory;
+		$source = "specified";
+	} else {
+		(undef, $build) = get_base($gcov_dir);
+		if (!defined($build)) {
+			die("ERROR: could not auto-detect build directory.\n".
+			    "Please use -b to specify the build directory\n");
+		}
+		$source = "auto-detected";
+	}
+	info("Using $build as kernel build directory ($source)\n");
+	# Build directory needs to be passed to geninfo
+	$base_directory = $build;
+	if (@kernel_directory) {
+		foreach my $dir (@kernel_directory) {
+			push(@params, "$build/$dir");
+		}
+	} else {
+		push(@params, $build);
+	}
+	lcov_geninfo(@params);
+}
+
+#
+# kernel_capture_from_dir(directory, gcov_kernel_version, build)
+#
+# Perform the actual kernel coverage capturing from the specified directory
+# assuming that the data was copied from the specified gcov kernel version.
+#
+
+sub kernel_capture_from_dir($$$)
+{
+	my ($dir, $gkv, $build) = @_;
+
+	# Create package or coverage file
+	if (defined($to_package)) {
+		create_package($to_package, $dir, $build, $gkv);
+	} else {
+		# Build directory needs to be passed to geninfo
+		$base_directory = $build;
+		lcov_geninfo($dir);
+	}
+}
+
+#
+# adjust_kernel_dir(dir, build)
+#
+# Adjust directories specified with -k so that they point to the directory
+# relative to DIR. Return the build directory if specified or the auto-
+# detected build-directory.
+#
+
+sub adjust_kernel_dir($$)
+{
+	my ($dir, $build) = @_;
+	my ($sys_base, $build_auto) = get_base($dir);
+
+	if (!defined($build)) {
+		$build = $build_auto;
+	}
+	if (!defined($build)) {
+		die("ERROR: could not auto-detect build directory.\n".
+		    "Please use -b to specify the build directory\n");
+	}
+	# Make @kernel_directory relative to sysfs base
+	if (@kernel_directory) {
+		@kernel_directory = apply_base_dir($dir, $sys_base, $build,
+						   @kernel_directory);
+	}
+	return $build;
+}
+
+sub kernel_capture()
+{
+	my $data_dir;
+	my $build = $base_directory;
+
+	if ($gcov_gkv == $GKV_SYS) {
+		$build = adjust_kernel_dir($gcov_dir, $build);
+	}
+	$data_dir = copy_gcov_dir($gcov_dir, @kernel_directory);
+	kernel_capture_from_dir($data_dir, $gcov_gkv, $build);
+}
+
+#
+# package_capture()
+#
+# Capture coverage data from a package of unprocessed coverage data files
+# as generated by lcov --to-package.
+#
+
+sub package_capture()
+{
+	my $dir;
+	my $build;
+	my $gkv;
+
+	($dir, $build, $gkv) = get_package($from_package);
+
+	# Check for build directory
+	if (defined($base_directory)) {
+		if (defined($build)) {
+			info("Using build directory specified by -b.\n");
+		}
+		$build = $base_directory;
+	}
+
+	# Do the actual capture
+	if (defined($gkv)) {
+		if ($gkv == $GKV_SYS) {
+			$build = adjust_kernel_dir($dir, $build);
+		}
+		if (@kernel_directory) {
+			$dir = copy_gcov_dir($dir, @kernel_directory);	
+		}
+		kernel_capture_from_dir($dir, $gkv, $build);
+	} else {
+		# Build directory needs to be passed to geninfo
+		$base_directory = $build;
+		lcov_geninfo($dir);
 	}
 }
 
@@ -731,11 +1323,11 @@
 		# Print info string
 		if ($to_file)
 		{
-			print(@_)
+			printf(@_)
 		}
 		else
 		{
-			# Don't interfer with the .info output to STDOUT
+			# Don't interfere with the .info output to STDOUT
 			printf(STDERR @_);
 		}
 	}
@@ -743,106 +1335,6 @@
 
 
 #
-# Check if the gcov kernel module is loaded. If it is, exit, if not, try
-# to load it.
-#
-# Die on error.
-#
-
-sub check_and_load_kernel_module()
-{
-	my $module_name;
-
-	# Is it loaded already?
-	stat("$gcov_dir");
-	if (-r _) { return(); }
-
-	info("Loading required gcov kernel module.\n");
-
-	# Do we have access to the insmod tool?
-	stat($insmod_tool);
-	if (!-x _)
-	{
-		die("ERROR: need insmod tool ($insmod_tool) to access kernel ".
-		    "coverage data!\n");
-	}
-	# Do we have access to the modprobe tool?
-	stat($modprobe_tool);
-	if (!-x _)
-	{
-		die("ERROR: need modprobe tool ($modprobe_tool) to access ".
-		    "kernel coverage data!\n");
-	}
-
-	# Try some possibilities of where the gcov kernel module may be found
-	foreach $module_name (@gcovmod)
-	{
-		# Try to load module from system wide module directory
-		# /lib/modules
-		if (system_no_output(3, $modprobe_tool, $module_name) == 0)
-		{
-			# Succeeded
-			$need_unload = $module_name;
-			return();
-		}
-
-		# Try to load linux 2.5/2.6 module from tool directory
-		if (system_no_output(3, $insmod_tool,
-				      "$tool_dir/$module_name.ko") == 0)
-		{
-			# Succeeded
-			$need_unload = $module_name;
-			return();
-		}
-
-		# Try to load linux 2.4 module from tool directory
-		if (system_no_output(3, $insmod_tool,
-				     "$tool_dir/$module_name.o") == 0)
-		{
-			# Succeeded
-			$need_unload = $module_name;
-			return();
-		}
-	}
-
-	# Hm, loading failed - maybe we aren't root?
-	if ($> != 0)
-	{
-		die("ERROR: need root access to load kernel module!\n");
-	}
-
-	die("ERROR: cannot load required gcov kernel module!\n");
-}
-
-
-#
-# unload_module()
-#
-# Unload the gcov kernel module.
-#
-
-sub unload_module($)
-{
-	my $module = $_[0];
-
-	info("Unloading kernel module $module\n");
-
-	# Do we have access to the rmmod tool?
-	stat($rmmod_tool);
-	if (!-x _)
-	{
-		warn("WARNING: cannot execute rmmod tool at $rmmod_tool - ".
-		     "gcov module still loaded!\n");
-	}
-
-	# Unload gcov kernel module
-	system_no_output(1, $rmmod_tool, $module)
-		and warn("WARNING: cannot unload gcov kernel module ".
-		         "$module!\n");
-}
-
-
-#
 # create_temp_dir()
 #
 # Create a temporary directory and return its path.
@@ -852,24 +1344,200 @@
 
 sub create_temp_dir()
 {
-	my $dirname;
-	my $number = sprintf("%d", rand(1000));
+	my $dir;
 
-	# Endless loops are evil
-	while ($number++ < 1000)
-	{
-		$dirname = "$tmp_dir/$tmp_prefix$number";
-		stat($dirname);
-		if (-e _) { next; }
+	if (defined($tmp_dir)) {
+		$dir = tempdir(DIR => $tmp_dir, CLEANUP => 1);
+	} else {
+		$dir = tempdir(CLEANUP => 1);
+	}
+	if (!defined($dir)) {
+		die("ERROR: cannot create temporary directory\n");
+	}
+	push(@temp_dirs, $dir);
 
-		mkdir($dirname)
-			or die("ERROR: cannot create temporary directory ".
-			       "$dirname!\n");
+	return $dir;
+}
 
-		return($dirname);
+
+#
+# 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_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;
 	}
 
-	die("ERROR: cannot create temporary directory in $tmp_dir!\n");
+	$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;
+}
+
+
+#
+# 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);
+}
+
+
+#
+# get_br_found_and_hit(brcount)
+#
+# Return (br_found, br_hit) for brcount
+#
+
+sub get_br_found_and_hit($)
+{
+	my ($brcount) = @_;
+	my $line;
+	my $br_found = 0;
+	my $br_hit = 0;
+
+	foreach $line (keys(%{$brcount})) {
+		my $brdata = $brcount->{$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);
 }
 
 
@@ -889,16 +1557,22 @@
 #        "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.
@@ -923,6 +1597,9 @@
 	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
@@ -961,14 +1638,14 @@
 				"compressed file $_[0]!\n");
 
 		# Open compressed file
-		open(INFO_HANDLE, "gunzip -c $_[0]|")
+		open(INFO_HANDLE, "-|", "gunzip -c '$_[0]'")
 			or die("ERROR: cannot start gunzip to decompress ".
 			       "file $_[0]!\n");
 	}
 	else
 	{
 		# Open decompressed file
-		open(INFO_HANDLE, $_[0])
+		open(INFO_HANDLE, "<", $_[0])
 			or die("ERROR: cannot read file $_[0]!\n");
 	}
 
@@ -981,7 +1658,7 @@
 		# Switch statement
 		foreach ($line)
 		{
-			/^TN:([^,]*)/ && do
+			/^TN:([^,]*)(,diff)?/ && do
 			{
 				# Test name information found
 				$testname = defined($1) ? $1 : "";
@@ -989,6 +1666,7 @@
 				{
 					$changed_testname = 1;
 				}
+				$testname .= $2 if (defined($2));
 				last;
 			};
 
@@ -1000,18 +1678,21 @@
 
 				$data = $result{$filename};
 				($testdata, $sumcount, $funcdata, $checkdata,
-				 $testfncdata, $sumfnccount) =
+				 $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;
 			};
@@ -1055,6 +1736,8 @@
 
 			/^FN:(\d+),([^,]+)/ && do
 			{
+				last if (!$func_coverage);
+
 				# Function data found, add to structure
 				$funcdata->{$2} = $1;
 
@@ -1073,6 +1756,8 @@
 
 			/^FNDA:(\d+),([^,]+)/ && do
 			{
+				last if (!$func_coverage);
+
 				# Function call count found, add to structure
 				# Add summary counts
 				$sumfnccount->{$2} += $1;
@@ -1084,6 +1769,28 @@
 				}
 				last;
 			};
+
+			/^BRDA:(\d+),(\d+),(\d+),(\d+|-)/ && do {
+				# Branch coverage data found
+				my ($line, $block, $branch, $taken) =
+				   ($1, $2, $3, $4);
+
+				last if (!$br_coverage);
+				$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
@@ -1096,12 +1803,16 @@
 							$testcount;
 						$testfncdata->{$testname} =
 							$testfnccount;
+						$testbrdata->{$testname} =
+							$testbrcount;
 					}	
 
 					set_info_entry($data, $testdata,
 						       $sumcount, $funcdata,
 						       $checkdata, $testfncdata,
-						       $sumfnccount);
+						       $sumfnccount,
+						       $testbrdata,
+						       $sumbrcount);
 					$result{$filename} = $data;
 					last;
 				}
@@ -1119,7 +1830,8 @@
 		$data = $result{$filename};
 
 		($testdata, $sumcount, undef, undef, $testfncdata,
-		 $sumfnccount) = get_info_entry($data);
+		 $sumfnccount, $testbrdata, $sumbrcount) =
+			get_info_entry($data);
 
 		# Filter out empty files
 		if (scalar(keys(%{$sumcount})) == 0)
@@ -1158,6 +1870,14 @@
 			}
 		}
 		$data->{"f_hit"} = $hitcount;
+
+		# Get found/hit values for branch data
+		{
+			my ($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)
@@ -1185,8 +1905,9 @@
 # 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)
+#  ref, testfncdata hash ref, sumfnccount hash ref, testbrdata hash ref,
+#  sumbrcount hash ref, lines found, lines hit, functions found,
+#  functions hit, branches found, branches hit)
 #
 
 sub get_info_entry($)
@@ -1197,26 +1918,32 @@
 	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 $f_found = $_[0]->{"f_found"};
 	my $f_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, $lines_found, $lines_hit,
-		$f_found, $f_hit);
+		$testfncdata, $sumfnccount, $testbrdata, $sumbrcount,
+		$lines_found, $lines_hit, $f_found, $f_hit,
+		$br_found, $br_hit);
 }
 
 
 #
 # set_info_entry(hash_ref, testdata_ref, sumcount_ref, funcdata_ref,
-#                checkdata_ref, testfncdata_ref, sumfcncount_ref[,lines_found,
-#                lines_hit, f_found, f_hit])
+#                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($$$$$$$;$$$$)
+sub set_info_entry($$$$$$$$$;$$$$$$)
 {
 	my $data_ref = $_[0];
 
@@ -1226,11 +1953,15 @@
 	$data_ref->{"check"} = $_[4];
 	$data_ref->{"testfnc"} = $_[5];
 	$data_ref->{"sumfnc"} = $_[6];
+	$data_ref->{"testbr"} = $_[7];
+	$data_ref->{"sumbr"} = $_[8];
 
-	if (defined($_[7])) { $data_ref->{"found"} = $_[7]; }
-	if (defined($_[8])) { $data_ref->{"hit"} = $_[8]; }
-	if (defined($_[9])) { $data_ref->{"f_found"} = $_[9]; }
-	if (defined($_[10])) { $data_ref->{"f_hit"} = $_[10]; }
+	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]; }
 }
 
 
@@ -1338,7 +2069,9 @@
 	my %result;
 	my $func;
 
-	%result = %{$funcdata1};
+	if (defined($funcdata1)) {
+		%result = %{$funcdata1};
+	}
 
 	foreach $func (keys(%{$funcdata2})) {
 		my $line1 = $result{$func};
@@ -1370,7 +2103,9 @@
 	my $f_hit;
 	my $function;
 
-	%result = %{$fnccount1};
+	if (defined($fnccount1)) {
+		%result = %{$fnccount1};
+	}
 	foreach $function (keys(%{$fnccount2})) {
 		$result{$function} += $fnccount2->{$function};
 	}
@@ -1424,6 +2159,167 @@
 	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)
 #
@@ -1440,6 +2336,8 @@
 	my $checkdata1;
 	my $testfncdata1;
 	my $sumfnccount1;
+	my $testbrdata1;
+	my $sumbrcount1;
 
 	my $entry2 = $_[1];	# Reference to hash containing second entry
 	my $testdata2;
@@ -1448,6 +2346,8 @@
 	my $checkdata2;
 	my $testfncdata2;
 	my $sumfnccount2;
+	my $testbrdata2;
+	my $sumbrcount2;
 
 	my %result;		# Hash containing combined entry
 	my %result_testdata;
@@ -1455,19 +2355,23 @@
 	my $result_funcdata;
 	my $result_testfncdata;
 	my $result_sumfnccount;
+	my $result_testbrdata;
+	my $result_sumbrcount;
 	my $lines_found;
 	my $lines_hit;
 	my $f_found;
 	my $f_hit;
+	my $br_found;
+	my $br_hit;
 
 	my $testname;
 	my $filename = $_[2];
 
 	# Retrieve data
 	($testdata1, $sumcount1, $funcdata1, $checkdata1, $testfncdata1,
-	 $sumfnccount1) = get_info_entry($entry1);
+	 $sumfnccount1, $testbrdata1, $sumbrcount1) = get_info_entry($entry1);
 	($testdata2, $sumcount2, $funcdata2, $checkdata2, $testfncdata2,
-	 $sumfnccount2) = get_info_entry($entry2);
+	 $sumfnccount2, $testbrdata2, $sumbrcount2) = get_info_entry($entry2);
 
 	# Merge checksums
 	$checkdata1 = merge_checksums($checkdata1, $checkdata2, $filename);
@@ -1479,7 +2383,12 @@
 	$result_testfncdata = add_testfncdata($testfncdata1, $testfncdata2);
 	($result_sumfnccount, $f_found, $f_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}))
 	{
@@ -1522,8 +2431,9 @@
 	# Store result
 	set_info_entry(\%result, \%result_testdata, $result_sumcount,
 		       $result_funcdata, $checkdata1, $result_testfncdata,
-		       $result_sumfnccount, $lines_found, $lines_hit,
-		       $f_found, $f_hit);
+		       $result_sumfnccount, $result_testbrdata,
+		       $result_sumbrcount, $lines_found, $lines_hit,
+		       $f_found, $f_hit, $br_found, $br_hit);
 
 	return(\%result);
 }
@@ -1573,6 +2483,7 @@
 	my $total_trace;
 	my $current_trace;
 	my $tracefile;
+	my @result;
 	local *INFO_HANDLE;
 
 	info("Combining tracefiles.\n");
@@ -1595,15 +2506,17 @@
 	if ($to_file)
 	{
 		info("Writing data to $output_filename\n");
-		open(INFO_HANDLE, ">$output_filename")
+		open(INFO_HANDLE, ">", $output_filename)
 			or die("ERROR: cannot write to $output_filename!\n");
-		write_info_file(*INFO_HANDLE, $total_trace);
+		@result = write_info_file(*INFO_HANDLE, $total_trace);
 		close(*INFO_HANDLE);
 	}
 	else
 	{
-		write_info_file(*STDOUT, $total_trace);
+		@result = write_info_file(*STDOUT, $total_trace);
 	}
+
+	return @result;
 }
 
 
@@ -1623,25 +2536,48 @@
 	my $checkdata;
 	my $testfncdata;
 	my $sumfnccount;
+	my $testbrdata;
+	my $sumbrcount;
 	my $testname;
 	my $line;
 	my $func;
 	my $testcount;
 	my $testfnccount;
+	my $testbrcount;
 	my $found;
 	my $hit;
 	my $f_found;
 	my $f_hit;
+	my $br_found;
+	my $br_hit;
+	my $ln_total_found = 0;
+	my $ln_total_hit = 0;
+	my $fn_total_found = 0;
+	my $fn_total_hit = 0;
+	my $br_total_found = 0;
+	my $br_total_hit = 0;
 
-	foreach $source_file (keys(%data))
+	foreach $source_file (sort(keys(%data)))
 	{
 		$entry = $data{$source_file};
 		($testdata, $sumcount, $funcdata, $checkdata, $testfncdata,
-		 $sumfnccount) = get_info_entry($entry);
-		foreach $testname (keys(%{$testdata}))
+		 $sumfnccount, $testbrdata, $sumbrcount, $found, $hit,
+		 $f_found, $f_hit, $br_found, $br_hit) =
+			get_info_entry($entry);
+
+		# Add to totals
+		$ln_total_found += $found;
+		$ln_total_hit += $hit;
+		$fn_total_found += $f_found;
+		$fn_total_hit += $f_hit;
+		$br_total_found += $br_found;
+		$br_total_hit += $br_hit;
+
+		foreach $testname (sort(keys(%{$testdata})))
 		{
 			$testcount = $testdata->{$testname};
 			$testfnccount = $testfncdata->{$testname};
+			$testbrcount = $testbrdata->{$testname};
 			$found = 0;
 			$hit   = 0;
 
@@ -1666,6 +2602,31 @@
 			print(INFO_HANDLE "FNF:$f_found\n");
 			print(INFO_HANDLE "FNH:$f_hit\n");
 
+			# Write branch related data
+			$br_found = 0;
+			$br_hit = 0;
+			foreach $line (sort({$a <=> $b}
+					    keys(%{$testbrcount}))) {
+				my $brdata = $testbrcount->{$line};
+				my $num = br_ivec_len($brdata);
+				my $i;
+
+				for ($i = 0; $i < $num; $i++) {
+					my ($block, $branch, $taken) =
+						br_ivec_get($brdata, $i);
+
+					print(INFO_HANDLE "BRDA:$line,$block,".
+					      "$branch,$taken\n");
+					$br_found++;
+					$br_hit++ if ($taken ne '-' &&
+						      $taken > 0);
+				}
+			}
+			if ($br_found > 0) {
+				print(INFO_HANDLE "BRF:$br_found\n");
+				print(INFO_HANDLE "BRH:$br_hit\n");
+			}
+
 			# Write line related data
 			foreach $line (sort({$a <=> $b} keys(%{$testcount})))
 			{
@@ -1686,13 +2647,16 @@
 			print(INFO_HANDLE "end_of_record\n");
 		}
 	}
+
+	return ($ln_total_found, $ln_total_hit, $fn_total_found, $fn_total_hit,
+		$br_total_found, $br_total_hit);
 }
 
 
 #
 # transform_pattern(pattern)
 #
-# Transform shell wildcard expression to equivalent PERL regular expression.
+# Transform shell wildcard expression to equivalent Perl regular expression.
 # Return transformed pattern.
 #
 
@@ -1739,6 +2703,7 @@
 	my $pattern;
 	my @pattern_list;
 	my $extracted = 0;
+	my @result;
 	local *INFO_HANDLE;
 
 	# Need perlreg expressions instead of shell pattern
@@ -1771,15 +2736,17 @@
 	{
 		info("Extracted $extracted files\n");
 		info("Writing data to $output_filename\n");
-		open(INFO_HANDLE, ">$output_filename")
+		open(INFO_HANDLE, ">", $output_filename)
 			or die("ERROR: cannot write to $output_filename!\n");
-		write_info_file(*INFO_HANDLE, $data);
+		@result = write_info_file(*INFO_HANDLE, $data);
 		close(*INFO_HANDLE);
 	}
 	else
 	{
-		write_info_file(*STDOUT, $data);
+		@result = write_info_file(*STDOUT, $data);
 	}
+
+	return @result;
 }
 
 
@@ -1795,6 +2762,7 @@
 	my $pattern;
 	my @pattern_list;
 	my $removed = 0;
+	my @result;
 	local *INFO_HANDLE;
 
 	# Need perlreg expressions instead of shell pattern
@@ -1824,18 +2792,128 @@
 	{
 		info("Deleted $removed files\n");
 		info("Writing data to $output_filename\n");
-		open(INFO_HANDLE, ">$output_filename")
+		open(INFO_HANDLE, ">", $output_filename)
 			or die("ERROR: cannot write to $output_filename!\n");
-		write_info_file(*INFO_HANDLE, $data);
+		@result = write_info_file(*INFO_HANDLE, $data);
 		close(*INFO_HANDLE);
 	}
 	else
 	{
-		write_info_file(*STDOUT, $data);
+		@result = write_info_file(*STDOUT, $data);
 	}
+
+	return @result;
 }
 
 
+# get_prefix(max_width, max_percentage_too_long, path_list)
+#
+# Return a path prefix that satisfies the following requirements:
+# - is shared by more paths in path_list than any other prefix
+# - the percentage of paths which would exceed the given max_width length
+#   after applying the prefix does not exceed max_percentage_too_long
+#
+# If multiple prefixes satisfy all requirements, the longest prefix is
+# returned. Return an empty string if no prefix could be found.
+
+sub get_prefix($$@)
+{
+	my ($max_width, $max_long, @path_list) = @_;
+	my $path;
+	my $ENTRY_NUM = 0;
+	my $ENTRY_LONG = 1;
+	my %prefix;
+
+	# Build prefix hash
+	foreach $path (@path_list) {
+		my ($v, $d, $f) = splitpath($path);
+		my @dirs = splitdir($d);
+		my $p_len = length($path);
+		my $i;
+
+		# Remove trailing '/'
+		pop(@dirs) if ($dirs[scalar(@dirs) - 1] eq '');
+		for ($i = 0; $i < scalar(@dirs); $i++) {
+			my $subpath = catpath($v, catdir(@dirs[0..$i]), '');
+			my $entry = $prefix{$subpath};
+
+			$entry = [ 0, 0 ] if (!defined($entry));
+			$entry->[$ENTRY_NUM]++;
+			if (($p_len - length($subpath) - 1) > $max_width) {
+				$entry->[$ENTRY_LONG]++;
+			}
+			$prefix{$subpath} = $entry;
+		}
+	}
+	# Find suitable prefix (sort descending by two keys: 1. number of
+	# entries covered by a prefix, 2. length of prefix)
+	foreach $path (sort {($prefix{$a}->[$ENTRY_NUM] ==
+			      $prefix{$b}->[$ENTRY_NUM]) ?
+				length($b) <=> length($a) :
+				$prefix{$b}->[$ENTRY_NUM] <=>
+				$prefix{$a}->[$ENTRY_NUM]}
+				keys(%prefix)) {
+		my ($num, $long) = @{$prefix{$path}};
+
+		# Check for additional requirement: number of filenames
+		# that would be too long may not exceed a certain percentage
+		if ($long <= $num * $max_long / 100) {
+			return $path;
+		}
+	}
+
+	return "";
+}
+
+
+#
+# shorten_filename(filename, width)
+#
+# Truncate filename if it is longer than width characters.
+#
+
+sub shorten_filename($$)
+{
+	my ($filename, $width) = @_;
+	my $l = length($filename);
+	my $s;
+	my $e;
+
+	return $filename if ($l <= $width);
+	$e = int(($width - 3) / 2);
+	$s = $width - 3 - $e;
+
+	return substr($filename, 0, $s).'...'.substr($filename, $l - $e);
+}
+
+
+sub shorten_number($$)
+{
+	my ($number, $width) = @_;
+	my $result = sprintf("%*d", $width, $number);
+
+	return $result if (length($result) <= $width);
+	$number = $number / 1000;
+	return $result if (length($result) <= $width);
+	$result = sprintf("%*dk", $width - 1, $number);
+	return $result if (length($result) <= $width);
+	$number = $number / 1000;
+	$result = sprintf("%*dM", $width - 1, $number);
+	return $result if (length($result) <= $width);
+	return '#';
+}
+
+sub shorten_rate($$$)
+{
+	my ($hit, $found, $width) = @_;
+	my $result = rate($hit, $found, "%", 1, $width);
+
+	return $result if (length($result) <= $width);
+	$result = rate($hit, $found, "%", 0, $width);
+	return $result if (length($result) <= $width);
+	return "#";
+}
+
 #
 # list()
 #
@@ -1847,17 +2925,250 @@
 	my $found;
 	my $hit;
 	my $entry;
+	my $fn_found;
+	my $fn_hit;
+	my $br_found;
+	my $br_hit;
+	my $total_found = 0;
+	my $total_hit = 0;
+	my $fn_total_found = 0;
+	my $fn_total_hit = 0;
+	my $br_total_found = 0;
+	my $br_total_hit = 0;
+	my $prefix;
+	my $strlen = length("Filename");
+	my $format;
+	my $heading1;
+	my $heading2;
+	my @footer;
+	my $barlen;
+	my $rate;
+	my $fnrate;
+	my $brrate;
+	my $lastpath;
+	my $F_LN_NUM = 0;
+	my $F_LN_RATE = 1;
+	my $F_FN_NUM = 2;
+	my $F_FN_RATE = 3;
+	my $F_BR_NUM = 4;
+	my $F_BR_RATE = 5;
+	my @fwidth_narrow = (5, 5, 3, 5, 4, 5);
+	my @fwidth_wide = (6, 5, 5, 5, 6, 5);
+	my @fwidth = @fwidth_wide;
+	my $w;
+	my $max_width = $opt_list_width;
+	my $max_long = $opt_list_truncate_max;
+	my $fwidth_narrow_length;
+	my $fwidth_wide_length;
+	my $got_prefix = 0;
+	my $root_prefix = 0;
 
-	info("Listing contents of $list:\n");
+	# Calculate total width of narrow fields
+	$fwidth_narrow_length = 0;
+	foreach $w (@fwidth_narrow) {
+		$fwidth_narrow_length += $w + 1;
+	}
+	# Calculate total width of wide fields
+	$fwidth_wide_length = 0;
+	foreach $w (@fwidth_wide) {
+		$fwidth_wide_length += $w + 1;
+	}
+	# Get common file path prefix
+	$prefix = get_prefix($max_width - $fwidth_narrow_length, $max_long,
+			     keys(%{$data}));
+	$root_prefix = 1 if ($prefix eq rootdir());
+	$got_prefix = 1 if (length($prefix) > 0);
+	$prefix =~ s/\/$//;
+	# Get longest filename length
+	foreach $filename (keys(%{$data})) {
+		if (!$opt_list_full_path) {
+			if (!$got_prefix || !$root_prefix &&
+			    !($filename =~ s/^\Q$prefix\/\E//)) {
+				my ($v, $d, $f) = splitpath($filename);
 
-	# List all files
+				$filename = $f;
+			}
+		}
+		# Determine maximum length of entries
+		if (length($filename) > $strlen) {
+			$strlen = length($filename)
+		}
+	}
+	if (!$opt_list_full_path) {
+		my $blanks;
+
+		$w = $fwidth_wide_length;
+		# Check if all columns fit into max_width characters
+		if ($strlen + $fwidth_wide_length > $max_width) {
+			# Use narrow fields
+			@fwidth = @fwidth_narrow;
+			$w = $fwidth_narrow_length;
+			if (($strlen + $fwidth_narrow_length) > $max_width) {
+				# Truncate filenames at max width
+				$strlen = $max_width - $fwidth_narrow_length;
+			}
+		}
+		# Add some blanks between filename and fields if possible
+		$blanks = int($strlen * 0.5);
+		$blanks = 4 if ($blanks < 4);
+		$blanks = 8 if ($blanks > 8);
+		if (($strlen + $w + $blanks) < $max_width) {
+			$strlen += $blanks;
+		} else {
+			$strlen = $max_width - $w;
+		}
+	}
+	# Filename
+	$w = $strlen;
+	$format		= "%-${w}s|";
+	$heading1 	= sprintf("%*s|", $w, "");
+	$heading2 	= sprintf("%-*s|", $w, "Filename");
+	$barlen		= $w + 1;
+	# Line coverage rate
+	$w = $fwidth[$F_LN_RATE];
+	$format		.= "%${w}s ";
+	$heading1 	.= sprintf("%-*s |", $w + $fwidth[$F_LN_NUM],
+				   "Lines");
+	$heading2 	.= sprintf("%-*s ", $w, "Rate");
+	$barlen		+= $w + 1;
+	# Number of lines
+	$w = $fwidth[$F_LN_NUM];
+	$format		.= "%${w}s|";
+	$heading2	.= sprintf("%*s|", $w, "Num");
+	$barlen		+= $w + 1;
+	# Function coverage rate
+	$w = $fwidth[$F_FN_RATE];
+	$format		.= "%${w}s ";
+	$heading1 	.= sprintf("%-*s|", $w + $fwidth[$F_FN_NUM] + 1,
+				   "Functions");
+	$heading2 	.= sprintf("%-*s ", $w, "Rate");
+	$barlen		+= $w + 1;
+	# Number of functions
+	$w = $fwidth[$F_FN_NUM];
+	$format		.= "%${w}s|";
+	$heading2	.= sprintf("%*s|", $w, "Num");
+	$barlen		+= $w + 1;
+	# Branch coverage rate
+	$w = $fwidth[$F_BR_RATE];
+	$format		.= "%${w}s ";
+	$heading1 	.= sprintf("%-*s", $w + $fwidth[$F_BR_NUM] + 1,
+				   "Branches");
+	$heading2 	.= sprintf("%-*s ", $w, "Rate");
+	$barlen		+= $w + 1;
+	# Number of branches
+	$w = $fwidth[$F_BR_NUM];
+	$format		.= "%${w}s";
+	$heading2	.= sprintf("%*s", $w, "Num");
+	$barlen		+= $w;
+	# Line end
+	$format		.= "\n";
+	$heading1	.= "\n";
+	$heading2	.= "\n";
+
+	# Print heading
+	print($heading1);
+	print($heading2);
+	print(("="x$barlen)."\n");
+
+	# Print per file information
 	foreach $filename (sort(keys(%{$data})))
 	{
+		my @file_data;
+		my $print_filename = $filename;
+
 		$entry = $data->{$filename};
-		(undef, undef, undef, undef, undef, undef, $found, $hit) =
+		if (!$opt_list_full_path) {
+			my $p;
+
+			$print_filename = $filename;
+			if (!$got_prefix || !$root_prefix &&
+			    !($print_filename =~ s/^\Q$prefix\/\E//)) {
+				my ($v, $d, $f) = splitpath($filename);
+
+				$p = catpath($v, $d, "");
+				$p =~ s/\/$//;
+				$print_filename = $f;
+			} else {
+				$p = $prefix;
+			}
+
+			if (!defined($lastpath) || $lastpath ne $p) {
+				print("\n") if (defined($lastpath));
+				$lastpath = $p;
+				print("[$lastpath/]\n") if (!$root_prefix);
+			}
+			$print_filename = shorten_filename($print_filename,
+							   $strlen);
+		}
+
+		(undef, undef, undef, undef, undef, undef, undef, undef,
+		 $found, $hit, $fn_found, $fn_hit, $br_found, $br_hit) =
 			get_info_entry($entry);
-		printf("$filename: $hit of $found lines hit\n");
+
+		# Assume zero count if there is no function data for this file
+		if (!defined($fn_found) || !defined($fn_hit)) {
+			$fn_found = 0;
+			$fn_hit = 0;
+		}
+		# Assume zero count if there is no branch data for this file
+		if (!defined($br_found) || !defined($br_hit)) {
+			$br_found = 0;
+			$br_hit = 0;
+		}
+
+		# Add line coverage totals
+		$total_found += $found;
+		$total_hit += $hit;
+		# Add function coverage totals
+		$fn_total_found += $fn_found;
+		$fn_total_hit += $fn_hit;
+		# Add branch coverage totals
+		$br_total_found += $br_found;
+		$br_total_hit += $br_hit;
+
+		# Determine line coverage rate for this file
+		$rate = shorten_rate($hit, $found, $fwidth[$F_LN_RATE]);
+		# Determine function coverage rate for this file
+		$fnrate = shorten_rate($fn_hit, $fn_found, $fwidth[$F_FN_RATE]);
+		# Determine branch coverage rate for this file
+		$brrate = shorten_rate($br_hit, $br_found, $fwidth[$F_BR_RATE]);
+
+		# Assemble line parameters
+		push(@file_data, $print_filename);
+		push(@file_data, $rate);
+		push(@file_data, shorten_number($found, $fwidth[$F_LN_NUM]));
+		push(@file_data, $fnrate);
+		push(@file_data, shorten_number($fn_found, $fwidth[$F_FN_NUM]));
+		push(@file_data, $brrate);
+		push(@file_data, shorten_number($br_found, $fwidth[$F_BR_NUM]));
+
+		# Print assembled line
+		printf($format, @file_data);
 	}
+
+	# Determine total line coverage rate
+	$rate = shorten_rate($total_hit, $total_found, $fwidth[$F_LN_RATE]);
+	# Determine total function coverage rate
+	$fnrate = shorten_rate($fn_total_hit, $fn_total_found,
+			       $fwidth[$F_FN_RATE]);
+	# Determine total branch coverage rate
+	$brrate = shorten_rate($br_total_hit, $br_total_found,
+			       $fwidth[$F_BR_RATE]);
+
+	# Print separator
+	print(("="x$barlen)."\n");
+
+	# Assemble line parameters
+	push(@footer, sprintf("%*s", $strlen, "Total:"));
+	push(@footer, $rate);
+	push(@footer, shorten_number($total_found, $fwidth[$F_LN_NUM]));
+	push(@footer, $fnrate);
+	push(@footer, shorten_number($fn_total_found, $fwidth[$F_FN_NUM]));
+	push(@footer, $brrate);
+	push(@footer, shorten_number($br_total_found, $fwidth[$F_BR_NUM]));
+
+	# Print assembled line
+	printf($format, @footer);
 }
 
 
@@ -1981,14 +3292,14 @@
 				"compressed file $diff_file!\n");
 
 		# Open compressed file
-		open(HANDLE, "gunzip -c $diff_file|")
+		open(HANDLE, "-|", "gunzip -c '$diff_file'")
 			or die("ERROR: cannot start gunzip to decompress ".
 			       "file $_[0]!\n");
 	}
 	else
 	{
 		# Open decompressed file
-		open(HANDLE, $diff_file)
+		open(HANDLE, "<", $diff_file)
 			or die("ERROR: cannot read file $_[0]!\n");
 	}
 
@@ -2155,6 +3466,28 @@
 
 
 #
+# apply_diff_to_brcount(brcount, linedata)
+#
+# Adjust line numbers of branch coverage data according to linedata.
+#
+
+sub apply_diff_to_brcount($$)
+{
+	my ($brcount, $linedata) = @_;
+	my $db;
+
+	# Convert brcount to db format
+	$db = brcount_to_db($brcount);
+	# Apply diff to db format
+	$db = apply_diff($db, $linedata);
+	# Convert db format back to brcount format
+	($brcount) = db_to_brcount($db);
+
+	return $brcount;
+}
+
+
+#
 # get_hash_max(hash_ref)
 #
 # Return the highest integer key from hash.
@@ -2235,10 +3568,16 @@
 	my $old_depth;
 	my $new_depth;
 
+	# Remove trailing slash from diff path
+	$diff_path =~ s/\/$//;
 	foreach (keys(%{$diff_data}))
 	{
+		my $sep = "";
+
+		$sep = '/' if (!/^\//);
+
 		# Try to match diff filename with filename
-		if ($filename =~ /^\Q$diff_path\E\/$_$/)
+		if ($filename =~ /^\Q$diff_path$sep$_\E$/)
 		{
 			if ($diff_name)
 			{
@@ -2432,12 +3771,17 @@
 	my $checkdata;
 	my $testfncdata;
 	my $sumfnccount;
+	my $testbrdata;
+	my $sumbrcount;
 	my $found;
 	my $hit;
 	my $f_found;
 	my $f_hit;
+	my $br_found;
+	my $br_hit;
 	my $converted = 0;
 	my $unchanged = 0;
+	my @result;
 	local *INFO_HANDLE;
 
 	($diff_data, $path_data) = read_diff($ARGV[0]);
@@ -2468,17 +3812,24 @@
 		info("Converting $filename\n");
 		$entry = $trace_data->{$filename};
 		($testdata, $sumcount, $funcdata, $checkdata, $testfncdata,
-		 $sumfnccount) = get_info_entry($entry);
+		 $sumfnccount, $testbrdata, $sumbrcount) =
+			get_info_entry($entry);
 		# Convert test data
 		foreach $testname (keys(%{$testdata}))
 		{
+			# Adjust line numbers of line coverage data
 			$testdata->{$testname} =
 				apply_diff($testdata->{$testname}, $line_hash);
+			# Adjust line numbers of branch coverage data
+			$testbrdata->{$testname} =
+				apply_diff_to_brcount($testbrdata->{$testname},
+						      $line_hash);
 			# Remove empty sets of test data
 			if (scalar(keys(%{$testdata->{$testname}})) == 0)
 			{
 				delete($testdata->{$testname});
 				delete($testfncdata->{$testname});
+				delete($testbrdata->{$testname});
 			}
 		}
 		# Rename test data to indicate conversion
@@ -2502,6 +3853,12 @@
 					$testfncdata->{$testname},
 					$testfncdata->{$testname.",diff"});
 				delete($testfncdata->{$testname.",diff"});
+				# Add branch counts
+				($testbrdata->{$testname}) = combine_brcount(
+					$testbrdata->{$testname},
+					$testbrdata->{$testname.",diff"},
+					$BR_ADD);
+				delete($testbrdata->{$testname.",diff"});
 			}
 			# Move test data to new testname
 			$testdata->{$testname.",diff"} = $testdata->{$testname};
@@ -2510,16 +3867,24 @@
 			$testfncdata->{$testname.",diff"} =
 				$testfncdata->{$testname};
 			delete($testfncdata->{$testname});
+			# Move branch count data to new testname
+			$testbrdata->{$testname.",diff"} =
+				$testbrdata->{$testname};
+			delete($testbrdata->{$testname});
 		}
 		# Convert summary of test data
 		$sumcount = apply_diff($sumcount, $line_hash);
 		# Convert function data
 		$funcdata = apply_diff_to_funcdata($funcdata, $line_hash);
+		# Convert branch coverage data
+		$sumbrcount = apply_diff_to_brcount($sumbrcount, $line_hash);
+		# Update found/hit numbers
 		# Convert checksum data
 		$checkdata = apply_diff($checkdata, $line_hash);
 		# Convert function call count data
 		adjust_fncdata($funcdata, $testfncdata, $sumfnccount);
 		($f_found, $f_hit) = get_func_found_and_hit($sumfnccount);
+		($br_found, $br_hit) = get_br_found_and_hit($sumbrcount);
 		# Update found/hit numbers
 		$found = 0;
 		$hit = 0;
@@ -2536,7 +3901,8 @@
 			# Store converted entry
 			set_info_entry($entry, $testdata, $sumcount, $funcdata,
 				       $checkdata, $testfncdata, $sumfnccount,
-				       $found, $hit, $f_found, $f_hit);
+				       $testbrdata, $sumbrcount, $found, $hit,
+				       $f_found, $f_hit, $br_found, $br_hit);
 		}
 		else
 		{
@@ -2559,17 +3925,72 @@
 	if ($to_file)
 	{
 		info("Writing data to $output_filename\n");
-		open(INFO_HANDLE, ">$output_filename")
+		open(INFO_HANDLE, ">", $output_filename)
 			or die("ERROR: cannot write to $output_filename!\n");
-		write_info_file(*INFO_HANDLE, $trace_data);
+		@result = write_info_file(*INFO_HANDLE, $trace_data);
 		close(*INFO_HANDLE);
 	}
 	else
 	{
-		write_info_file(*STDOUT, $trace_data);
+		@result = write_info_file(*STDOUT, $trace_data);
 	}
+
+	return @result;
 }
 
+#
+# summary()
+#
+
+sub summary()
+{
+	my $filename;
+	my $current;
+	my $total;
+	my $ln_total_found;
+	my $ln_total_hit;
+	my $fn_total_found;
+	my $fn_total_hit;
+	my $br_total_found;
+	my $br_total_hit;
+
+	# Read and combine trace files
+	foreach $filename (@opt_summary) {
+		$current = read_info_file($filename);
+		if (!defined($total)) {
+			$total = $current;
+		} else {
+			$total = combine_info_files($total, $current);
+		}
+	}
+	# Calculate coverage data
+	foreach $filename (keys(%{$total}))
+	{
+		my $entry = $total->{$filename};
+		my $ln_found;
+		my $ln_hit;
+		my $fn_found;
+		my $fn_hit;
+		my $br_found;
+		my $br_hit;
+
+		(undef, undef, undef, undef, undef, undef, undef, undef,
+			$ln_found, $ln_hit, $fn_found, $fn_hit, $br_found,
+			$br_hit) = get_info_entry($entry);
+
+		# Add to totals
+		$ln_total_found	+= $ln_found;
+		$ln_total_hit	+= $ln_hit;
+		$fn_total_found += $fn_found;
+		$fn_total_hit	+= $fn_hit;
+		$br_total_found += $br_found;
+		$br_total_hit	+= $br_hit;
+	}
+
+
+	return ($ln_total_found, $ln_total_hit, $fn_total_found, $fn_total_hit,
+		$br_total_found, $br_total_hit);
+}
 
 #
 # system_no_output(mode, parameters)
@@ -2591,12 +4012,12 @@
 	local *OLD_STDOUT;
 
 	# Save old stdout and stderr handles
-	($mode & 1) && open(OLD_STDOUT, ">>&STDOUT");
-	($mode & 2) && open(OLD_STDERR, ">>&STDERR");
+	($mode & 1) && open(OLD_STDOUT, ">>&", "STDOUT");
+	($mode & 2) && open(OLD_STDERR, ">>&", "STDERR");
 
 	# Redirect to /dev/null
-	($mode & 1) && open(STDOUT, ">/dev/null");
-	($mode & 2) && open(STDERR, ">/dev/null");
+	($mode & 1) && open(STDOUT, ">", "/dev/null");
+	($mode & 2) && open(STDERR, ">", "/dev/null");
  
 	system(@_);
 	$result = $?;
@@ -2606,8 +4027,8 @@
 	($mode & 2) && close(STDERR);
 
 	# Restore old handles
-	($mode & 1) && open(STDOUT, ">>&OLD_STDOUT");
-	($mode & 2) && open(STDERR, ">>&OLD_STDERR");
+	($mode & 1) && open(STDOUT, ">>&", "OLD_STDOUT");
+	($mode & 2) && open(STDERR, ">>&", "OLD_STDERR");
  
 	return $result;
 }
@@ -2628,7 +4049,7 @@
 	my $value;
 	local *HANDLE;
 
-	if (!open(HANDLE, "<$filename"))
+	if (!open(HANDLE, "<", $filename))
 	{
 		warn("WARNING: cannot read configuration file $filename\n");
 		return undef;
@@ -2667,8 +4088,8 @@
 #   key_string => var_ref
 #
 # where KEY_STRING is a keyword and VAR_REF is a reference to an associated
-# variable. If the global configuration hash CONFIG contains a value for
-# keyword KEY_STRING, VAR_REF will be assigned the value for that keyword. 
+# variable. If the global configuration hashes CONFIG or OPT_RC contain a value
+# for keyword KEY_STRING, VAR_REF will be assigned the value for that keyword. 
 #
 
 sub apply_config($)
@@ -2677,8 +4098,9 @@
 
 	foreach (keys(%{$ref}))
 	{
-		if (defined($config->{$_}))
-		{
+		if (defined($opt_rc{$_})) {
+			${$ref->{$_}} = $opt_rc{$_};
+		} elsif (defined($config->{$_})) {
 			${$ref->{$_}} = $config->{$_};
 		}
 	}
@@ -2688,6 +4110,7 @@
 {
 	my ($msg) = @_;
 
+	temp_cleanup();
 	warn("$tool_name: $msg");
 }
 
@@ -2695,5 +4118,186 @@
 {
 	my ($msg) = @_;
 
+	temp_cleanup();
 	die("$tool_name: $msg");
 }
+
+sub abort_handler($)
+{
+	temp_cleanup();
+	exit(1);
+}
+
+sub temp_cleanup()
+{
+	if (@temp_dirs) {
+		info("Removing temporary directories.\n");
+		foreach (@temp_dirs) {
+			rmtree($_);
+		}
+		@temp_dirs = ();
+	}
+}
+
+sub setup_gkv_sys()
+{
+	system_no_output(3, "mount", "-t", "debugfs", "nodev",
+			 "/sys/kernel/debug");
+}
+
+sub setup_gkv_proc()
+{
+	if (system_no_output(3, "modprobe", "gcov_proc")) {
+		system_no_output(3, "modprobe", "gcov_prof");
+	}
+}
+
+sub check_gkv_sys($)
+{
+	my ($dir) = @_;
+
+	if (-e "$dir/reset") {
+		return 1;
+	}
+	return 0;
+}
+
+sub check_gkv_proc($)
+{
+	my ($dir) = @_;
+
+	if (-e "$dir/vmlinux") {
+		return 1;
+	}
+	return 0;
+}
+
+sub setup_gkv()
+{
+	my $dir;
+	my $sys_dir = "/sys/kernel/debug/gcov";
+	my $proc_dir = "/proc/gcov";
+	my @todo;
+
+	if (!defined($gcov_dir)) {
+		info("Auto-detecting gcov kernel support.\n");
+		@todo = ( "cs", "cp", "ss", "cs", "sp", "cp" );
+	} elsif ($gcov_dir =~ /proc/) {
+		info("Checking gcov kernel support at $gcov_dir ".
+		     "(user-specified).\n");
+		@todo = ( "cp", "sp", "cp", "cs", "ss", "cs");
+	} else {
+		info("Checking gcov kernel support at $gcov_dir ".
+		     "(user-specified).\n");
+		@todo = ( "cs", "ss", "cs", "cp", "sp", "cp", );
+	}
+	foreach (@todo) {
+		if ($_ eq "cs") {
+			# Check /sys
+			$dir = defined($gcov_dir) ? $gcov_dir : $sys_dir;
+			if (check_gkv_sys($dir)) {
+				info("Found ".$GKV_NAME[$GKV_SYS]." gcov ".
+				     "kernel support at $dir\n");
+				return ($GKV_SYS, $dir);
+			}
+		} elsif ($_ eq "cp") {
+			# Check /proc
+			$dir = defined($gcov_dir) ? $gcov_dir : $proc_dir;
+			if (check_gkv_proc($dir)) {
+				info("Found ".$GKV_NAME[$GKV_PROC]." gcov ".
+				     "kernel support at $dir\n");
+				return ($GKV_PROC, $dir);
+			}
+		} elsif ($_ eq "ss") {
+			# Setup /sys
+			setup_gkv_sys();
+		} elsif ($_ eq "sp") {
+			# Setup /proc
+			setup_gkv_proc();
+		}
+	}
+	if (defined($gcov_dir)) {
+		die("ERROR: could not find gcov kernel data at $gcov_dir\n");
+	} else {
+		die("ERROR: no gcov kernel data found\n");
+	}
+}
+
+
+#
+# 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 rate($hit, $found, "% ($hit of $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("Summary 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);
+}
+
+
+#
+# rate(hit, found[, suffix, precision, width])
+#
+# Return the coverage rate [0..100] for HIT and FOUND values. 0 is only
+# returned when HIT is 0. 100 is only returned when HIT equals FOUND.
+# PRECISION specifies the precision of the result. SUFFIX defines a
+# string that is appended to the result if FOUND is non-zero. Spaces
+# are added to the start of the resulting string until it is at least WIDTH
+# characters wide.
+#
+
+sub rate($$;$$$)
+{
+        my ($hit, $found, $suffix, $precision, $width) = @_;
+        my $rate; 
+
+	# Assign defaults if necessary
+        $precision	= 1	if (!defined($precision));
+	$suffix		= ""	if (!defined($suffix));
+	$width		= 0	if (!defined($width));
+        
+        return sprintf("%*s", $width, "-") if (!defined($found) || $found == 0);
+        $rate = sprintf("%.*f", $precision, $hit * 100 / $found);
+
+	# Adjust rates if necessary
+        if ($rate == 0 && $hit > 0) {
+		$rate = sprintf("%.*f", $precision, 1 / 10 ** $precision);
+        } elsif ($rate == 100 && $hit != $found) {
+		$rate = sprintf("%.*f", $precision, 100 - 1 / 10 ** $precision);
+	}
+
+	return sprintf("%*s", $width, $rate.$suffix);
+}
diff --git a/bin/updateversion.pl b/bin/updateversion.pl
index 49f639a..55f2bc1 100755
--- a/bin/updateversion.pl
+++ b/bin/updateversion.pl
@@ -93,7 +93,7 @@
 	open(OUT, ">$filename.new") ||
 		die("Error: cannot create $filename.new\n");
 	while (<IN>) {
-		s/(our\s+\$lcov_version\s*=\s*\").*(\".*)$/$1LCOV version $version$2/g;
+		s/(our\s+\$lcov_version\s*=\s*["']).*(["'].*)$/$1LCOV version $version$2/g;
 		print(OUT $_);
 	}
 	close(OUT);
diff --git a/example/Makefile b/example/Makefile
index 5428237..2f698a1 100644
--- a/example/Makefile
+++ b/example/Makefile
@@ -70,7 +70,7 @@
 	@echo
 	$(LCOV) --zerocounters --directory .
 	./example
-	$(LCOV) --capture --directory . --output-file trace_noargs.info --test-name test_noargs
+	$(LCOV) --capture --directory . --output-file trace_noargs.info --test-name test_noargs --no-external
 
 test_2_to_2000:
 	@echo
@@ -80,7 +80,7 @@
 	@echo
 	$(LCOV) --zerocounters --directory .
 	./example 2 2000
-	$(LCOV) --capture --directory . --output-file trace_args.info --test-name test_2_to_2000
+	$(LCOV) --capture --directory . --output-file trace_args.info --test-name test_2_to_2000 --no-external
 
 test_overflow:
 	@echo
@@ -90,7 +90,7 @@
 	@echo
 	$(LCOV) --zerocounters --directory .
 	./example 0 100000 || true
-	$(LCOV) --capture --directory . --output-file trace_overflow.info --test-name "test_overflow"
+	$(LCOV) --capture --directory . --output-file trace_overflow.info --test-name "test_overflow" --no-external
 
 clean:
 	rm -rf *.o *.bb *.bbg *.da *.gcno *.gcda *.info output example \
diff --git a/lcovrc b/lcovrc
index b0e511a..9c65970 100644
--- a/lcovrc
+++ b/lcovrc
@@ -12,14 +12,14 @@
 # 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
+genhtml_hi_limit = 90
+genhtml_med_limit = 75
 
-# For line coverage
-genhtml_hi_limit = 50
-genhtml_med_limit = 15
+# Width of line coverage field in source code view
+genhtml_line_field_width = 12
 
-# For function coverage
-genhtml_function_hi_limit = 90
-genhtml_function_med_limit = 75
+# Width of branch coverage field in source code view
+genhtml_branch_field_width = 16
 
 # Width of overview image (used by --frames option of genhtml)
 genhtml_overview_width = 80
@@ -81,7 +81,14 @@
 
 # Include function coverage data display (can be disabled by the
 # --no-func-coverage option of genhtml)
-genhtml_function_coverage = 1
+#genhtml_function_coverage = 1
+
+# Include branch coverage data display (can be disabled by the
+# --no-branch-coverage option of genhtml)
+#genhtml_branch_coverage = 1
+
+# Specify the character set of all generated HTML pages
+genhtml_charset=UTF-8
 
 # Location of the gcov tool (same as --gcov-info option of geninfo)
 #geninfo_gcov_tool = gcov
@@ -93,12 +100,30 @@
 # option of geninfo if non-zero, same as --no-checksum if zero)
 #geninfo_checksum = 1
 
+# Specify whether to capture coverage data for external source files (can
+# be overridden by the --external and --no-external options of geninfo/lcov)
+#geninfo_external = 1
+
 # Enable libtool compatibility mode if non-zero (same as --compat-libtool option
 # of geninfo if non-zero, same as --no-compat-libtool if zero)
 #geninfo_compat_libtool = 0
 
+# Use gcov's --all-blocks option if non-zero
+#geninfo_gcov_all_blocks = 1
+
+# Specify compatiblity modes (same as --compat option of geninfo).
+#geninfo_compat = libtool=on, hammer=auto, split_crc=auto
+
+# Adjust path to source files by removing or changing path components that
+# match the specified pattern (Perl regular expression format)
+#geninfo_adjust_src_path = /tmp/build => /usr/src
+
+# Specify if geninfo should try to automatically determine the base-directory
+# when collecting coverage data.
+geninfo_auto_base = 1
+
 # Directory containing gcov kernel files
-lcov_gcov_dir = /proc/gcov
+# lcov_gcov_dir = /proc/gcov
 
 # Location of the insmod tool
 lcov_insmod_tool = /sbin/insmod
@@ -111,3 +136,22 @@
 
 # Location for temporary directories
 lcov_tmp_dir = /tmp
+
+# Show full paths during list operation if non-zero (same as --list-full-path
+# option of lcov)
+lcov_list_full_path = 0
+
+# Specify the maximum width for list output. This value is ignored when
+# lcov_list_full_path is non-zero.
+lcov_list_width = 80
+
+# Specify the maximum percentage of file names which may be truncated when
+# choosing a directory prefix in list output. This value is ignored when
+# lcov_list_full_path is non-zero.
+lcov_list_truncate_max = 20
+
+# Specify if function coverage data should be collected and processed.
+lcov_function_coverage = 1
+
+# Specify if branch coverage data should be collected and processed.
+lcov_branch_coverage = 0
diff --git a/man/gendesc.1 b/man/gendesc.1
index e1fb38a..d1c5382 100644
--- a/man/gendesc.1
+++ b/man/gendesc.1
@@ -1,4 +1,4 @@
-.TH gendesc 1 "LCOV 1.7" 2008\-11\-17 "User Manuals"
+.TH gendesc 1 "LCOV 1.10" 2012\-10\-10 "User Manuals"
 .SH NAME
 gendesc \- Generate a test case description file
 .SH SYNOPSIS
diff --git a/man/genhtml.1 b/man/genhtml.1
index e99c642..9788323 100644
--- a/man/genhtml.1
+++ b/man/genhtml.1
@@ -1,4 +1,4 @@
-.TH genhtml 1 "LCOV 1.7" 2008\-11\-17 "User Manuals"
+.TH genhtml 1 "LCOV 1.10" 2012\-10\-10 "User Manuals"
 .SH NAME
 genhtml \- Generate HTML view from LCOV coverage data files
 .SH SYNOPSIS
@@ -52,6 +52,18 @@
 .RB [ \-\-function\-coverage ]
 .RB [ \-\-no\-function\-coverage ]
 .br
+.RB [ \-\-branch\-coverage ]
+.RB [ \-\-no\-branch\-coverage ]
+.br
+.RB [ \-\-demangle\-cpp ]
+.RB [ \-\-ignore\-errors
+.IR errors  ]
+.br
+.RB [ \-\-config\-file
+.IR config\-file ]
+.RB [ \-\-rc
+.IR keyword = value ]
+.br
 .IR tracefile(s)
 .RE
 .SH DESCRIPTION
@@ -75,10 +87,17 @@
 to modify layout and colors of the generated HTML output. Files are
 marked in different colors depending on the associated coverage rate. By
 default, the coverage limits for low, medium and high coverage are set to
-0\-15%, 15\-50% and 50\-100% percent respectively. To change these
+0\-75%, 75\-90% and 90\-100% percent respectively. To change these
 values, use configuration file options
 .IR genhtml_hi_limit " and " genhtml_med_limit .
 
+Also note that when displaying percentages, 0% and 100% are only printed when
+the values are exactly 0% and 100% respectively. Other values which would
+conventionally be rounded to 0% or 100% are instead printed as nearest
+non-boundary value. This behavior is in accordance with that of the
+.BR gcov (1)
+tool.
+
 .SH OPTIONS
 .B \-h
 .br
@@ -113,7 +132,7 @@
 If enabled, a frameset is created for each source code file, providing
 an overview of the source code as a "clickable" image. Note that this
 option will slow down output creation noticeably because each source
-code character has to be inspected once. Note also that the GD.pm PERL
+code character has to be inspected once. Note also that the GD.pm Perl
 module has to be installed for this option to work (it may be obtained
 from http://www.cpan.org).
 
@@ -369,7 +388,6 @@
 .RE
 .BI "\-\-html\-extension " extension
 .RS
-
 Use customized filename extension for generated HTML pages.
 
 This option is useful in situations where different filename extensions
@@ -384,7 +402,6 @@
 
 .B \-\-html\-gzip
 .RS
-
 Compress all generated html files with gzip and add a .htaccess file specifying
 gzip\-encoding in the root output directory.
 
@@ -438,6 +455,96 @@
 .IR genhtml_function_coverage .
 
 .RE
+.B \-\-branch\-coverage
+.br
+.B \-\-no\-branch\-coverage
+.RS
+Specify whether to display branch coverage data in HTML output.
+
+Use \-\-branch\-coverage to enable branch coverage display or
+\-\-no\-branch\-coverage to disable it. Branch coverage data display is
+.B enabled
+by default
+
+When branch coverage display is enabled, each overview page will contain
+the number of branches found and hit per file or directory, together with
+the resulting coverage rate. In addition, each source code view will contain
+an extra column which lists all branches of a line with indications of
+whether the branch was taken or not. Branches are shown in the following format:
+
+ ' + ': Branch was taken at least once
+.br
+ ' - ': Branch was not taken
+.br
+ ' # ': The basic block containing the branch was never executed
+.br
+
+Note that it might not always be possible to relate branches to the
+corresponding source code statements: during compilation, GCC might shuffle
+branches around or eliminate some of them to generate better code.
+
+This option can also be configured permanently using the configuration file
+option
+.IR genhtml_branch_coverage .
+
+.RE
+.B \-\-demangle\-cpp
+.RS
+Specify whether to demangle C++ function names.
+
+Use this option if you want to convert C++ internal function names to
+human readable format for display on the HTML function overview page.
+This option requires that the c++filt tool is installed (see
+.BR c++filt (1)).
+
+.RE
+.B \-\-ignore\-errors
+.I errors
+.br
+.RS
+Specify a list of errors after which to continue processing.
+
+Use this option to specify a list of one or more classes of errors after which
+geninfo should continue processing instead of aborting.
+
+.I errors
+can be a comma\-separated list of the following keywords:
+
+.B source:
+the source code file for a data set could not be found.
+.RE
+
+.B \-\-config\-file
+.I config\-file
+.br
+.RS
+Specify a configuration file to use.
+
+When this option is specified, neither the system\-wide configuration file
+/etc/lcovrc, nor the per\-user configuration file ~/.lcovrc is read.
+
+This option may be useful when there is a need to run several
+instances of
+.B genhtml
+with different configuration file options in parallel.
+.RE
+
+.B \-\-rc
+.IR keyword = value
+.br
+.RS
+Override a configuration directive.
+
+Use this option to specify a
+.IR keyword = value
+statement which overrides the corresponding configuration statement in
+the lcovrc configuration file. You can specify this option more than once
+to override multiple configuration statements.
+See
+.BR lcovrc (5)
+for a list of available keywords and their meaning.
+.RE
+
 .SH FILES
 
 .I /etc/lcovrc
@@ -455,6 +562,7 @@
 
 .SH SEE ALSO
 .BR lcov (1),
+.BR lcovrc (5),
 .BR geninfo (1),
 .BR genpng (1),
 .BR gendesc (1),
diff --git a/man/geninfo.1 b/man/geninfo.1
index 47fcfa3..d9ca97c 100644
--- a/man/geninfo.1
+++ b/man/geninfo.1
@@ -1,4 +1,4 @@
-.TH geninfo 1 "LCOV 1.7" 2008\-11\-17 "User Manuals"
+.TH geninfo 1 "LCOV 1.10" 2012\-10\-10 "User Manuals"
 .SH NAME
 geninfo \- Generate tracefiles from .da files
 .SH SYNOPSIS
@@ -32,6 +32,19 @@
 .br
 .RB [ \-\-no\-recursion ]
 .I directory
+.RB [ \-\-external ]
+.RB [ \-\-no\-external ]
+.br
+.RB [ \-\-config\-file
+.IR config\-file ]
+.RB [ \-\-no\-markers ]
+.br
+.RB [ \-\-derive\-func\-data ]
+.RB [ \-\-compat
+.IR  mode =on|off|auto]
+.br
+.RB [ \-\-rc
+.IR keyword = value ]
 .RE
 .SH DESCRIPTION
 .B geninfo 
@@ -57,6 +70,33 @@
 is called from within
 .BR lcov ,
 so that there is usually no need to call it directly.
+
+.B Exclusion markers
+
+To exclude specific lines of code from a tracefile, you can add exclusion
+markers to the source code. Exclusion markers are keywords which can for
+example be added in the form of a comment.
+
+The following markers are recognized by geninfo:
+
+LCOV_EXCL_LINE
+.RS
+Lines containing this marker will be excluded.
+.br
+.RE
+LCOV_EXCL_START
+.RS
+Marks the beginning of an excluded section. The current line is part of this
+section.
+.br
+.RE
+LCOV_EXCL_STOP
+.RS
+Marks the end of an excluded section. The current line not part of this
+section.
+.RE
+.br
+
 .SH OPTIONS
 
 .B \-b
@@ -84,11 +124,10 @@
 directory in which the source code file is located.
 
 Note that this option will not work in environments where multiple base
-directories are used. In that case repeat the geninfo call for each base
-directory while using the \-\-ignore\-errors option to prevent geninfo from
-exiting when the first source code file could not be found. This way you can
-get partial coverage information for each base directory which can then be
-combined using the \-a option.
+directories are used. In that case use configuration file setting
+.B geninfo_auto_base=1
+(see
+.BR lcovrc (5)).
 .RE
 
 .B \-\-checksum
@@ -112,6 +151,73 @@
 to speed up coverage data processing and to reduce the size of tracefiles.
 .RE
 
+.B \-\-compat
+.IR mode = value [, mode = value ,...]
+.br
+.RS
+Set compatibility mode.
+
+Use \-\-compat to specify that geninfo should enable one or more compatibility
+modes when capturing coverage data. You can provide a comma-separated list
+of mode=value pairs to specify the values for multiple modes.
+
+Valid
+.I values
+are:
+
+.B on
+.RS
+Enable compatibility mode.
+.RE
+.B off
+.RS
+Disable compatibility mode.
+.RE
+.B auto
+.RS
+Apply auto-detection to determine if compatibility mode is required. Note that
+auto-detection is not available for all compatibility modes.
+.RE
+
+If no value is specified, 'on' is assumed as default value.
+
+Valid
+.I modes
+are:
+
+.B libtool
+.RS
+Enable this mode if you are capturing coverage data for a project that
+was built using the libtool mechanism. See also
+\-\-compat\-libtool.
+
+The default value for this setting is 'on'.
+
+.RE
+.B hammer
+.RS
+Enable this mode if you are capturing coverage data for a project that
+was built using a version of GCC 3.3 that contains a modification
+(hammer patch) of later GCC versions. You can identify a modified GCC 3.3
+by checking the build directory of your project for files ending in the
+extension '.bbg'. Unmodified versions of GCC 3.3 name these files '.bb'.
+
+The default value for this setting is 'auto'.
+
+.RE
+.B split_crc
+.RS
+Enable this mode if you are capturing coverage data for a project that
+was built using a version of GCC 4.6 that contains a modification
+(split function checksums) of later GCC versions. Typical error messages
+when running geninfo on coverage data produced by such GCC versions are
+\'out of memory' and 'reached unexpected end of file'.
+
+The default value for this setting is 'auto'
+.RE
+
+.RE
+
 .B \-\-compat\-libtool
 .br
 .B \-\-no\-compat\-libtool
@@ -132,6 +238,49 @@
 libtool, disable this option to prevent problems when capturing coverage data.
 .RE
 
+.B \-\-config\-file
+.I config\-file
+.br
+.RS
+Specify a configuration file to use.
+
+When this option is specified, neither the system\-wide configuration file
+/etc/lcovrc, nor the per\-user configuration file ~/.lcovrc is read.
+
+This option may be useful when there is a need to run several
+instances of
+.B geninfo
+with different configuration file options in parallel.
+.RE
+
+.B \-\-derive\-func\-data
+.br
+.RS
+Calculate function coverage data from line coverage data.
+
+Use this option to collect function coverage data, even if the version of the
+gcov tool installed on the test system does not provide this data. lcov will
+instead derive function coverage data from line coverage data and
+information about which lines belong to a function.
+.RE
+
+.B \-\-external
+.br
+.B \-\-no\-external
+.br
+.RS
+Specify whether to capture coverage data for external source files.
+
+External source files are files which are not located in one of the directories
+specified by \-\-directory or \-\-base\-directory. Use \-\-external to include
+external source files while capturing coverage data or \-\-no\-external to
+ignore this data.
+
+Data for external source files is
+.B included
+by default.
+.RE
+
 .B \-f
 .br
 .B \-\-follow
@@ -180,10 +329,20 @@
 
 Run geninfo with this option on the directories containing .bb, .bbg or .gcno
 files before running any test case. The result is a "baseline" coverage data
-file that contains zero coverage for every instrumented line. Combine this
-data file (using lcov \-a) with coverage data files captured after a test
-run to ensure that the percentage of total lines covered is correct even
-when not all source code files were loaded during the test.
+file that contains zero coverage for every instrumented line and function.
+Combine this data file (using lcov \-a) with coverage data files captured
+after a test run to ensure that the percentage of total lines covered is
+correct even when not all object code files were loaded during the test.
+
+Note: currently, the \-\-initial option does not generate branch coverage
+information.
+.RE
+
+.B \-\-no\-markers
+.br
+.RS
+Use this option if you want to get coverage data without regard to exclusion
+markers in the source code file.
 .RE
 
 .B \-\-no\-recursion
@@ -215,6 +374,22 @@
 only error or warning messages are printed.
 .RE
 
+.B \-\-rc
+.IR keyword = value
+.br
+.RS
+Override a configuration directive.
+
+Use this option to specify a
+.IR keyword = value
+statement which overrides the corresponding configuration statement in
+the lcovrc configuration file. You can specify this option more than once
+to override multiple configuration statements.
+See
+.BR lcovrc (5)
+for a list of available keywords and their meaning.
+.RE
+
 .BI "\-t " testname
 .br
 .BI "\-\-test\-name " testname
@@ -279,6 +454,19 @@
   FNF:<number of functions found>
   FNH:<number of function hit>
 
+Branch coverage information is stored which one line per branch:
+
+  BRDA:<line number>,<block number>,<branch number>,<taken>
+
+Block number and branch number are gcc internal IDs for the branch. Taken is
+either '-' if the basic block containing the branch was never executed or
+a number indicating how often that branch was taken.
+
+Branch coverage summaries are stored in two lines:
+
+  BRF:<number of branches found>
+  BRH:<number of branches hit>
+
 Then there is a list of execution counts for each instrumented line
 (i.e. a line which resulted in executable code):
 
@@ -317,6 +505,7 @@
 
 .SH SEE ALSO
 .BR lcov (1),
+.BR lcovrc (5),
 .BR genhtml (1),
 .BR genpng (1),
 .BR gendesc (1),
diff --git a/man/genpng.1 b/man/genpng.1
index 147a25d..5c13bd5 100644
--- a/man/genpng.1
+++ b/man/genpng.1
@@ -1,4 +1,4 @@
-.TH genpng 1 "LCOV 1.7" 2008\-11\-17 "User Manuals"
+.TH genpng 1 "LCOV 1.10" 2012\-10\-10 "User Manuals"
 .SH NAME
 genpng \- Generate an overview image from a source file
 .SH SYNOPSIS
@@ -23,7 +23,7 @@
 
 Note that the
 .I GD.pm
-PERL module has to be installed for this script to work
+Perl module has to be installed for this script to work
 (it may be obtained from
 .IR http://www.cpan.org ).
 
diff --git a/man/lcov.1 b/man/lcov.1
index aa30fb8..092d865 100644
--- a/man/lcov.1
+++ b/man/lcov.1
@@ -1,73 +1,186 @@
-.TH lcov 1 "LCOV 1.7" 2008\-11\-17 "User Manuals"
+.TH lcov 1 "LCOV 1.10" 2012\-10\-10 "User Manuals"
 .SH NAME
 lcov \- a graphical GCOV front\-end
 .SH SYNOPSIS
 .B lcov
-.RB [ \-h | \-\-help ]
-.RB [ \-v | \-\-version ]
-.RB [ \-q | \-\-quiet ]
+.BR \-c | \-\-capture
 .RS 5
 .br
-.RB [ \-z | \-\-zerocounters ]
-.RB [ \-c | \-\-capture ]
-.br
-.RB [ \-a | \-\-add\-tracefile
-.IR tracefile ]
-.br
-.RB [ \-e | \-\-extract
-.IR tracefile ]
-.br
-.RB [ \-r | \-\-remove
-.IR tracefile ]
-.br
-.RB [ \-l | \-\-list
-.IR tracefile ]
-.br
-.RB [ \-\-diff
-.IR "tracefile diff" ]
-.br
-.RB [ \-i | \-\-initial ]
-.RB [ \-t | \-\-test\-name
-.IR testname ]
-.br
-.RB [ \-o | \-\-output\-file
-.IR filename ]
-.br
 .RB [ \-d | \-\-directory
 .IR directory ]
-.br
-.RB [ \-f | \-\-follow ]
-.br
 .RB [ \-k | \-\-kernel\-directory
 .IR directory ]
 .br
+.RB [ \-o | \-\-output\-file
+.IR tracefile ]
+.RB [ \-t | \-\-test\-name
+.IR testname ]
+.br
 .RB [ \-b | \-\-base\-directory
 .IR directory ]
+.RB [ \-i | \-\-initial ]
+.RB [ \-\-gcov\-tool
+.IR tool ]
+.br
+.RB [ \-\-checksum ]
+.RB [ \-\-no\-checksum ]
+.RB [ \-\-no\-recursion ]
+.RB [ \-f | \-\-follow ]
+.br
+.RB [ \-\-compat\-libtool ]
+.RB [ \-\-no\-compat\-libtool ]
+.RB [ \-\-ignore\-errors
+.IR errors ]
+.br
+.RB [ \-\-to\-package
+.IR package ]
+.RB [ \-\-from\-package
+.IR package ]
+.RB [ \-q | \-\-quiet ]
+.br
+.RB [ \-\-no\-markers ]
+.RB [ \-\-external ]
+.RB [ \-\-no\-external ]
+.br
+.RB [ \-\-config\-file
+.IR config\-file ]
+.RB [ \-\-rc
+.IR keyword = value ]
+.br
+.RB [ \-\-compat
+.IR  mode =on|off|auto]
+.br
+.RE
+
+.B lcov
+.BR \-z | \-\-zerocounters
+.RS 5
+.br
+.RB [ \-d | \-\-directory
+.IR directory ]
+.RB [ \-\-no\-recursion ]
+.RB [ \-f | \-\-follow ]
+.br
+.RB [ \-q | \-\-quiet ]
+.br
+.RE
+
+.B lcov
+.BR \-l | \-\-list
+.I tracefile
+.RS 5
+.br
+.RB [ \-q | \-\-quiet ]
+.RB [ \-\-list\-full\-path ]
+.RB [ \-\-no\-list\-full\-path ]
+.br
+.RB [ \-\-config\-file
+.IR config\-file ]
+.RB [ \-\-rc
+.IR keyword = value ]
+.br
+.RE
+
+.B lcov
+.BR \-a | \-\-add\-tracefile
+.I tracefile
+.RS 5
+.br
+.RB [ \-o | \-\-output\-file
+.IR tracefile ]
+.RB [ \-\-checksum ]
+.RB [ \-\-no\-checksum ]
+.br
+.RB [ \-q | \-\-quiet ]
+.RB [ \-\-config\-file
+.IR config\-file ]
+.RB [ \-\-rc
+.IR keyword = value ]
+.br
+.RE
+
+.B lcov
+.BR \-e | \-\-extract
+.I tracefile pattern
+.RS 5
+.br
+.RB [ \-o | \-\-output\-file
+.IR tracefile ]
+.RB [ \-\-checksum ]
+.RB [ \-\-no\-checksum ]
+.br
+.RB [ \-q | \-\-quiet ]
+.RB [ \-\-config\-file
+.IR config\-file ]
+.RB [ \-\-rc
+.IR keyword = value ]
+.br
+.RE
+
+.B lcov
+.BR \-r | \-\-remove
+.I tracefile pattern
+.RS 5
+.br
+.RB [ \-o | \-\-output\-file
+.IR tracefile ]
+.RB [ \-\-checksum ]
+.RB [ \-\-no\-checksum ]
+.br
+.RB [ \-q | \-\-quiet ]
+.RB [ \-\-config\-file
+.IR config\-file ]
+.RB [ \-\-rc
+.IR keyword = value ]
+.br
+.RE
+
+.B lcov
+.BR \-\-diff
+.IR "tracefile diff"
+.RS 5
+.br
+.RB [ \-o | \-\-output\-file
+.IR tracefile ]
+.RB [ \-\-checksum ]
+.RB [ \-\-no\-checksum ]
 .br
 .RB [ \-\-convert\-filenames ]
 .RB [ \-\-strip
 .IR depth ]
 .RB [ \-\-path
 .IR path ]
+.RB [ \-q | \-\-quiet ]
 .br
-.RB [ \-\-checksum ]
-.RB [ \-\-no\-checksum ]
+.RB [ \-\-config\-file
+.IR config\-file ]
+.RB [ \-\-rc
+.IR keyword = value ]
 .br
-.RB [ \-\-compat\-libtool ]
-.RB [ \-\-no\-compat\-libtool ]
+.RE
+
+.B lcov
+.BR \-\-summary
+.I tracefile
+.RS 5
 .br
-.RB [ \-\-gcov\-tool
-.IR tool ]
-.RB [ \-\-ignore\-errors
-.IR errors ]
+.RB [ \-q | \-\-quiet ]
 .br
-.RB [ \-\-no\-recursion ]
+.RE
+
+.B lcov
+.RB [ \-h | \-\-help ]
+.RB [ \-v | \-\-version ]
+.RS 5
+.br
+.RE
+
 .SH DESCRIPTION
 .B lcov
 is a graphical front\-end for GCC's coverage testing tool gcov. It collects
-gcov data for multiple source files and creates HTML pages containing the
-source code annotated with coverage information. It also adds overview pages
-for easy navigation within the file structure.
+line, function and branch coverage data for multiple source files and creates
+HTML pages containing the source code annotated with coverage information.
+It also adds overview pages for easy navigation within the file structure.
 
 Use
 .B lcov
@@ -79,8 +192,10 @@
 
 For Linux kernel coverage:
 .RS
-Follow the installation instructions for the gcov\-kernel patch:
+Follow the setup instructions for the gcov\-kernel infrastructure:
 .I http://ltp.sourceforge.net/coverage/gcov.php
+.br
+
 
 .RE
 For user space application coverage:
@@ -93,6 +208,14 @@
 .B lcov
 as ".info file" or "tracefile" and that the output of GCOV
 is called ".da file".
+
+Also note that when printing percentages, 0% and 100% are only printed when
+the values are exactly 0% and 100% respectively. Other values which would
+conventionally be rounded to 0% or 100% are instead printed as nearest
+non-boundary value. This behavior is in accordance with that of the
+.BR gcov (1)
+tool.
+
 .SH OPTIONS
 
 
@@ -113,8 +236,8 @@
 The result of the add operation will be written to stdout or the tracefile
 specified with \-o.
 
-Only one of \-z, \-c, \-a, \-e, \-r, \-l and \-\-diff may be specified
-at a time.
+Only one of  \-z, \-c, \-a, \-e, \-r, \-l, \-\-diff or \-\-summary may be
+specified at a time.
 
 .RE
 
@@ -143,11 +266,10 @@
 directory in which the source code file is located.
 
 Note that this option will not work in environments where multiple base
-directories are used. In that case repeat the lcov call for each base directory
-while using the \-\-ignore\-errors option to prevent lcov from exiting when the
-first source code file could not be found. This way you can get partial coverage
-information for each base directory which can then be combined using the \-a
-option.
+directories are used. In that case use configuration file setting
+.B geninfo_auto_base=1
+(see
+.BR lcovrc (5)).
 .RE
 
 .B \-c
@@ -164,8 +286,8 @@
 The result of the capture operation will be written to stdout or the tracefile
 specified with \-o.
 
-Only one of \-z, \-c, \-a, \-e, \-r, \-l and \-\-diff may be specified
-at a time.
+Only one of  \-z, \-c, \-a, \-e, \-r, \-l, \-\-diff or \-\-summary may be
+specified at a time.
 .RE
 
 .B \-\-checksum
@@ -189,6 +311,73 @@
 to speed up coverage data processing and to reduce the size of tracefiles.
 .RE
 
+.B \-\-compat
+.IR mode = value [, mode = value ,...]
+.br
+.RS
+Set compatibility mode.
+
+Use \-\-compat to specify that lcov should enable one or more compatibility
+modes when capturing coverage data. You can provide a comma-separated list
+of mode=value pairs to specify the values for multiple modes.
+
+Valid
+.I values
+are:
+
+.B on
+.RS
+Enable compatibility mode.
+.RE
+.B off
+.RS
+Disable compatibility mode.
+.RE
+.B auto
+.RS
+Apply auto-detection to determine if compatibility mode is required. Note that
+auto-detection is not available for all compatibility modes.
+.RE
+
+If no value is specified, 'on' is assumed as default value.
+
+Valid
+.I modes
+are:
+
+.B libtool
+.RS
+Enable this mode if you are capturing coverage data for a project that
+was built using the libtool mechanism. See also
+\-\-compat\-libtool.
+
+The default value for this setting is 'on'.
+
+.RE
+.B hammer
+.RS
+Enable this mode if you are capturing coverage data for a project that
+was built using a version of GCC 3.3 that contains a modification
+(hammer patch) of later GCC versions. You can identify a modified GCC 3.3
+by checking the build directory of your project for files ending in the
+extension '.bbg'. Unmodified versions of GCC 3.3 name these files '.bb'.
+
+The default value for this setting is 'auto'.
+
+.RE
+.B split_crc
+.RS
+Enable this mode if you are capturing coverage data for a project that
+was built using a version of GCC 4.6 that contains a modification
+(split function checksums) of later GCC versions. Typical error messages
+when running lcov on coverage data produced by such GCC versions are
+\'out of memory' and 'reached unexpected end of file'.
+
+The default value for this setting is 'auto'
+.RE
+
+.RE
+
 .B \-\-compat\-libtool
 .br
 .B \-\-no\-compat\-libtool
@@ -209,6 +398,21 @@
 libtool, disable this option to prevent problems when capturing coverage data.
 .RE
 
+.B \-\-config\-file
+.I config\-file
+.br
+.RS
+Specify a configuration file to use.
+
+When this option is specified, neither the system\-wide configuration file
+/etc/lcovrc, nor the per\-user configuration file ~/.lcovrc is read.
+
+This option may be useful when there is a need to run several
+instances of
+.B lcov
+with different configuration file options in parallel.
+.RE
+
 .B \-\-convert\-filenames
 .br
 .RS
@@ -250,8 +454,8 @@
 The result of the diff operation will be written to stdout or the tracefile
 specified with \-o.
 
-Only one of \-z, \-c, \-a, \-e, \-r, \-l and \-\-diff may be specified
-at a time.
+Only one of  \-z, \-c, \-a, \-e, \-r, \-l, \-\-diff or \-\-summary may be
+specified at a time.
 .RE
 
 .B \-d
@@ -272,6 +476,23 @@
 Note that you may specify this option more than once.
 .RE
 
+.B \-\-external
+.br
+.B \-\-no\-external
+.br
+.RS
+Specify whether to capture coverage data for external source files.
+
+External source files are files which are not located in one of the directories
+specified by \-\-directory or \-\-base\-directory. Use \-\-external to include
+external source files while capturing coverage data or \-\-no\-external to
+ignore this data.
+
+Data for external source files is
+.B included
+by default.
+.RE
+
 .B \-e
 .I tracefile
 .I pattern
@@ -295,8 +516,8 @@
 The result of the extract operation will be written to stdout or the tracefile
 specified with \-o.
 
-Only one of \-z, \-c, \-a, \-e, \-r, \-l and \-\-diff may be specified
-at a time.
+Only one of  \-z, \-c, \-a, \-e, \-r, \-l, \-\-diff or \-\-summary may be
+specified at a time.
 .RE
 
 .B \-f
@@ -307,6 +528,19 @@
 Follow links when searching for .da files.
 .RE
 
+.B \-\-from\-package
+.I package
+.br
+.RS
+Use .da files in
+.I package
+instead of kernel or directory.
+
+Use this option if you have separate machines for build and test and
+want to perform the .info file creation on the build machine. See
+\-\-to\-package for more information.
+.RE
+
 .B \-\-gcov\-tool
 .I tool
 .br
@@ -393,9 +627,11 @@
 .IR subdirectory .
 
 Use this option if you don't want to get coverage data for all of the
-kernel, but only for specific subdirectories.
+kernel, but only for specific subdirectories. This option may be specified
+more than once.
 
-Note that you may specify this option more than once.
+Note that you may need to specify the full path to the kernel subdirectory
+depending on the version of the kernel gcov support.
 .RE
 
 .B \-l
@@ -408,8 +644,30 @@
 List the contents of the
 .IR tracefile .
 
-Only one of  \-z, \-c, \-a, \-e, \-r, \-l and \-\-diff may be specified
-at a time.
+Only one of  \-z, \-c, \-a, \-e, \-r, \-l, \-\-diff or \-\-summary may be
+specified at a time.
+.RE
+
+.B \-\-list\-full\-path
+.br
+.B \-\-no\-list\-full\-path
+.br
+.RS
+Specify whether to show full paths during list operation.
+
+Use \-\-list\-full\-path to show full paths during list operation
+or \-\-no\-list\-full\-path to show shortened paths. Paths are
+.B shortened
+by default.
+.RE
+
+.B \-\-no\-markers
+.br
+.RS
+Use this option if you want to get coverage data without regard to exclusion
+markers in the source code file. See
+.BR "geninfo " (1)
+for details on exclusion markers.
 .RE
 
 .B \-\-no\-recursion
@@ -458,6 +716,22 @@
 the standard output.
 .RE
 
+.B \-\-rc
+.IR keyword = value
+.br
+.RS
+Override a configuration directive.
+
+Use this option to specify a
+.IR keyword = value
+statement which overrides the corresponding configuration statement in
+the lcovrc configuration file. You can specify this option more than once
+to override multiple configuration statements.
+See
+.BR lcovrc (5)
+for a list of available keywords and their meaning.
+.RE
+
 .B \-r
 .I tracefile
 .I pattern
@@ -481,8 +755,8 @@
 The result of the remove operation will be written to stdout or the tracefile
 specified with \-o.
 
-Only one of \-z, \-c, \-a, \-e, \-r, \-l and \-\-diff may be specified
-at a time.
+Only one of  \-z, \-c, \-a, \-e, \-r, \-l, \-\-diff or \-\-summary may be
+specified at a time.
 .RE
 
 .B \-\-strip
@@ -495,6 +769,18 @@
 number of initial directories when matching tracefile and diff filenames.
 .RE
 
+.B \-\-summary
+.I tracefile
+.br
+.RS
+Show summary coverage information for the specified tracefile.
+
+Note that you may specify this option more than once.
+
+Only one of  \-z, \-c, \-a, \-e, \-r, \-l, \-\-diff or \-\-summary may be
+specified at a time.
+.RE
+
 .B \-t
 .I testname
 .br
@@ -511,6 +797,46 @@
 character ("_").
 .RE
 
+.B \-\-to\-package
+.I package
+.br
+.RS
+Store .da files for later processing.
+
+Use this option if you have separate machines for build and test and
+want to perform the .info file creation on the build machine. To do this,
+follow these steps:
+
+On the test machine:
+.RS
+.br
+\- run the test
+.br
+\- run lcov \-c [\-d directory] \-\-to-package
+.I file
+.br
+\- copy
+.I file
+to the build machine
+.RE
+.br
+
+On the build machine:
+.RS
+.br
+\- run lcov \-c \-\-from-package
+.I file
+[\-o and other options]
+.RE
+.br
+
+This works for both kernel and user space coverage data. Note that you might
+have to specify the path to the build directory using \-b with
+either \-\-to\-package or \-\-from-package. Note also that the package data
+must be converted to a .info file before recompiling the program or it will
+become invalid.
+.RE
+
 .B \-v
 .br
 .B \-\-version
@@ -529,8 +855,8 @@
 By default tries to reset kernel execution counts. Use the \-\-directory
 option to reset all counters of a user space program.
 
-Only one of \-z, \-c, \-a, \-e, \-r, \-l and \-\-diff may be specified
-at a time.
+Only one of  \-z, \-c, \-a, \-e, \-r, \-l, \-\-diff or \-\-summary may be
+specified at a time.
 .RE
 
 .SH FILES
diff --git a/man/lcovrc.5 b/man/lcovrc.5
index 8bb7a63..8566ef3 100644
--- a/man/lcovrc.5
+++ b/man/lcovrc.5
@@ -1,4 +1,4 @@
-.TH lcovrc 5 "LCOV 1.7" 2008\-11\-17 "User Manuals"
+.TH lcovrc 5 "LCOV 1.10" 2012\-10\-10 "User Manuals"
 
 .SH NAME
 lcovrc \- lcov configuration file
@@ -47,18 +47,21 @@
 #genhtml_css_file = gcov.css
 .br
 
-# Coverage rate limits for line coverage
+# Coverage rate limits
 .br
-genhtml_hi_limit = 50
+genhtml_hi_limit = 90
 .br
-genhtml_med_limit = 15
+genhtml_med_limit = 75
 .br
 
-# Coverage rate limits for function coverage
+# Width of line coverage field in source code view
 .br
-genhtml_function_hi_limit = 90
+genhtml_line_field_width = 12
 .br
-genhtml_function_med_limit = 75
+
+# Width of branch coverage field in source code view
+.br
+genhtml_branch_field_width = 16
 .br
 
 # Width of overview image
@@ -133,7 +136,17 @@
 
 # Include function coverage data display
 .br
-genhtml_function_coverage = 1
+#genhtml_function_coverage = 1
+.br
+
+# Include branch coverage data display
+.br
+#genhtml_branch_coverage = 1
+.br
+
+# Specify the character set of all generated HTML pages
+.br
+genhtml_charset=UTF\-8
 .br
 
 # Location of the gcov tool
@@ -156,30 +169,83 @@
 geninfo_compat_libtool = 0
 .br
 
+# Specify whether to capture coverage data for external source
+.br
+# files
+.br
+#geninfo_external = 1
+.br
+
+# Use gcov's --all-blocks option if non-zero
+.br
+#geninfo_gcov_all_blocks = 1
+.br
+
+# Specify compatiblity modes (same as \-\-compat option
+.br
+# of geninfo)
+.br
+#geninfo_compat = libtool=on, hammer=auto, split_crc=auto
+.br
+
+# Adjust path to source files by removing or changing path
+.br
+# components that match the specified pattern (Perl regular
+.br
+# expression format)
+.br
+#geninfo_adjust_src_path = /tmp/build => /usr/src
+
+# Specify if geninfo should try to automatically determine
+.br
+# the base-directory when collecting coverage data.
+.br
+geninfo_auto_base = 1
+.br
+
 # Directory containing gcov kernel files
 .br
 lcov_gcov_dir = /proc/gcov
 .br
 
-# Location of the insmod tool
-.br
-lcov_insmod_tool = /sbin/insmod
-.br
-
-# Location of the modprobe tool
-.br
-lcov_modprobe_tool = /sbin/modprobe
-.br
-
-# Location of the rmmod tool
-.br
-lcov_rmmod_tool = /sbin/rmmod
-.br
-
 # Location for temporary directories
 .br
 lcov_tmp_dir = /tmp
 .br
+
+# Show full paths during list operation if non\-zero
+.br
+lcov_list_full_path = 0
+.br
+
+# Specify the maximum width for list output. This value is
+.br
+# ignored when lcov_list_full_path is non\-zero.
+.br
+lcov_list_width = 80
+.br
+
+# Specify the maximum percentage of file names which may be
+.br
+# truncated when choosing a directory prefix in list output.
+.br
+# This value is ignored when lcov_list_full_path is non\-zero.
+.br
+lcov_list_truncate_max = 20
+
+# Specify if function coverage data should be collected and
+.br
+# processed.
+.br
+lcov_function_coverage = 1
+.br
+
+# Specify if branch coverage data should be collected and
+.br
+# processed.
+.br
+lcov_branch_coverage = 0
+.br
 .PP
 
 .SH OPTIONS
@@ -206,17 +272,12 @@
 .BR genhtml_med_limit " ="
 .I med_limit
 .br
-.BR genhtml_function_med_limit " ="
-.I hi_limit
-.br
-.BR genhtml_function_med_limit " ="
-.I med_limit
 .IP
 Specify coverage rate limits for classifying file entries. Use this option to
-modify the coverage rates (in percent) for line or function coverage at which
-a result is classified as high, medium or low coverage. This classification
-affects the color of the corresponding entries on the overview pages of the
-HTML output:
+modify the coverage rates (in percent) for line, function and branch coverage at
+which a result is classified as high, medium or low coverage. This
+classification affects the color of the corresponding entries on the overview
+pages of the HTML output:
 .br
 
 High:   hi_limit  <= rate <= 100        default color: green
@@ -226,8 +287,27 @@
 Low:    0         <= rate < med_limit   default color: red
 .br
 
-Defaults are 50 and 15 percent for line coverage and 90 and 75 percent for
-function coverage.
+Defaults are 90 and 75 percent.
+.PP
+
+.BR genhtml_line_field_width " ="
+.I number_of_characters
+.IP
+Specify the width (in characters) of the source code view column containing
+line coverage information.
+.br
+
+Default is 12.
+.PP
+
+.BR genhtml_branch_field_width " ="
+.I number_of_characters
+.IP
+Specify the width (in characters) of the source code view column containing
+branch coverage information.
+.br
+
+Default is 16.
 .PP
 
 .BR genhtml_overview_width " ="
@@ -438,6 +518,34 @@
 Default is 1.
 .PP
 
+.BR genhtml_branch_coverage " ="
+.IR 0 | 1
+.IP
+If non\-zero, include branch coverage data when generating HTML output using
+.BR genhtml .
+.br
+
+This option can be set to 0 by using the \-\-no\-branch\-coverage option of
+.BR genhtml .
+.br
+
+Default is 1.
+.PP
+
+.BR genhtml_charset " ="
+.I charset
+.IP
+Specify the character set of all generated HTML pages.
+.br
+
+Use this option if the source code contains characters which are not
+part of the default character set. Note that this option is ignored
+when a custom HTML prolog is specified (see also
+.BR genhtml_html_prolog ).
+.br
+
+Default is UTF-8.
+.PP
 .BR geninfo_gcov_tool " ="
 .I path_to_gcov
 .IP
@@ -492,40 +600,129 @@
 Default is 1.
 .PP
 
+.BR geninfo_external " ="
+.IR 0 | 1
+.IP
+If non\-zero, capture coverage data for external source files.
+
+External source files are files which are not located in one of the directories
+(including sub-directories)
+specified by the \-\-directory or \-\-base\-directory options of
+.BR lcov / geninfo .
+
+Default is 1.
+.PP
+
+.BR geninfo_gcov_all_blocks " ="
+.IR 0 | 1
+.IP
+If non\-zero, call the gcov tool with option --all-blocks.
+
+Using --all-blocks will produce more detailed branch coverage information for
+each line. Set this option to zero if you do not need detailed branch coverage
+information to speed up the process of capturing code coverage or to work
+around a bug in some versions of gcov which will cause it to endlessly loop
+when analysing some files.
+
+Default is 1.
+.PP
+
+.BR geninfo_compat " ="
+.IR mode = value [, mode = value ,...]
+.IP
+Specify that geninfo should enable one or more compatibility modes
+when capturing coverage data.
+
+This option corresponds to the \-\-compat command line option of
+.BR geninfo .
+
+Default is 'libtool=on, hammer=auto, split_crc=auto'.
+.PP
+
+.BR geninfo_adjust_src_path " ="
+.IR pattern " => " replacement
+.br
+.BR geninfo_adjust_src_path " ="
+.I pattern
+.IP
+Adjust source paths when capturing coverage data.
+
+Use this option in situations where geninfo cannot find the correct
+path to source code files of a project. By providing a
+.I pattern
+in Perl regular expression format (see
+.BR perlre (1))
+and an optional replacement string, you can instruct geninfo to
+remove or change parts of the incorrect source path.
+
+.B Example:
+.br
+
+1. When geninfo reports that it cannot find source file
+.br
+
+    /path/to/src/.libs/file.c
+.br
+
+while the file is actually located in
+.br
+
+    /path/to/src/file.c
+.br
+
+use the following parameter:
+.br
+
+    geninfo_adjust_src_path = /.libs
+
+This will remove all "/.libs" strings from the path.
+
+2. When geninfo reports that it cannot find source file
+.br
+
+    /tmp/build/file.c
+.br
+
+while the file is actually located in
+.br
+
+    /usr/src/file.c
+.br
+
+use the following parameter:
+.br
+
+    geninfo_adjust_src_path = /tmp/build => /usr/src
+.br
+
+This will change all "/tmp/build" strings in the path to "/usr/src".
+.PP
+
+.BR geninfo_auto_base " ="
+.IR 0 | 1
+.IP
+If non\-zero, apply a heuristic to determine the base directory when
+collecting coverage data.
+.br
+
+Use this option when using geninfo on projects built with libtool or
+similar build environments that work with multiple base directories,
+i.e. environments, where the current working directory when invoking the
+compiler ist not the same directory in which the source code file is
+located, and in addition, is different between files of the same project.
+.br
+
+Default is 1.
+.PP
+
 .BR lcov_gcov_dir " ="
 .I path_to_kernel_coverage_data
 .IP
-Specify the path to the directory where kernel coverage data can be found.
+Specify the path to the directory where kernel coverage data can be found
+or leave undefined for auto-detection.
 .br
 
-Default is '/proc/gcov'.
-.PP
-
-.BR lcov_insmod_tool " ="
-.I path_to_insmod
-.IP
-Specify the location of the insmod tool used to load kernel modules.
-.br
-
-Default is '/sbin/insmod'.
-.PP
-
-.BR lcov_modprobe_tool " ="
-.I path_to_modprobe
-.IP
-Specify the location of the modprobe tool used to load kernel modules.
-.br
-
-Default is '/sbin/modprobe'.
-.PP
-
-.BR lcov_rmmod_tool " ="
-.I path_to_rmmod
-.IP
-Specify the location of the rmmod tool used to unload kernel modules.
-.br
-
-Default is '/sbin/rmmod'.
+Default is auto-detection.
 .PP
 
 .BR lcov_tmp_dir " ="
@@ -537,6 +734,73 @@
 Default is '/tmp'.
 .PP
 
+.BR lcov_list_full_path " ="
+.IR 0 | 1
+.IP
+If non-zero, print the full path to source code files during a list operation.
+.br
+
+This option corresponds to the \-\-list\-full\-path option of
+.BR lcov .
+.br
+
+Default is 0.
+.PP
+
+.BR lcov_list_max_width " ="
+.IR width
+.IP
+Specify the maximum width for list output. This value is ignored when
+lcov_list_full_path is non\-zero.
+.br
+
+Default is 80.
+.PP
+
+.BR lcov_list_truncate_max
+.B " ="
+.IR percentage
+.IP
+Specify the maximum percentage of file names which may be truncated when
+choosing a directory prefix in list output. This value is ignored when
+lcov_list_full_path is non\-zero.
+.br
+
+Default is 20.
+.PP
+
+.BR lcov_function_coverage " ="
+.IR 0 | 1
+.IP
+Specify whether lcov should handle function coverage data.
+.br
+
+Setting this option to 0 can reduce memory and CPU time consumption
+when lcov is collecting and processing coverage data, as well as
+reduce the size of the resulting data files. Note that setting
+.B genhtml_function_coverage
+will override this option for HTML generation.
+.br
+
+Default is 1.
+.PP
+
+.BR lcov_branch_coverage " ="
+.IR 0 | 1
+.IP
+Specify whether lcov should handle branch coverage data.
+.br
+
+Setting this option to 0 can reduce memory and CPU time consumption
+when lcov is collecting and processing coverage data, as well as
+reduce the size of the resulting data files. Note that setting
+.B genhtml_branch_coverage
+will override this option for HTML generation.
+.br
+
+Default is 0.
+.PP
+
 .SH FILES
 
 .TP
diff --git a/rpm/lcov.spec b/rpm/lcov.spec
index 894e1c4..d1daca1 100644
--- a/rpm/lcov.spec
+++ b/rpm/lcov.spec
@@ -1,6 +1,6 @@
 Summary: A graphical GCOV front-end
 Name: lcov
-Version: 1.7
+Version: 1.10
 Release: 1
 License: GPL
 Group: Development/Tools
@@ -8,6 +8,7 @@
 Source0: http://downloads.sourceforge.net/ltp/lcov-%{version}.tar.gz
 BuildRoot: /var/tmp/%{name}-%{version}-root
 BuildArch: noarch
+Requires: perl >= 5.8.8
 
 %description
 LCOV is a graphical front-end for GCC's coverage testing tool gcov. It collects
@@ -35,6 +36,8 @@
 /etc
 
 %changelog
+* Mon May 07 2012 Peter Oberparleiter (Peter.Oberparleiter@de.ibm.com)
+- added dependency on perl 5.8.8 for >>& open mode support
 * Wed Aug 13 2008 Peter Oberparleiter (Peter.Oberparleiter@de.ibm.com)
 - changed description + summary text
 * Mon Aug 20 2007 Peter Oberparleiter (Peter.Oberparleiter@de.ibm.com)