#! @PERL@
#-*- perl -*-

# Copyright (C) 2000, 2001 R Development Core Team
#
# 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, 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.
#
# A copy of the GNU General Public License is available via WWW at
# http://www.gnu.org/copyleft/gpl.html.	 You can also obtain it by
# writing to the Free Software Foundation, Inc., 59 Temple Place,
# Suite 330, Boston, MA  02111-1307  USA.

# Send any bug reports to r-bugs@r-project.org

use Cwd;
use File::Basename;
use File::Copy;
use File::Find;
use File::Path;
use Getopt::Long;
use R::Dcf;
use R::Utils;
use R::Rd;
use Text::DelimMatch;

## don't buffer output
$| = 1;

my $revision = ' $Revision: 1.51 $ ';
my $version;
my $name;
$revision =~ / ([\d\.]*) /;
$version = $1;
($name = $0) =~ s|.*/||;

my $LATEX = '@LATEX@';
my $MAKE = '@MAKE@';

## Options
my $opt_clean = 1;
my $opt_examples = 1;
my $opt_tests = 1;
my $opt_latex = 1;
my $opt_use_gct = 0;
my $opt_codoc = 1;

my @knownoptions = ("help|h", "version|v", "outdir|o:s", "library|l:s",
		    "no-clean", "no-examples", "no-tests", "no-latex",
		    "use-gct" => \$opt_use_gct, "no-codoc");
GetOptions (@knownoptions) || usage();

R_version("R add-on package checker", $version) if $opt_version;
usage() if $opt_help;

$opt_clean = 0 if $opt_no_clean;
$opt_examples = 0 if $opt_no_examples;
$opt_tests = 0 if $opt_no_tests;
$opt_latex = 0 if $opt_no_latex;
$opt_codoc = 0 if $opt_no_codoc;

my $startdir = cwd();
$opt_outdir = $startdir unless $opt_outdir;
chdir($opt_outdir) ||
    die "Error: cannot change to directory \`$opt_outdir'\n";
my $outdir = cwd();
chdir($startdir);

my $R_HOME = $ENV{'R_HOME'} ||
    die "Error: environment variable R_HOME not found\n";

my $R_CMD = $ENV{'R_CMD'} ||
    die "Error: environment variable R_CMD not found\n";

my $OS = R_getenv("R_OSTYPE", "unix");
my $R_exe = "${R_HOME}/bin/R";
my $TMPDIR = R_getenv("TMPDIR", "/tmp");
$WINDOWS = ($OS eq "windows");
if($WINDOWS) {
    $TMPDIR = R_getenv("TMPDIR", "/TEMP");
    die "Please set TMPDIR to a valid temporary directory\n" 
      unless (-e $TMPDIR);
    $R_exe = "Rterm.exe";
    $LATEX = "latex";
    $MAKE = "make";
}
my $cfile = "$TMPDIR/Rcmd.$$";

my $R_LIBS = $ENV{'R_LIBS'};
my $library;
if($opt_library) {
    chdir($opt_library) ||
	die "Error: cannot change to directory \`$opt_library'\n";
    $library = cwd();
    if($WINDOWS) {
	$ENV{'R_LIBS'} = "$library;$R_LIBS";
    }
    else {
	$ENV{'R_LIBS'} = "$library:$R_LIBS";
    }
	
    chdir($startdir);
}

my $R_opts = "--vanilla";

if($opt_latex) {
    my $log = new R::Logfile();
    $log->checking("for working latex");
    open(texfile, "> $TMPDIR/Rtextest$$.tex")
      or die "Error: cannot write to Rtextest$$.tex\n";
    print texfile "\\documentclass\{article\}\\begin\{document\}" .
	"test\\end\{document\}\n";
    close texfile;
    chdir($TMPDIR);
    if(Rsystem("$LATEX Rtextest$$ > Rtextest$$.out")) {
	$log->result("NO");
	$HAVE_LATEX = 0;
    } else {
	$log->result("OK");
	$HAVE_LATEX = 1;
    }
    unlink(<Rtextest$$.*>);
    chdir($startdir);
    $log->close();
}

## get the valid keywords for later ...
my %standard_keywords = ();
open(KEYWORDS, "< ${R_HOME}/doc/KEYWORDS.db")
  or die "Cannot open \`KEYWORDS.db'";
while(<KEYWORDS>) {
    if(/^.*\|([^:]*):.*/) {
	$standard_keywords{$1} = 1;
    }
}
close KEYWORDS;

## this is the main loop over all packages to be checked
$#ARGV >= 0 or die "Error: no packages were specified";
my $pkg;
foreach $pkg (@ARGV) {
    ## $pkg should be the path to the package (bundle) root source
    ## directory, either absolute or relative to $startdir.
    ## $pkgdir is the corresponding absolute path.
    ## $pkgname is the name of the package (bundle).
    chdir($startdir);
    $pkg =~ s/\/$//;
    (-d $pkg) || die "Error: package dir \`$pkg' does not exist";
    chdir($pkg) ||
	die "Error: cannot change to directory \`$pkg'\n";
    my $pkgdir = cwd();
    my $pkgname = basename($pkgdir);
    chdir($startdir);

    my $pkgoutdir = "$outdir/$pkgname.Rcheck";
    rmtree($pkgoutdir) if ($opt_clean && (-d $pkgoutdir)) ;
    if(! -d $pkgoutdir) {
	if(! mkdir($pkgoutdir, 0755)) {
	    die("Error: cannot create directory \`$pkgoutdir'\n");
	    exit(1);
	}
    }
    
    $log = new R::Logfile("$pkgoutdir/00check.log");
    $log->message("using log directory \`$pkgoutdir'");
    
    if(! $opt_library) {
	$library = $pkgoutdir;
	if($WINDOWS) {
	    $ENV{'R_LIBS'} = "$library;$R_LIBS";
	}
	else {
	    $ENV{'R_LIBS'} = "$library:$R_LIBS";
	}
    }

    my $description;
    my $is_base_pkg = 0;
    ## Package sources from the R distribution are special.  They have a
    ## `DESCRIPTION.in' file (instead of `DESCRIPTION'), with Version
    ## field containing `@VERSION@' for substitution by configure.  We
    ## test for such packages by looking for `DESCRIPTION.in' with
    ## Priority `base', and skip the installation test for such
    ## packages.
    if(-r "$pkgdir/DESCRIPTION.in") {
	$description = new R::Dcf("$pkgdir/DESCRIPTION.in");
	if($description->{"Priority"} eq "base") {
	    $log->message("looks like \`${pkgname}' is a base package");
	    $log->message("skipping installation test");
	    $is_base_pkg = 1;
	}
    }

    if(!$is_base_pkg) {
	print "\n";
	if(system("${R_CMD} INSTALL -l $library $pkgdir")) {
	    $log->error("installation failed");
	    exit(1);
	}
	print("\n");
    
	$log->checking("for file \`$pkgname/DESCRIPTION'");
	if(-r "$pkgdir/DESCRIPTION") {
	    $description = new R::Dcf("$pkgdir/DESCRIPTION");
	    $log->result("OK");
	}
	else {
	    $log->result("NO");
	    exit(1);
	}
    }

    my $is_bundle = 0;
    if($description->{"Contains"}) {
	$log->message("looks like \`${pkgname}' is a package bundle");
	$is_bundle = 1;
	my @bundlepkgs = split(/\s+/, $description->{"Contains"});
	my $ppkg = "";
	foreach $ppkg (@bundlepkgs) {
	    $log->message("checking \`$ppkg' in bundle \`$pkgname'");
	    $log->setstars("**");
	    chdir($startdir);
	    check_pkg("$pkgdir/$ppkg", $pkgoutdir, $startdir, $library,
		      $is_bundle, $description, $log, $is_base_pkg);
	    $log->setstars("*");
	}
    }
    else {
	$is_bundle = 0;
	chdir($startdir);
	check_pkg($pkgdir, $pkgoutdir, $startdir, $library,
		  $is_bundle, $description, $log, $is_base_pkg);
    }
    
    if($log->{"warnings"}) {
	print("\n") ;
	$log->summary();
    }
    $log->close();
    print("\n");
}

#**********************************************************

sub check_pkg {

    my ($pkg, $pkgoutdir, $startdir, $library,
	$in_bundle, $description, $log, $is_base_pkg) = @_;
    my ($pkgdir, $pkgname);
    
    ## $pkg is the argument we received from the main loop.
    ## $pkgdir is the corresponding absolute path,
    ## $pkgname the name of the package.
    ## Note that we need to do repeat the checking from the main loop in
    ## the case of package bundles (and we could check for this).
    $log->checking("package directory");
    chdir($startdir);
    $pkg =~ s/\/$//;
    if(-d $pkg) {
	chdir($pkg) ||
	    die "Error: cannot change to directory \`$pkg'\n";
	$pkgdir = cwd();
	$pkgname = basename($pkgdir);
    }
    else {
	$log->error("Error: package dir \`$pkg' does not exist");
	exit 1;
    }
    $log->result("OK");
    
    chdir($pkgdir);

    ## check for sufficient file permissions (Unix only).
    
    if($OS eq "unix") {
	$log->checking("for sufficient file permissions");
	my @badfiles = ();
	my @excludepatterns = ("^.Rbuildignore\$", "\~\$", "\.swp\$",
			       "^.*/\.#[^/]*\$", "^.*/#[^/]*#\$");
	if(-f "./.Rbuildignore") {
	    open(RBUILDIGNORE, "./.Rbuildignore");
	    while(<RBUILDIGNORE>) {
		chop;
		push(@excludepatterns, $_);
	    }
	    close(RBUILDIGNORE);
	}
	sub findWrongPerms {
	    my $filename = $File::Find::name;
	    $filename =~ s/^[^\/]*\///;
	    foreach $p (@excludepatterns) {
		return 0 if($filename =~ /$p/);
	    }
	    if(-d $_ && (((stat $_)[2] & 00755) < oct("755"))) {
		push @badfiles, $File::Find::name;
	    }
	    if(-f $_ && (((stat $_)[2] & 00644) < oct("644"))) {
		push @badfiles, $File::Find::name;
	    }
	}
	find(\&findWrongPerms, ".");
	if($#badfiles >= 0) {
	    $log->error("  Found the following files with " .
			"insufficient permissions:");
	    $log->message("    " . join("\n    ", @badfiles));
	    $log->message("  Please fix permissions and try again.");
	    exit(1);
	}
	$log->result("OK");
    }

    if($in_bundle) {		# join DESCRIPTION and DESCRIPTION.in
	if(-r "DESCRIPTION.in") {
	    $log->checking("for file \`DESCRIPTION.in'");
	    my $description_in = new R::Dcf("DESCRIPTION.in");
	    my $key;
	    foreach $key (keys(%$description)) {
		$description_in->{$key} = $description->{$key};
	    }
	    ## from now on use $description_in instead of $description
	    ## in this subroutine
	    $description = $description_in;
	    $log->result("OK");
	    $log->message("joining DESCRIPTION and DESCRIPTION.in");
	}
	else {
	    $log->result("NO");
	    exit(1);
	}
    }

    ## check mandatory entries in DESCRIPTION:
    ##   Package, Version, License, Description, Title, Author,
    ##   Maintainer.

    $log->checking("DESCRIPTION Package field");
    if(! $description->{"Package"}) {
	$log->error("no DESCRIPTION Package field found");
	exit(1);
    }
    my $tmp = $description->{"Package"};
    if($description->{"Package"} ne $pkgname) {
	$log->error("DESCRIPTION Package field differs from dir name");
	exit(1);
    }
    $log->result("OK");

    $log->checking("DESCRIPTION Version field");
    if(! $description->{"Version"}) {
	$log->error("no DESCRIPTION Version field found");
	exit(1);
    }
    if(($description->{"Version"} =~ /[^\d\.\-]/) && !$is_base_pkg) {
	## Package sources from the R distribution have `@VERSION@' in
	## their `DESCRIPTION.in' files ...
	$log->error("Version may only contain digits, \`.' and \`-'");
	exit(1);
    }
    $log->result("OK");
    
    my $field;
    foreach $field (qw(License Description Title Author)) {
	$log->checking("DESCRIPTION $field field");
	if(! $description->{$field}) {
	    $log->error("no DESCRIPTION $field field found");
	    exit(1);
	}
	$log->result("OK");
    }

    $log->checking("DESCRIPTION Maintainer field");
    if(!$description->{"Maintainer"}) {
	$log->error("no DESCRIPTION Maintainer field found");
	exit(1);
    }
    elsif($description->{"Maintainer"} !~ /^[^<>]*<[^<>]+> *$/) {
	$log->warning();
	$log->message("  malformed Maintainer field");
    }
    else {
	$log->result("OK");
    }

    if($description->{"Depends"}) {
	$log->checking("DESCRIPTION Depends field");
	my @dependencies = split(/\,/, $description->{"Depends"});
	my $any = 0;
	foreach my $dep (@dependencies) {
	    $dep =~ /^\s*(\w+)(\s*\(([^) ]+)\s+([^) ]+)\))?\s*$/;
	    ## The entry is malformed if there is no match, or there is
	    ## a match but the dep_op is different from `<=' or `>', or
	    ## the dep_version does not only consist of digits, `.' or
	    ## `-'.
	    if(!($1) || ($2 && (!(($3 eq "<=") || ($3 eq ">="))
				|| ($4 =~ /[^\d\.\-]/)))) {
		$any++;
		break;
	    }
	}
	if($any == 0) {
	    $log->result("OK")
	}
	else {
	    $log->warning();
	    $log->message("  malformed Depends field");
	}
    }

    ## check usage of library.dynam (if any)

    if(-d "R") {
	$log->checking("R files for library.dynam");
	# only need to check the installed file
	open Rfile, "< $library/$pkgname/R/$pkgname"
	  or die "cannot open \`$library/$pkgname/R/$pkgname' for reading\n";
	my $any = 0;
	while(<Rfile>) {
	    if(/library.dynam\(\"(.*?)\"/o) {
		my $arg = $1;
		if($arg =~ /\.so$/) {
                    $any++;
                    $log->error("  library.dynam used with extension \`.so'");
	            exit(1);
                }
		if($arg =~ /\.sl$/) {
                    $any++;
                    $log->error("  library.dynam used with extension \`.sl'");
	            exit(1);
                }
		if($arg =~ /\.dll$/) {
                    $any++;
                    $log->error("  library.dynam used with extension \`.dll'");
	            exit(1);
                }
	    }
	}
        close Rfile;
	$log->result("OK") unless $any;
    }

    ## check R documentation files
    
    if(-d "man") {
	$log->checking("Rd files");
	my @rdfiles;
	my @badfiles;
	while(<man/*.[rR]d>) {
	    if(/</) {
		push @badfiles, $_;
	    }
	    else {
		push @rdfiles, $_;
	    }
	}
	if(-d "man/$OS") {
	    while(<man/$OS/*.[rR]d>) {
		if(/</) {
		    push @badfiles, $_;
		}
		else {
		    push @rdfiles, $_;
		}
	    }
	}
	if($#badfiles >= 0) {
	    $log->error("  Cannot handle Rd file names containing \`<'.\n" .
			"  These are not legal file names on all R platforms.\n" .
			"  Please rename the following files and try again:");
	    $log->message("    " . join("\n    ", @badfiles));
	    exit(1);
	}
	
	@rdfiles = sort(@rdfiles);
	my $file;
	my @mandatoryTags = qw(name alias title description keyword);
	my @uniqueTags = qw(name title description usage arguments
			    format details value references source
			    seealso examples note author synopsis);
	my @badbraces;	
	my %badmandatory;
	my %badunique;
	my %badkeywords;
	
	## create hash allTags with all tags found in mandatory Tags and
	## uniqueTags 
	my $tag;
	my %allTags;
	foreach $tag (@mandatoryTags, @uniqueTags) {
	    $allTags{$tag}++;
	}
	
	foreach $rdfile (@rdfiles) {
	    open RDFILE, "< $rdfile"
	      or die "cannot open \`$rdfile' for reading\n";

	    my %keywords;
	    my %tagcount;
	    while(<RDFILE>) {
		my $line = $_;
		foreach $tag (keys %allTags) {
		    if($line =~ /^\s*\\$tag/) {
			$tagcount{$tag}++;
		    }
		}
                if($line =~ /^\s*\\keyword{\s*([^}]*[^}\s])\s*}.*/) {
                    $keywords{$1} = 1;
                }
	    }
	    close RDFILE;
	    
	    foreach $tag (@mandatoryTags) {
		push(@{$badmandatory{$tag}}, $rdfile)
		    unless $tagcount{$tag}>0;
	    }
	    foreach $tag (@uniqueTags) {
		push(@{$badunique{$tag}}, $rdfile)
		    unless $tagcount{$tag}<=1;
	    }
            foreach $key (keys(%keywords)) {
                push(@{$badkeywords{$rdfile}}, $key)
                    unless $standard_keywords{$key};
            }

	    ## Check for unbalanced braces.
	    my $text = "";
	    ## Read the Rd file via Rdpp, and get rid of the \alias
	    ## entries as these may have unbalanced braces (Paren.Rd).
	    my @lines = split(/\n/, R::Rd::Rdpp($rdfile, $OS));
	    foreach $line (@lines) {
		$text .= "\n$line" unless ($line =~ /^\\alias/);
	    }
	    $text .= "\n";
	    my $dc = new Text::DelimMatch;
	    $dc->delim("\{", "\}");
	    $dc->escape("\\");
	    $text =~ s/([^\\])%.*\n/$1\n/g; # ???
	    ## Now loop through matching pairs of braces.
	    while($dc->match($text)) {
		$text = $dc->post_matched;
	    }
	    ## Get rid of trailing comments.
	    $text =~ s/^%.*\n/\n/gs;
	    $text =~ s/\n%.*\n/\n/gs;
	    ## Anything left in $text means unbalanced.
	    if(!($text =~ /^\s*$/)) {
		push(@badbraces, $rdfile);
	    }
	    
	}

	my $any = 0;

	if($#badbraces >= 0) {
	    $log->warning("") unless $any;
	    $any++;
	    $log->message("  Rd files with unbalanced braces:");
	    $log->message("    " . join("\n    ", @badbraces));
	}

	foreach $tag (@mandatoryTags) {
	    if(exists $badmandatory{$tag}) {
		$log->warning("") unless $any;
		$any++;
		$log->message("  Rd files without \`${tag}':");
		$log->message("    " .
			      join("\n    ", @{$badmandatory{$tag}}));
	    }
	}
	foreach $tag (@uniqueTags) {
	    if(exists $badunique{$tag}) {
		$log->warning("") unless $any;
		$any++;
		$log->message("  Rd files with duplicate \`${tag}':");
		$log->message("    " .
			      join("\n    ", @{$badunique{$tag}}));
	    }
	}
        if(keys(%badkeywords)) {
	    $log->warning("") unless $any;
	    $any++;
            foreach $file (keys(%badkeywords)) {
                $log->message("  non-standard keyword(s) in \`$file': " .
                              join(" ", @{$badkeywords{$file}}));
            }
        }

	$log->result("OK") unless $any;
    }

    ## check for undocumented objects

    if(((-d "R") || (-d "data")) && -d "man") {
	$log->checking("for undocumented objects");

	my $Rcmd = "$TMPDIR/Rcmd.$$";
	my $Rout = "$TMPDIR/Rout.$$";
	open Rcmd, "> $Rcmd" or die "Cannot write to \`$Rcmd'\n";
        print Rcmd "undoc(dir = \"${pkgdir}\")\n";
        close Rcmd;
        system("${R_exe} ${R_opts} < ${Rcmd} > ${Rout}");
        my @out;
        open Rout,  "< $Rout";
        while (<Rout>) {chomp; push(@out, $_);}
        close Rout;
        unlink($Rcmd);
        unlink($Rout);

	my @err = grep {s/^Error *//} @out;
	@out = grep {/^ *\[/} @out;
	if($#err<0) {
	    if($#out>=0) {
		$log->warning();
                $log->print(join("\n", @out) . "\n");
	    }
	    else {
		$log->result("OK");
	    }
	}
	else {
	    $log->error("  " . join("\n ", @err));
	    exit(1);
	}
    }

    ## check for code/documentation mismatches

    if($opt_codoc && ((-d "R") || (-d "data")) && -d "man") {
	$log->checking("for code/documentation mismatches");

	my $Rcmd = "$TMPDIR/Rcmd.$$";
	my $Rout = "$TMPDIR/Rout.$$";
	open Rcmd, "> $Rcmd" or die "Cannot write to \`$Rcmd'\n";
        print Rcmd "codoc(dir = \"${pkgdir}\")\n";
        close Rcmd;
        system("${R_exe} ${R_opts} --quiet < ${Rcmd} > ${Rout}");
        my @out;
        open Rout,  "< $Rout";
        while(<Rout>) {chomp; push(@out, $_);}
        close Rout;
        unlink($Rcmd);
        unlink($Rout);

	## <FIXME>
	## With the current heuristics codoc() may throw an error even
	## though the documentation is valid.  Change this when we know
	## what must be valid R code, similar to the above for undoc().
	@out = grep(!/^(\>|Using|list| *$)/, @out);
	## Also, the `\t+' in the above is ugly, but needed to catch the
	## pretty-printing in errorcall() for non-NULL call ...
	if($#out>=0) {
	    $log->warning();
	    $log->print(join("\n", @out) . "\n");
	}
	else {
	    $log->result("OK");
	}
	## </FIXME>
    }

    chdir($pkgoutdir);

    ## run the examples

    if($opt_examples && <$library/$pkgname/R-ex/*.R>) {
	$log->creating("${pkgname}-Ex.R");
        my $iszipped = 0;
	my $cmd;
	
        if(-e "${library}/${pkgname}/R-ex/R-ex.zip") {
            $iszipped = 1;
            system("unzip ${library}/${pkgname}/R-ex/R-ex.zip " .
                   "-d ${library}/${pkgname}/R-ex");
        }
        if($WINDOWS) {
            ## avoid Rcmd as line may be too long after expansion.
            $cmd = "perl ${R_HOME}/bin/massage-Examples ".
  		     "${pkgname} ${library}/${pkgname}/R-ex/*.R ".
  		     "> ${pkgname}-Ex.R";
        } else {
	    $cmd = "${R_CMD} perl ${R_HOME}/share/perl/massage-Examples.pl ".
	             "${pkgname} ${library}/${pkgname}/R-ex/*.R ".
		     "> ${pkgname}-Ex.R";
        }

 	if(Rsystem($cmd)) {
  	    $log->error();
  	    exit(1);
        }
        if($iszipped) {
            unlink(<${library}/${pkgname}/R-ex/*.R>);
        }

	$log->result("OK");
	$log->checking("examples");
	
	if($opt_use_gct) {
            $cmd = "(echo 'gctorture(TRUE)'; cat ${pkgname}-Ex.R) " .
		      "| ${R_exe} ${R_opts} > ${pkgname}-Ex.Rout";
	}
	else {
            $cmd = "${R_exe} ${R_opts} " .
		      "< ${pkgname}-Ex.R > ${pkgname}-Ex.Rout";
	}
	if(Rsystem($cmd)) {
	    $log->error("running examples failed");
	    exit(1);
	}
	$log->result("OK");
    }

    ## run the package-specific tests

    if($opt_tests && (-d "$pkgdir/tests")) {
        $log->checking("tests");
        my $testsrcdir = "$pkgdir/tests";
        my $testdir = "$pkgoutdir/tests";
        if(! -d $testdir) {
            if(! mkdir($testdir, 0755)) {
	        die("could not create directory $testdir\n");
	        exit(1);
	    }
        }
        chdir($testdir);
        while(<$testsrcdir/*>) {
            copy($_, basename($_));
        }
        my $makefiles = "-f ${R_HOME}/share/make/tests.mk";
        my $makevars = "";
        if($WINDOWS && (-r "$testsrcdir/Makefile.win")) {
            $makefiles .= " -f $testsrcdir/Makefile.win";
        }
        elsif(-r "$testsrcdir/Makefile") {
            $makefiles .= " -f $testsrcdir/Makefile";
        }
        if($WINDOWS && (-r "$testsrcdir/Makevars.win")) {
            $makevars = " -f $testsrcdir/Makevars.win";
        }
        elsif(-r "$testsrcdir/Makevars") {
            $makevars = " -f $testsrcdir/Makevars";
        }
        else {
            open makevars, "> Makevars";
            print makevars "makevars = -f Makevars\n";
            print makevars "srcdir = $testsrcdir\n";
            ## at least windows does not pass env correctly to make
            print makevars "R_LIBS = $ENV{'R_LIBS'}\n"; 
            print makevars "VPATH = \$(srcdir)\n\n";
            print makevars "test-src-1 =";
            while(<$testdir/*.R>) {
                print makevars "\\\n " . basename($_);
            }
            print makevars "\n";
            print makevars "test-src-auto =";
            while(<$testdir/*.Rin>) {
                s/Rin$/R/;
                print makevars "\\\n " . basename($_);
            }
            print makevars "\n";
	    print makevars "USE_GCT = $opt_use_gct\n";
            close makevars;
            $makevars = " -f Makevars";
        }
        print "\n";
        if(system("$MAKE $makefiles $makevars")) {
            $log->error();
            exit(1);
        }
        chdir($pkgoutdir);
        $log->result("OK");
    }

    ## run LaTeX on the manual
            
    if($opt_latex && (-d "$library/$pkgname/latex")) {
        if($WINDOWS) {
            $ENV{'TEXINPUTS'} = "$R_HOME/share/texmf;$ENV{'TEXINPUTS'}";
        }
        else {
            $ENV{'TEXINPUTS'} = "$R_HOME/share/texmf:$ENV{'TEXINPUTS'}";
        }
        $log->creating("${pkgname}-manual.tex");
        open manual, "> ${pkgname}-manual.tex";
        print manual "\\documentclass\{article\}\n" .
            "\\usepackage[ae, hyper]\{Rd\}\n".
                "\\begin\{document\}\n";
        while(<$library/$pkgname/latex/*.tex>) {
            my $file = $_;
            open file, "< $file" ||
                $log->error("cannot open file \`$file' for reading");
            while(<file>) {
                print manual $_;
            }
            close file;
        }
        print manual "\\end\{document\}\n";
        close manual;
        $log->result("OK");
        if($HAVE_LATEX) {
            $log->checking("${pkgname}-manual.tex");
            print "\n";
            if(Rsystem("$LATEX ${pkgname}-manual")) {
                $log->error();
                exit(1);
            }
            $log->result("OK");
        }
    }
}
            

#**********************************************************

sub Rsystem 
{
    my $cmd = $_[0];
    if($WINDOWS) {
	open cfile,  "> $cfile" or die "Cannot write to \`$cfile'\n";
	print cfile "$cmd\n";
	close cfile;
	$res = system("sh $cfile");
	unlink($cfile);
	return $res;
    } else {
	return system($cmd);
    }
}

#**********************************************************

sub usage {
    print STDERR <<END;
Usage: R CMD $name [options] pkgdirs

Check R packages from package sources in the directories specified by
pkgdirs.  A variety of diagnostic checks on directory structure, index
and control files are performed.  The package is installed into the log
directory (which includes the translation of all Rd files into several
formats), and the Rd files are tested by LaTeX (if available).  All
examples and tests provided by the package are tested if they run
succesfully.

Options:
  -h, --help            print short help message and exit
  -v, --version         print version info and exit
  -l, --library=LIB     library directory used for test installation
                        of packages (default is outdir)
  -o, --outdir=DIR      directory used for logfiles, R output, etc.
                        (default is \`pkg.Rcheck\' in current directory,
			where \`pkg\' is the name of the package checked)
      --no-clean        do not clean outdir before using it
      --no-codoc        do not check for code/documentation mismatches
      --no-examples     do not run the examples in the Rd files
      --no-tests        do not run code in tests subdirectory
      --no-latex        do not run LaTeX on help files
      --use-gct         use \`gctorture(TRUE)' when running examples/tests

By default, all test sections are turned on.

Email bug reports to <r-bugs\@r-project.org>.
END
    exit 0;
}