#! /usr/bin/perl
#-*- perl -*-
# Copyright (C) 2000-2004 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::Compare;
use File::Find;
use File::Path;
use File::Copy;
use Getopt::Long;
use IO::File;
use R::Dcf;
use R::Logfile;
use R::Rd;
use R::Utils;
use R::Vars;
use Text::DelimMatch;
use Text::Wrap;

## Don't buffer output.
$| = 1;

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

R::Vars::error("R_HOME", "R_EXE");

my $WINDOWS = ($R::Vars::OSTYPE eq "windows");

my @exclude_patterns = R::Utils::get_exclude_patterns();

my @known_options = ("help|h", "version|v", "binary", "no-docs",
		     "use-zip", "use-zip-help", "use-zip-data",
		     "force", "no-vignettes");

if($WINDOWS) {
    die "Please set TMPDIR to a valid temporary directory\n"
	unless (-e ${R::Vars::TMPDIR});
    @known_options = ("help|h", "version|v", "binary", "docs:s",
		      "auto-zip",
		      "use-zip", "use-zip-help", "use-zip-data",
		      "force", "no-vignettes");
}

GetOptions(@known_options) or usage();

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

## Use system default unless explicitly specified otherwise.
$ENV{"R_DEFAULT_PACKAGES"} = "";

my $startdir = R_cwd();
my $R_platform = R_getenv("R_PLATFORM", "unknown-binary");
my $gzip = R_getenv("R_GZIPCMD", "gzip");
my $tar = R_getenv("TAR", "tar");
my $libdir = &file_path(${R::Vars::TMPDIR}, "Rinst.$$");

my $INSTALL_opts = "";
$INSTALL_opts .= " --use-zip" if $opt_use_zip;
$INSTALL_opts .= " --use-zip-data" if $opt_use_zip_data;
$INSTALL_opts .= " --use-zip-help" if $opt_use_zip_help;
if($WINDOWS) {
    $INSTALL_opts .= " --docs=$opt_docs" if $opt_docs;
    $INSTALL_opts .= " --auto-zip" if $opt_auto_zip;
} else {
    $INSTALL_opts .= " --no-docs" if $opt_no_docs;
}
## <FIXME>
## Once we have a 'global' log file, use $log->warning() instead of just
## print().
if(!$opt_binary && $INSTALL_opts ne "") {
    print "** Options '$INSTALL_opts' only for '--binary' ignored\n";
}
## </FIXME>

## This is the main loop over all packages to be packaged.
foreach my $pkg (@ARGV) {
    my $is_bundle = 0;
    $pkg =~ s/\/$//;
    my $pkgname = basename($pkg);
    chdir($startdir);

    my $log = new R::Logfile();

    my $description;
    $log->checking("for file '$pkg/DESCRIPTION'");
    if(-r &file_path($pkg, "DESCRIPTION")) {
	$description = new R::Dcf(&file_path($pkg, "DESCRIPTION"));
	$log->result("OK");
    }
    else {
	$log->result("NO");
	exit(1);
    }

    my @bundlepkgs;
    if($description->{"Contains"}) {
	$log->message("looks like '${pkg}' is a package bundle");
	$is_bundle = 1;
	@bundlepkgs = split(/\s+/, $description->{"Contains"});
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("preparing '$ppkg' in bundle '$pkg':");
	    $log->setstars("**");
	    chdir($startdir);
	    prepare_pkg(&file_path("$pkg", "$ppkg"), $is_bundle,
			$description, $log);
	    $log->setstars("*");
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $log->message("cleaning '$ppkg' in bundle '$pkg'");
	    $log->setstars("**");
	    chdir($startdir);
	    cleanup_pkg(&file_path("$pkg", "$ppkg"), $log);
	    $log->setstars("*");
	}
	rmtree("$libdir") if (-d "$libdir");
    } else {
	$is_bundle = 0;
	chdir($startdir);
	$log->message("preparing '$pkg':");
	prepare_pkg("$pkg", $is_bundle, $description, $log);
    }

    chdir($startdir);

    $log->message("removing junk files");
    find(\&unlink_junk_files, $pkg);
	}
	foreach my $ppkg (@bundlepkgs) {
	    $l