#!/usr/bin/env perl
use strict;
use warnings;
use File::Path qw(make_path);
use File::Find;
use File::Basename qw(dirname basename);
use File::Which qw(which);
use Cwd;
$|++; # This causes printed output to be flushed immediately, regardless of newline
# Find helper programs
our $cppcheck = "not found";
our $code2html = "not found";
if(exists($ENV{'CPPCHECK'})){
$cppcheck = $ENV{'CPPCHECK'};
}elsif(defined(which("cppcheck"))){
$cppcheck = which("cppcheck");
}
if(exists($ENV{'CODE2HTML'})){
$code2html = $ENV{'CODE2HTML'};
}elsif(defined(which("code2html"))){
$code2html = which("code2html");
}
if($cppcheck eq "not found" or $code2html eq "not found"){
print "Helper applications not found:\n";
print " cppcheck : $cppcheck\n";
print " code2html : $code2html \n";
print "\n";
print "Make sure these are either in your PATH or set your\n";
print "CPPCHECK and CODE2HTML environment variables to point to them.\n";
print "\n";
exit(-1);
}
#our $bindir = "/Users/davidl/Desktop/c++check";
#our $cppcheck = "$bindir/cppcheck";
#our $code2html = "$bindir/code2html";
our $top_dir = cwd();
our $htmldir = "$top_dir/html";
our @infiles;
#---------------------------------------------------------------
# Loop over user's inputs and make list of all files to process
foreach my $arg (@ARGV)
{
# User could pass file or directory
if( -d $arg){
# Scan directory looking for C,C++ files
our $startdir_abs = $arg;
if( $startdir_abs !~ /\/$/){ $startdir_abs .= "/"; }
find(\&AddFile, $arg);
}elsif( -f $arg){
my $relpath = dirname($arg)."/";
my $fname = $arg;
if( $fname =~ /^\Q$relpath/){ $fname=$'; }
if($relpath eq "./"){ $relpath = ""; }
my $entry = {relpath=>"$relpath", fname=>"$fname", startdir_abs=>dirname($arg)."/"};
push(@infiles, $entry);
}else{
print "Can't find \"$arg\"\n";
}
}
#---------------------------------------------------------------
# Check that we found at least 1 input file before continuing
my$Ninputfiles = @infiles;
if($Ninputfiles < 1){
print "\nNeed at least 1 input file!\n";
printf "\n Usage:\n c++check file_or_dir1 [file_or_dir2] ...\n\n";
exit(0);
}
#---------------------------------------------------------------
# Loop over input files and run each through the c++ code checker
print "making \"$htmldir\"\n";
mkdir($htmldir);
our @items = ();
foreach my $infile (@infiles){
# Copy info to hash variable for easier access
my %infile = %$infile;
# Make sure output directory exists for html file
my $pathname = $infile{"relpath"}.$infile{"fname"};
chdir($htmldir);
make_path($infile{"relpath"});
# Process file and store output in hash
chdir($top_dir); # the startdir_abs may be relative
chdir($infile{"startdir_abs"});
my @tmp = &ProcessFile($pathname);
push(@items, @tmp);
print "Found ".@tmp." issues (".@items." total so far)\n";
}
#---------------------------------------------------------------
# Remove duplicate entries from items
print "Removing duplicate items\n";
for(my $i=0; $i<@items; $i++){
my $item1 = $items[$i];
my %it1 = %$item1;
for(my $j=$i+1; $j<@items; $j++){
my $item2 = $items[$j];
my %it2 = %$item2;
my $differ = 0;
while( (my $k, my $v) = each %it1){
if($it2{$k} ne $v){
if($k =~ /^(file|line|type|mess)/){ # only check file,line,type, and mess
$differ=1;
}
}
}
if($differ == 0){
splice @items,$j,1; # remove the duplicate item
$j--; # backup one so we check the new j-th element on next iteration
}
}
}
print " ".@items." unique issues identified.\n";
#---------------------------------------------------------------
# Count number of errors, warnings, etc
our %Ntype;
foreach my $item (@items){
my %it = %$item;
$Ntype{$it{"type"}}++;
}
#---------------------------------------------------------------
# Loop over input files again, running each through html-ifier
our $startdir_abs;
chdir($top_dir); # the startdir_abs may be relative
chdir($startdir_abs);
foreach my $infile (@infiles){
my %infile = %$infile;
my $fname = $infile{"fname"};
my $pathname = $infile{"relpath"}.$fname;
my $ofname = $pathname.".html";
# Open output file
print "Writing $pathname ...\n";
open FILE, ">$htmldir/$ofname";
# Run code2html
my $cmd = "$code2html -n -N -l c++ $pathname";
print FILE "\n";
my $out = `$cmd`;
my @lines = split(/\n/, $out);
foreach my $line (@lines){
# Add link to CPPCheck, it's full output, and self-recognition line at bottom
if( $line =~ /<\/body/){
my $html = "
Static C++ analysis provided by ";
$html .= "".`$cppcheck --version`."";
$html .= " (see full output from cppcheck for this file)";
$html .= "
Integration of static analysis results into HTML by David Lawrence
\n";
print FILE "$html
\n";
}
# Print original line
print FILE "$line\n";
# Add footer for Toggler
if( $line =~ /<\/body/){
print FILE &MakeTogglerJavascriptFooter()."\n";
}
# Add our own style sheet
if ($line =~ /\
${mess}
\n"; } } } } # Close output file close(FILE); } #--------------------------------------------------------------- # Write out index file open FILE, ">$htmldir/index.html"; print FILE "\n"; print FILE "\n"; print FILE &MakeTogglerJavascriptHeader()."\n"; print FILE "\n"; print FILE "\n\n"; print FILE "date: | ".`date` ." |
invocator: | ".`whoami` ." |
machine: | ".`uname -a`." |
";
print FILE "Notification counts:
\n";
print FILE &MakeTogglerJavascriptMenu(1);
print FILE "
File | \n"; print FILE "Type | \n"; print FILE "\n"; foreach my $item (@items){ my %it = %$item; my $line = $it{"line"}; my $file = $it{"filename"}; my $linkstr = "$file:$line"; print FILE " |
---|---|---|
$linkstr | \n"; print FILE "".$it{"type"}." | \n"; print FILE "".$it{"mess"}." | \n"; print FILE "
$k: | \n"; $html .= "\n"; if($include_num){$html .= " | $v | \n";} $html .= "\n"; $html .= " | \n"; $html .= " |