#!/usr/bin/perl -w

###############################################################################
# Runs krazy over the KDE source code for the EnglishBreakfastNetwork (EBN)   #
# Copyright 2006-2008,2010 by Allen Winter <winter@kde.org>                   #
# Copyright 2009-2010 by Bertjan Broeksema <b.broeksema@kdemail.net>          #
#                                                                             #
# 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.               #
#                                                                             #
###############################################################################
# Program options:
#   --help:            display help message and exit
#   --version          display version information and exit
#   --config <file>    settings file in YAML format
#   --dry-run          don't execute the checks; only show what would be run
#   --with-sloccount   Run sloccount in each module
#   --nodb             don't execute any database commands
#   --nosaxon          don't execute the xslt transformations.
#   --dbgen [id]       use the specified 'id' for the database generationId.
#                      Do NOT use this option unless you know what you're doing.
#   --component [comp] process component 'comp'
#   --module [mod1<,mod2,..>]
#                      process module list from component 'comp'
#   --title [title]    the title to print on the output reports
#
use strict;
use Getopt::Long;
use Env qw (PSQL);
use Sys::Hostname;
use File::Basename;
use File::Copy;
use File::Find;
use File::Path;
use POSIX qw (strftime);
use FindBin qw($Bin);
use lib "$Bin/../lib";
use Krazy::Config;
use Krazy::Utils;
use YAML::Any qw(LoadFile Load);

my ($Prog) = 'krazy2xml';
my
 $VERSION = '2.92';    #split line so MakeMaker can find the version here

my (@modules);    # list of modules to process.  change as desired.
my ($comp);       # default component to process
my ($module);     # default module to process (empty implies ALL modules)
my ($tempdir);    # fullpath to the top level temporary output directory
my ($destdir);    # fullpath to the top level output directory
my ($trunk);      # fullpath to the top level of the trunk checkout
my ($krazy);      # fullpath to the krazy program
my ($plgpth);     # fullpath to the krazy plugins
my ($xtrpth);     # fullpath to the krazy extras
my ($PSQL);       # fullpath to the psql program
my ($md5);        # fullpath to the md5sum checksum program
my ($svn);        # fullpath to the svnlook program
my ($repos);      # fullpath to the svn repository (where svnlook looks)
my ($checks);     # The list of checks to be executed.

my ($saxon);      # fullpath to the saxon program
my ($stylesheet); # full path to the main stylesheet (krazy-main.xsl)
my ($sloccount);  # full path to sloccount executable

my ($settings);   # fullpath to system settings 

my ($help)       = '';
my ($version)    = '';
my ($dryrun)     = '';
my ($configf)    = '';
my ($nodb)       = '';
my ($generation) = '';
my ($nosaxon)    = '';
my ($countloc)   = '';
my ($reptitle)   = '';

exit 1
  if ( !GetOptions(
       'help'           => \$help,
       'version'        => \$version,
       'dry-run'        => \$dryrun,
       'config=s'       => \$configf,
       'nodb'           => \$nodb,
       'dbgen=i'        => \$generation,
       'nosaxon'        => \$nosaxon,
       'with-sloccount' => \$countloc,
       'component=s'    => \$comp,
       'module=s'       => \$module,
       'title=s'        => \$reptitle
      )
     );

&Help()    if ($help);
&Version() if ($version);

# System settings configuration
if (-e "$configf") {
  $settings   = LoadFile("$configf");

  $tempdir    = $settings->{'tempdir'};
  $destdir    = $settings->{'destdir'};
  $comp       = $settings->{'comp'};
  $module     = $settings->{'module'};
  $trunk      = $settings->{'trunk'};
  $krazy      = $settings->{'krazy'};
  $plgpth     = $settings->{'plgpth'};
  $xtrpth     = $settings->{'xtrpth'};
  $PSQL       = $settings->{'PSQL'};
  $md5        = $settings->{'md5'};
  $svn        = $settings->{'svn'};
  $repos      = $settings->{'repos'};
  $checks     = $settings->{'checks'};
  $saxon      = $settings->{'saxon'};
  $stylesheet = $settings->{'stylesheet'};
  $sloccount  = $settings->{'sloccount'};

} elsif ( -e "$ENV{'HOME'}/.krazy2xml" ) {
  $settings   = LoadFile("$ENV{'HOME'}/.krazy2xml");

  $tempdir    = $settings->{'tempdir'};
  $destdir    = $settings->{'destdir'};
  $comp       = $settings->{'comp'};
  $module     = $settings->{'module'};
  $trunk      = $settings->{'trunk'};
  $krazy      = $settings->{'krazy'};
  $plgpth     = $settings->{'plgpth'};
  $xtrpth     = $settings->{'xtrpth'};
  $PSQL       = $settings->{'PSQL'};
  $md5        = $settings->{'md5'};
  $svn        = $settings->{'svn'};
  $repos      = $settings->{'repos'};
  $checks     = $settings->{'checks'};
  $saxon      = $settings->{'saxon'};
  $stylesheet = $settings->{'stylesheet'};
  $sloccount  = $settings->{'sloccount'};

} else {
  $tempdir    = '/mnt/ebn/apache/www.englishbreakfastnetwork.org/krazy/temp';
  $destdir    = '/mnt/ebn/apache/www.englishbreakfastnetwork.org/krazy/reports';
  $comp       = 'kde-4.x';
  $module     = '';
  $trunk      = '/mnt/ebn/src';
  $krazy      = '/mnt/ebn/bin/krazy2';
  $plgpth     = '/mnt/ebn/lib/krazy2/krazy-plugins';
  $xtrpth     = '/mnt/ebn/lib/krazy2/krazy-extras';
  $PSQL       = '/usr/bin/psql' if ( !$PSQL );
  $md5        = '/usr/bin/md5sum';
  $svn        = '/usr/bin/svnlook youngest';
  $repos      = '/mnt/ebn/svn/kde';
  $checks     = ''; # execute all checks.
  $saxon      = '/usr/bin/saxon8';
  $stylesheet = '/mnt/ebn/share/xsl/krazy-main.xsl';
  $sloccount  = '/usr/bin/sloccount';
}
#END OF SETTINGS

# Setup for db
my($hasdb) = !$nodb; # Added for convenience.
$PSQL="" if ($nodb);

my ($compId) = '';
my ($toolId) = '';

if ($hasdb) {
  $compId = &execSql("\"SELECT id FROM components WHERE name='$comp';\"");
  if ( !$compId || $compId eq "0" ) {
    die "Error: Cannot get Component Id for $comp\n";
  }

  $toolId = &execSql("\"SELECT id FROM tools WHERE name='krazy' AND component=$compId;\"");
  if ( !$toolId || $toolId eq "0" ) {
    die "Error: Cannot get Tool Id for krazy and component $comp\n";
  }

  if (!$generation) {
    $generation = &execSql("\"SELECT * FROM nextval('generation');\"");
  }
}

my ($svnRev) = '';
if ( hostname() =~ m/englishbreakfast/ ) {
  $svnRev = `$svn $repos`;
} else { # I don't have a copy of the repository, just a local checkout.
  $svnRev = `$svn $repos | grep Revision | awk '{print \$2}'`;
}
chomp($svnRev);
$svnRev = 0 if ( !$svnRev || $svnRev eq "0" );

# Global settings
my (@gIgModsList);    #modules to ignore
my (@gIgSubsList) = ( #subdirs to ignore in all modules
  "doc",
  "cmake",
  "pics",
  "applnk",
  "admin"
);
my (@gExSubsList);   # extra subdirs to include in all modules
my ($defRegex)   = "/////";
my ($gSkipRegex) = "$defRegex";    #regex of stuff to skip in a subdir
my ($gPriority)  = "high";         #priority. default is high
my ($gStrict)    = "normal";       #strictness. default is normal
my (@gCheckList);                  #plugins to run. default is all
my (@gExcludeList);                #plugins to exclude. default is none
my (@gExtraList);                  #extra plugins. default is none

# Per Module settings
my (@mIgSubsList);
my (@mExSubsList);
my ($mSkipRegex) = "";
my ($mPriority) = "";
my ($mStrict) = "";
my (@mCheckList);
my (@mExcludeList);
my (@mExtraList);

# Per Subdir settings
my ($sSkipRegex) = "";
my ($sPriority) = "";
my ($sStrict) = "";
my (@sCheckList);
my (@sExcludeList);
my (@sExtraList);

# Override Global settings from the component .krazy file
&mergeComponentSettings("$trunk/$comp");

push( @modules, split( ",", $module ) ) if ($module);

# Create the module list
my ($m);
if ( $#modules < 0 ) {
  my ( $im, $found );
  opendir( DIR, "$trunk/$comp" ) or die "Error: Cannot open $trunk/$comp: $!";
  while ( defined( $m = readdir(DIR) ) ) {
    next unless ( -d "$trunk/$comp/$m" );
# allow kdebase-foo    next if ( -l "$trunk/$comp/$m" );    #skip symlinks
    next if ( $m eq "." );               #skip cwd
    next if ( $m eq ".." );              #skip parent dir
    next if ( $m eq ".svn" );            #skip .svn
    $found = 0;
    for $im (@gIgModsList) {
      if ( $im eq $m ) {                 #IGNOREMODS from component-level .krazy
        $found = 1;
        last;
      }
    }
    push @modules, $m unless $found;
  }
}

# Let's doit!
my (@subdirs);

&myMkdir("$tempdir/$comp") if (! -d "$tempdir/$comp");

# This is the same for all modules so generate only once
print "  Generating $comp/tool-list.xml\n";
my $command = "$krazy --export=xml --list --explain > $tempdir/$comp/tool-list.xml";
if ($dryrun) {
  $command = "echo " . "\'" . $command . "\'";
}
system("$command");

for $m (@modules) {
  print "  [$m]\n";
  &myRmdir("$tempdir/$comp/$m");
  &myMkdir("$tempdir/$comp/$m");

  # Create two required files for xmltodb:
  # 1) tool-list.xml
  print "    Copying tool-list.xml to module dir\n";
  copy("$tempdir/$comp/tool-list.xml", "$tempdir/$comp/$m/tool-list.xml");

  # 2) component.info
  print "    Generating $comp/$m/component.info\n";
  open(COMPINFO,">$tempdir/$comp/$m/component.info") || die("Cannot Open component.info");
  print COMPINFO "component=$comp\n";
  print COMPINFO "module=$m\n";
  print COMPINFO "revision=$svnRev\n";
  close(COMPINFO);

  @subdirs = ();
  if ( -f "$trunk/$comp/$m/CMakeLists.txt" ) {
    &subdirsFromCMakeList("$trunk/$comp/$m/CMakeLists.txt");
  } else {
    if ( -f "$trunk/$comp/$m/subdirs" ) {
      &subdirsFromSubdirs("$trunk/$comp/$m/subdirs");
    } else {
      &subdirsFromAll("$trunk/$comp/$m");
    }
  }
  &mergeModuleSettings("$trunk/$comp/$m");
  &doItForList( "$m", $svnRev );
  &myMkdir("$destdir/$comp/$m");
  &myMvdir("$tempdir/$comp/$m", "$destdir/$comp/$m");
}

# Update our generation field in the tools table.
if ($hasdb) {
  &execSql("\"UPDATE tools SET generation=$generation WHERE id=$toolId;\"");

  # Put a summary of this run into the generations table.
  my ($now) = `date "+%B %d %Y %T"`;
  chomp $now;
  &execSql("\"INSERT INTO generations VALUES ($generation, '$now',"
    . "(SELECT SUM(issues) FROM results_krazy WHERE component='$compId' "
    . " AND generation=$generation), $toolId, $svnRev );\"");
}

#==============================================================================

#override global settings from component-level directives
sub mergeComponentSettings {
  my ($rc) = @_;
  $rc .= "/.krazy";

  my (%d) = ParseKrazyRC($rc);

  @gIgModsList = @{ $d{'IGMODSLIST'} } if ( $#{ $d{'IGMODSLIST'} } >= 0 );
  @gIgSubsList = @{ $d{'IGSUBSLIST'} } if ( $#{ $d{'IGSUBSLIST'} } >= 0 );
  @gExSubsList = @{ $d{'IGEXTRASLIST'} } if ( $#{ $d{'IGEXTRASLIST'} } >= 0 );

  if ( $d{'SKIPREGEX'} ) {
    $gSkipRegex = $d{'SKIPREGEX'};
    $gSkipRegex =~ s+\|+\\\|+g;
  }

  $gPriority = $d{'PRIORITY'} if ( $d{'PRIORITY'} );
  $gStrict = $d{'STRICT'} if ( $d{'STRICT'} );

  @gExcludeList = split( ",", $d{'EXCLUDE'} ) if ( $d{'EXCLUDE'} );
  @gExtraList   = split( ",", $d{'EXTRA'} )   if ( $d{'EXTRA'} );
  #do not allow CHECK at the component-level
  #@gCheckList   = split( ",", $d{'CHECK'} )   if ( $d{'CHECK'} );
}

#merge global settings with module-level directives
sub mergeModuleSettings {
  my ($rc) = @_;
  $rc .= "/.krazy";

  my (%d) = ParseKrazyRC($rc);

  @mIgSubsList = @gIgSubsList;
  push( @mIgSubsList, @{ $d{'IGSUBSLIST'} } ) if ( $#{ $d{'IGSUBSLIST'} } >= 0 );
  @mIgSubsList = deDupe(@mIgSubsList);

  @mExSubsList = @gExSubsList;
  push( @mExSubsList, @{ $d{'IGEXTRASLIST'} } ) if ( $#{ $d{'IGEXTRASLIST'} } >= 0 );
  @mExSubsList = deDupe(@mExSubsList);

  $mSkipRegex = $gSkipRegex;
  my ($tmpstr) = "";
  $tmpstr = $d{'SKIPREGEX'} if ( $d{'SKIPREGEX'} );
  $tmpstr =~ s+\|+\\\|+g;
  if ( $mSkipRegex ne $defRegex ) {
    $mSkipRegex = $mSkipRegex . "\\|" . $tmpstr if ( $tmpstr );
  } else {
    $mSkipRegex = $tmpstr if ( $tmpstr );
  }

  #module priority and strictness overrides the global values
  $mPriority = $gPriority;
  $mPriority = $d{'PRIORITY'} if ( $d{'PRIORITY'} );
  $mStrict = $gStrict;
  $mStrict = $d{'STRICT'} if ( $d{'STRICT'} );

  @mExcludeList = @gExcludeList;
  push( @mExcludeList, split( ",", $d{'EXCLUDE'} ) ) if ( $d{'EXCLUDE'} );
  @mExcludeList = deDupe(@mExcludeList);

  @mExtraList = @gExtraList;
  push( @mExtraList, split( ",", $d{'EXTRA'} ) ) if ( $d{'EXTRA'} );
  @mExtraList = deDupe(@mExtraList);

  @mCheckList = @gCheckList;
  push( @mCheckList, split( ",", $d{'CHECK'} ) ) if ( $d{'CHECK'} );
  @mCheckList = deDupe(@mCheckList);
}

#merge module-level directives with subdir-level directives
sub mergeSubdirSettings {
  my ($rc) = @_;
  $rc .= "/.krazy";

  my (%d) = ParseKrazyRC($rc);

  $sSkipRegex = $mSkipRegex;
  my ($tmpstr) = "";
  $tmpstr = $d{'SKIPREGEX'} if ( $d{'SKIPREGEX'} );
  $tmpstr =~ s+\|+\\\|+g;
  if ( $sSkipRegex ne $defRegex ) {
    $sSkipRegex = $sSkipRegex . "\\|" . $tmpstr if ( $tmpstr );
  } else {
    $sSkipRegex = $tmpstr if ( $tmpstr );
  }

  #subdir priority and strictness overrides the module values
  $sPriority = $mPriority;
  $sPriority = $d{'PRIORITY'} if ( $d{'PRIORITY'} );
  $sStrict = $mStrict;
  $sStrict = $d{'STRICT'} if ( $d{'STRICT'} );

  @sExcludeList = @mExcludeList;
  push( @sExcludeList, split( ",", $d{'EXCLUDE'} ) ) if ( $d{'EXCLUDE'} );
  @sExcludeList = deDupe(@sExcludeList);

  @sExtraList = @mExtraList;
  push( @sExtraList, split( ",", $d{'EXTRA'} ) ) if ( $d{'EXTRA'} );
  @sExtraList = deDupe(@sExtraList);

  @sCheckList = @mCheckList;
  push( @sCheckList, split( ",", $d{'CHECK'} ) ) if ( $d{'CHECK'} );
  @sCheckList = deDupe(@sCheckList);
}

sub doItForList {
  my (
    $module,    # module
    $rev
  ) = @_;

  my ( $s, $is );
  my ($checksum);
  my ( $out, @issues, $iss );
  push(@subdirs, @mExSubsList);
  if ( $#subdirs >= 0 ) {
    for $s (@subdirs) {

      $out = "$tempdir/$comp/$module/$s";
      &myMkdir("$out");
      $checksum = 0;
      $iss      = 0;
      if ( &inIgnoreSubs($s) ) {
        print "    Ignoring $comp/$module/$s\n";
        &createIgnorePage( "$out", "$comp/$module/$s" ) if (!$nosaxon);
      } else {
        &mergeSubdirSettings( "$trunk/$comp/$module/$s" );
        if ($countloc) {
          my $command = "$sloccount $trunk/$comp/$module > $tempdir/$comp/$module/sloccount.output";
          if ($dryrun) {
            $command = "echo " . "\'" . $command . "\'";
          }

          system("$command");

        }
        &doIt( "$trunk/$comp/$module/$s", "$out", "$comp/$module/$s", $rev );
        if ( -f "$out/index.html" && `grep "Total Issues" $out/index.html` ) {
          (@issues) =
            split( " ", `grep "Total Issues" $out/index.html | head -1` );
          $iss = $issues[3];

          #compute checksum (grep out the date, which changes every run)
          ($checksum) =
            split( " ", `grep -v "\.\.\.as of" $out/index.html | $md5` );
        }
      }
      if ($hasdb) {
        &execSql("\"INSERT INTO results_krazy "
               . "(checksum, issues, report, component, module, application, generation ) "
               . "VALUES ( '$checksum', $iss, '$comp/$module/$s/index.html'"
               .        ", '$compId', '$module', '$s', $generation );\"");
      }
    }
  }
}

sub doIt {
  my (
    $in,     # dir to process
    $out,    # dir to write the report
    $cms,    # component/module/subdir
    $rev     # svn revision
  ) = @_;

  my ($t);

  #subtract checkList from excludeList
  &removeChecksFromExcludes();

  $t = &arrayToCSL(@sExcludeList);
  my ($exclude) = "";
  $exclude = "--exclude=$t" if ($t);

  $t = &arrayToCSL(@sExtraList);
  my ($extra) = "";
  $extra = "--extra=$t" if ($t);

  my ($skip) = $sSkipRegex;
  if ( $skip ne $defRegex && $skip ne "" ) {
    print "    Processing $cms (without $skip)\n";
  } else {
    print "    Processing $cms\n";
  }

  my ($priority) = "";
  $priority = "--priority=$sPriority" if ($sPriority);

  my ($strict) = "";
  $strict = "--strict=$sStrict" if ($sStrict);

  my ($title);
  if ($reptitle) {
    $title = $reptitle;
  } else {
    $title = "Results for $cms";
  }

  if ( not -d "$in" ) {
    print "Skipping non-existing dir: $in\n";
    return;
  }

  my ($command) =
 "cd $in; find . -name '*.tcc' -o -name '*.cpp' -o -name '*.cc' -o -name '*.cxx' -o -name '*.c' -o -name '*.h' -o -name '*.hxx' -o -name '*.ui' -o -name '*.desktop' -o -name '*.kcfg' -o -name Messages.sh -o -name '*.rc' -o -name tips -o -name '*.qml' -o -name '*.qdoc' -type f | grep -v '$skip' | xargs $krazy $checks --ignorerc $priority $strict --export=xml --explain --title \"$title\" -cms $cms --rev $rev $exclude $extra > $out/result.xml";
  if ($dryrun) {
    $command = "echo " . "\'" . $command . "\'";
  }
  # Generate the xml report.
  system("$command");

  # Probably no files to process resulting in an empty (0 byte) result.xml file.
  if (!$dryrun and -z "$out/result.xml") {
    return;
  }

  # Translate the xml report to ebn website 
  # NOTE: This is imo a temporary solution. As soon as we store the results in
  #       the database in a fine grained enough manner, we can change te ebn site
  #       to be a php only site and not a mixture of php and krazy generated pages.
  my ( $component, $module, $subdir ) = split( "/", $cms );
  if (!$nosaxon) {
    $command = "$saxon -im krazy2ebn $out/result.xml $stylesheet component=$component module=$module submodule=$subdir > $out/index.html";

    if ($dryrun) {
      $command = "echo " . "\'" . $command . "\'";
    }

    system($command);
    # Make sure the file ends with a newline.
    if (-f "$out/index.html") {
      open( F, ">>$out/index.html" );
      print F "\n";
      close F;
    }
  }

  # TODO: translate the xml reports to SQL queries and execute them.

  # We're finished, remove the xml file.
  #$command = "rm $out/result.xml";
  #system($command);
}

sub subdirsFromCMakeList {
  my ($f) = @_;
  open( F, "$f" ) || die "Error: Cannot open $f";
  my ($line);
  while ( $line = <F> ) {
    $line =~ s/#.*$//;
    next unless ( $line =~ m/add_subdirectory/i );
    $line =~ s/macro_optional_//i;
    $line =~ s/add_subdirectory[[:space:]]*\(\s+(\S+)\s+\)/$1/i;
    $line =~ s/add_subdirectory[[:space:]]*\((\S+)\)/$1/i;
    $line =~ s/^\s+//;
    $line =~ s/\s+$//;
    push( @subdirs, $line );
  }
  close(F);
}

sub subdirsFromSubdirs {
  my ($f) = @_;
  (@subdirs) = split( "[[:space:]]", `cat $f` );
}

sub subdirsFromAll {
  my ($d) = @_;
  my ($s);
  if ( !-d $d ) {
    print "bad component or module specified... exiting\n";
    exit 1;
  }
  opendir( DIR, "$d" ) or die "Error: Cannot open $d: $!";
  while ( defined( $s = readdir(DIR) ) ) {
    next unless ( -d "$d/$s" );
    next if ( -l "$d/$s" );           #skip symlinks
    next if ( $s eq "." );            #skip cwd
    next if ( $s eq ".." );           #skip parent dir
    next if ( $s =~ /svn/ );          #skip .svn
    next if ( $s =~ /build/ );        #skip build dirs
    next if ( $s eq "admin" );
    next if ( $s eq "cmake" );
    next if ( $s eq "CMakeFiles" );
    next if ( $s eq "doc" );
    next if ( $s eq ".libs" );
    next if ( $s eq "Testing" );
    next if ( $s eq "lib" );
    next if ( $s eq "bin" );
    next if ( $s eq "pics" );
    next if ( $s eq "m4" );
    push( @subdirs, $s );
  }
}

sub removeChecksFromExcludes() {
  my (%count) = ();
  my ( @diff, $item );
  foreach $item ( @sExcludeList, @sCheckList ) {
    $count{$item}++;
  }
  foreach $item (@sExcludeList) {
    if ( $count{$item} == 1 ) {
      push( @diff, $item );
    }
  }
  @sExcludeList = @diff;
}

# determine if $s is found in @mIgSubsList
sub inIgnoreSubs {
  my ($s) = @_;
  my ($item);
  foreach $item (@mIgSubsList) {
    return 1 if ( $s eq $item );
  }
  return 0;
}

# turn array into a comma-separated list
sub arrayToCSL {
  my (@list) = @_;
  my ($item);
  my ($s) = "";
  foreach $item (@list) {
    $s .= $item . ",";
  }
  $s =~ s/,$//;
  return $s;
}

sub myMkdir {
  my ($d) = @_;
  if ( !-d "$d" ) {
    mkpath("$d") || die "Error: Cannot create directory $d: $!\n";
    chmod 0775, $d;
  }
}

sub myRmdir {
  my ($d) = @_;
  if ( -d "$d" ) {
    rmtree("$d") || die "Error: Cannot remove directory $d: $!\n";
  }
}

sub myMvdir {
  my ( $d1, $d2 ) = @_;
  &myRmdir($d2);
  if ( -d "$d1" ) {
    rename( $d1, $d2 ) || die "Error: Cannot move directory $d1 to $d2: $!\n";
    chmod 0775, $d2;
  }
}

sub execSql {
  if ($PSQL) {
    if ( $PSQL ne "debug" ) {
      return `echo @_ | $PSQL -t -h services.codeyard.net -U kde kde -A -q`;
    } else {
      print "echo @_ | PSQL -t -h services.codeyard.net -U kde kde -A -q\n";
    }
  }
}

# createIgnorePage: create an empty "ignore" page.
sub createIgnorePage {
  my ( $outdir, $cms ) = @_;
  my ($f) = "$outdir" . "/index.html";
  open( F, ">$f" ) || return;
  my ($title) = "Results for " . "$cms";
  print "$title\n";
  my ( $component, $module, $subdir ) = split( "/", $cms );
  my ($upcomp) = uc($component);
  $upcomp =~ s/-/ /;

  print F "<html>\n";
  print F "<head>\n";
  print F "<title>$title</title>\n";
  print F "<link rel=\"stylesheet\" type=\"text/css\" title=\"Normal\" href=\"/style.css\" />\n";
  print F "</head>\n";
  print F "<body>\n";
  print F "<div id=\"title\">\n";
  print F "<div class=\"logo\">&nbsp;</div>\n";
  print F "<div class=\"header\">\n";
  print F "<h1><a href=\"/\">English Breakfast Network</a></h1>\n";
  print F "<p><a href=\"/\">Almost, but not quite, entirely unlike tea.</a></p>\n";
  print F "</div>\n";
  print F "</div>\n";
  print F "<div id=\"content\">\n";
  print F "<div class=\"inside\">\n";

  # Links to other available reports
  if ( $component && $module && $subdir ) {
    print F "<p style=\"font-size: x-small;font-style: sans-serif;\">\n";
    print F "Other $module/$subdir reports:\n";
    print F "[<a href=\"/apidocs/apidox-$component/$module-$subdir.html\">APIDOX</a>]\n";
    print F "[<a href=\"/sanitizer/reports/$component/$module/$subdir/index.html\">Docs</a>]\n";
    print F "</p>\n";
  }

  print F "<h1>$title</h1>\n";
  print F "<p>Not processed per IGNORESUBS directive found in $component/.krazy or $module/.krazy file.<br>\n";

  print F " ...as of ";
  print F asOf();
  print F "</p>\n";
  print F "<ol>\n";

  print F "</ol>\n";
  print F "</div>\n";
  print F "</div>\n";
  print F "<div id=\"footer\">\n";
  print F "<p>Site content Copyright 2005-2010 by Adriaan de Groot,<br/>\n";
  print F "except images as indicated.</p>\n";
  print F "</div>\n";
  print F "</body>\n";
  print F "</html>\n";

  close F;
}

#==============================================================================
# Help function: print help message and exit.
sub Help {
  &Version();
  print "KDE source code checking for the English Breakfast Network (EBN)\n\n";
  print "Usage: $Prog [OPTION]... FILE...\n";
  print "  --help              display help message and exit\n";
  print "  --version           display version information and exit\n";
  print "  --dry-run           don't execute the checks; only show what would be run\n";
  print "  --config <file>     settings file in YAML format\n";
  print "  --nodb              don't execute any database commands\n";
  print "  --dbgen [id]        use the specified value for the database generationId.\n";
  print "                      Do NOT use this option unless you know what you're doing.\n";
  print "  --nosaxon           don't execute the xslt transformation\n";
  print "  --with-sloccount    Run sloccount in each module\n";
  print "  --component [comp]  component to process (default=\"kde-4.x\")\n";
  print "  --module [mod1<,mod2...>]\n";
  print "                      comma-separated list of modules to process (default=ALL)\n";
  print "  --title [title]     the title to print on the output reports\n";

  print "\n";
  exit 0 if $help;
}

# Version function: print the version number and exit.
sub Version {
  print "$Prog, version $VERSION\n";
  exit 0 if $version;
}

__END__

#==============================================================================

=head1 NAME

krazy2xml - KDE source code checking for the English Breakfast Network (EBN)

=head1 SYNOPSIS

krazy2xml [options]

=head1 DESCRIPTION

krazy2xml is a big wrapper around krazy(1) for no obvious use other than
generating Code Checking Quality reports on the English Breakfast Network.

=head1 OPTIONS

=over 4

=item B<--help>

Print help message and exit.

=item B<--version>

Print version information and exit.

=item B<--config> <settings.yaml>

Read system settings from the specified settings.yaml configuration file.

=item B<--dry-run>

With this option the checker programs aren't run; instead, the command line
for each check that would be run is printed.

=item B<--nodb>

Set this option to turn-off all database operations.

=item B<--dbgen>=[id]

Use the specified value for the database generationId.

The value is intended to be computed by the database being
used and then passed into this program, rather than this
program computing the value itself.

Do B<NOT> use this option unless you know what you're doing.

=item B<--component>=[comp]

Only run for the specified KDE component. By default, the kde-4.x
component is assumed.

=item B<--module>=[mod1,<mod2>,...<modN>]

Only run for the specified KDE module within the component.
By default all modules within the component are processed.

=item B<--title>=title

A title string to print onto each report page. If this option is not provided,
a title for each page is generated that contains the source component,
module and subdir.

=back

=head1 EXAMPLES

=over 4

=item Process all modules within component extragear

 % krazy2xml --component=extragear

=item Process module kdelibs within component kde-4.0

 % krazy2xml --component=kde-4.0 --module=kdelibs

=back

=head1 ENVIRONMENT

KRAZY_PLUGIN_PATH - this is a colon-separated list of paths which is
searched when locating plugins. By default, plugins are searched for in
the path F<$TOP/lib/krazy2/krazy-plugins:krazy-plugins>.

KRAZY_EXTRA_PATH - this is a colon-separated list of paths which is
searched when locating "extra" plugins. By default, the "extras" are searched
for in the path F<$TOP/lib/krazy2/krazy-extras:krazy-extras>.

KRAZY_SET_PATH - this is a colon-separated list of paths which is
searched when locating checker sets. By default, the sets are searched
for in the path F<$TOP/lib/krazy2/krazy-sets:krazy-sets>.

where $TOP is the top-level installation directory (eg. F</usr/local>, F</usr/local/Krazy2>)

=head1 EXIT STATUS

In normal operation, krazy2xml exits with status 0.

If a command line option was incorrectly provided, krazy2xml exits with
status=1.

If krazy2 was envoked with the B<--help> or B<--version> options it
will exit with status=0.

=head1 COPYRIGHT

Copyright (c) 2005-2010 by Allen Winter <winter@kde.org>

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.

=head1 FILES

krazyrc(3) files are processed in descending hierarchical order
with each sublevel setting appending in a logical way to the previous
level settings. The following files are read:
 F<component/.krazy>
 F<component/module/.krazy>
 F<component/module/subdir/.krazy>

=head1 SEE ALSO

krazyrc(3), krazy2(1), krazy2all(1)

=head1 AUTHORS

Allen Winter, <winter@kde.org>

=cut
