#!/usr/bin/perl
##############################################################################
#
#   File Name    - linux-installer
#
#   Description  - Perl Linux installer for mtn-browse
#
#   Author       - A.E.Cooper.
#
#   Legal Stuff  - Copyright (c) 2008 Anthony Edward Cooper
#                  <aecooper@coosoft.plus.com>.
#
#                  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 3 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 software; if not, write to the Free
#                  Software Foundation, Inc., 59 Temple Place - Suite 330,
#                  Boston, MA 02111-1307 USA.
#
##############################################################################
#
##############################################################################
#
#   Global Data For This Module
#
##############################################################################



# ***** DIRECTIVES *****

require 5.008005;

use lib "installer-files";
use strict;
use warnings;

# ***** REQUIRED PACKAGES *****

# Standard Perl and CPAN modules.

use Getopt::Long;
use IO::Dir;
use IO::File;
use Pod::Usage;

# Modules specific to this application.

use DependencyChecker;
use Installer;

# ***** FUNCTIONAL PROTOTYPES *****

# Private routines.

sub program_is_valid($);
#
##############################################################################
#
#   Routine      - Main Body Of Code
#
#   Description  - This is the main body of code for the install script.
#
#   Data         - @_           : The command line arguments.
#                  Return Value : Unix exit code.
#
##############################################################################



{

    my ($confirm,
        $dep_level,
        $dest_dir,
        $dir,
        $file,
        $file_comparison,
        $globs_file,
        $graphviz_dot,
        $help,
        $html_viewer,
        $infile,
        $input,
        $installer,
        $interactive,
        $lib_dest_dir,
        $lib_dir,
        $lib_prefix_dir,
        $locale,
        $man,
        $mas_path,
        $outfile,
        $parser,
        $prefix_dir,
        $prefix_dest_dir,
        $quiet,
        $use_dists_mas_lib,
        $use_labels_in_graph,
        $use_source_view_2);

    # Assign defaults.

    $dep_level = 2;
    $dest_dir = "";
    $file_comparison = "meld";
    $globs_file = "/usr/share/mime/globs";
    $graphviz_dot = "dot";
    $html_viewer = "";
    $prefix_dir = "/usr";
    $lib_dir = "/lib/mtn-browse";
    $use_dists_mas_lib = 1;

    # Process any command line options.

    $parser = Getopt::Long::Parser->new();
    $parser->configure("bundling");
    if (! $parser->getoptions("confirm|c"           => \$confirm,
                              "dep-level=i"         => \$dep_level,
                              "destdir|d=s"         => \$dest_dir,
                              "file-comparison|f=s" => \$file_comparison,
                              "globs-file|g=s"      => \$globs_file,
                              "graphviz-dot=s"      => \$graphviz_dot,
                              "html-viewer=s"       => \$html_viewer,
                              "interactive|i"       => \$interactive,
                              "libdir|l=s"          => \$lib_dir,
                              "prefix|p=s"          => \$prefix_dir,
                              "quiet|q"             => \$quiet,
                              "use-dists-mas!"      => \$use_dists_mas_lib,
                              "use-labels!"         => \$use_labels_in_graph,
                              "help|?"              => \$help,
                              "man|m"               => \$man))
    {
        pod2usage(1);
    }
    pod2usage(0) if ($help);
    pod2usage(-exitstatus => 0, -verbose => 2) if ($man);

    # Do dependency checks if required.

    if ($dep_level > 0)
    {

        my (%deps,
            $err_msg,
            $met);

        # Build up the dependency list.

        %deps = ("Glib"               => 1.144,
                 "Gnome2"             => 1.040,
                 "Gnome2::Canvas"     => 1.002,
                 "Gnome2::VFS"        => 1.080,
                 "Gtk2"               => 1.146,
                 "Gtk2::GladeXML"     => 1.006,
                 "Gtk2::SourceView2"  => 0.10,
                 "Locale::TextDomain" => 1.16);
        $deps{"Monotone::AutomateStdio"} = 1.02 if (! $use_dists_mas_lib);

        # Do the check.

        $met = DependencyChecker::check(\%deps, \$err_msg);

        # If the error was down to a missing Gtk2::SourceView2 package then
        # look for Gtk2::SourceView.

        if ($met)
        {
            $use_source_view_2 = 1;
        }
        else
        {
            if ($err_msg =~ m/Gtk2::SourceView2 [0-9.]+ not found\.$/)
            {
                delete($deps{"Gtk2::SourceView2"});
                $deps{"Gtk2::SourceView"} = 1.000;
                $met = DependencyChecker::check(\%deps, \$err_msg);
            }
        }

        # Deal with the result depending upon what the user wants.

        if (! $met)
        {
            printf(STDERR "%s: %s\n",
                   ($dep_level == 2) ? "Error" : "Warning",
                   $err_msg);
            exit(1) if ($dep_level < 3);
        }
        exit(0) if ($dep_level == 1);

    }

    # Do interactive stuff and validate options.

    print("Linux installer for mtn-browse\n\n") if ($interactive);
    while (1)
    {
        if ($interactive)
        {
            print("Where is your MIME globs file? [" . $globs_file . "]: ");
            chomp($input = <STDIN>);
            ($input) = ($input =~ m/^\s*(.*)\s*$/);
            $globs_file = $input if ($input ne "");
        }
        if (! -f $globs_file)
        {
            print("Error: File `" . $globs_file . "' is unreadable.\n");
            exit(1) if (! $interactive);
            next;
        }

        if ($interactive)
        {
            print("Where will it be run from (PREFIX_DIR)? [" . $prefix_dir
                  . "]: ");
            chomp($input = <STDIN>);
            ($input) = ($input =~ m/^\s*(.*)\s*$/);
            $prefix_dir = $input if ($input ne "");
        }
        if (-e $prefix_dir && ! -d $prefix_dir)
        {
            print("Error: Directory `" . $prefix_dir . "' is not a "
                  . "directory.\n");
            exit(1) if (! $interactive);
            next;
        }

        if ($interactive)
        {
            print("Where do you want to copy it to (DEST_DIR)? [" . $dest_dir
                  . "]: ");
            chomp($input = <STDIN>);
            ($input) = ($input =~ m/^\s*(.*)\s*$/);
            $dest_dir = $input if ($input ne "");
        }
        $prefix_dest_dir = $dest_dir . $prefix_dir;
        if (-e $prefix_dest_dir
            && (! -d $prefix_dest_dir || ! -w $prefix_dest_dir))
        {
            print("Error: Directory `" . $prefix_dest_dir . "' is unwritable "
                  . "or not a directory.\n");
            exit(1) if (! $interactive);
            next;
        }

        if ($interactive)
        {
            print("Where should the libraries go (LIB_DIR)? [<PREFIX_DIR>"
                  . $lib_dir . "]: ");
            chomp($input = <STDIN>);
            ($input) = ($input =~ m/^\s*(.*)\s*$/);
            $lib_dir = $input if ($input ne "");
        }
        $lib_prefix_dir = $prefix_dir . "/" . $lib_dir;
        $lib_prefix_dir =~ s/\/\//\//g;
        $lib_dest_dir = $dest_dir . $prefix_dir. "/" . $lib_dir;
        $lib_dest_dir =~ s/\/\//\//g;
        if (-e $lib_dest_dir && (! -d $lib_dest_dir || ! -w $lib_dest_dir))
        {
            print("Error: Directory `" . $lib_dest_dir . "' is unwritable or "
                  . "not a directory.\n");
            exit(1) if (! $interactive);
            next;
        }

        if ($interactive)
        {
            print("Program to use for file comparisons (FILE_COMPARE_CMD)? "
                  . "[" . $file_comparison . "]: ");
            chomp($input = <STDIN>);
            ($input) = ($input =~ m/^\s*(.*)\s*$/);
            $file_comparison = $input if ($input ne "");
        }
        program_is_valid($file_comparison) if ($file_comparison ne "");

        if ($interactive)
        {
            print("Path to Graphviz dot program (GRAPHVIZ_LAYOUT_PROGRAM)? ["
                  . $graphviz_dot . "]: ");
            chomp($input = <STDIN>);
            ($input) = ($input =~ m/^\s*(.*)\s*$/);
            $graphviz_dot = $input if ($input ne "");
        }
        program_is_valid($graphviz_dot) if ($graphviz_dot ne "");

        if ($interactive)
        {
            print("Use Yelp or web browser to display help (HTML_VIEWER_CMD)? "
                  . "[" . (($html_viewer ne "") ? $html_viewer : "Use Yelp")
                  . "]: ");
            chomp($input = <STDIN>);
            ($input) = ($input =~ m/^\s*(.*)\s*$/);
            $html_viewer = $input if ($input ne "");
        }
        if ($html_viewer ne "" && ! program_is_valid($html_viewer))
        {
            exit(1) if (! $interactive);
            next;
        }

        if ($interactive)
        {
            print("Use the Monotone::AutomateStdio (MAS) library that came "
                  . "with this package? [Y]: ");
            chomp($input = <STDIN>);
            if ($input =~ m/^\s*[nN]\s*$/)
            {
                $use_dists_mas_lib = 0;
            }
            else
            {
                $use_dists_mas_lib = 1;
            }
        }

        if ($interactive)
        {
            print("Use Gtk2::Labels in history graph? [N]: ");
            chomp($input = <STDIN>);
            if ($input =~ m/^\s*[yY]\s*$/)
            {
                $use_labels_in_graph = 1;
            }
            else
            {
                $use_labels_in_graph = 0;
            }
        }

        if ($confirm || $interactive || ! $quiet)
        {
            print("\nInstallation options are:\n");
            print("GLOBS_FILE                 = " . $globs_file . "\n");
            print("PREFIX_DIR                 = " . $prefix_dir . "\n");
            print("DEST_DIR                   = " . $dest_dir . "\n");
            print("LIB_DIR                    = <PREFIX_DIR>/" . $lib_dir
                  . "\n");
            print("FILE_COMPARE_CMD           = " . $file_comparison . "\n");
            print("GRAPHVIZ_LAYOUT_PROGRAM    = " . $graphviz_dot . "\n");
            print("HTML_VIEWER_CMD            = "
                  . (($html_viewer ne "") ? $html_viewer : "Use Yelp") . "\n");
            print("Use my MAS                 = "
                  . ($use_dists_mas_lib ? "Yes" : "No") . "\n");
            print("Use Gtk2::Labels in graphs = "
                  . ($use_labels_in_graph ? "Yes" : "No") . "\n");
        }
        if ($interactive)
        {
            print("\nAccept these? [N]: ");
            chomp($input = <STDIN>);
            if ($input =~ m/^\s*[yY]\s*$/)
            {
                last;
            }
        }
        elsif ($confirm)
        {
            print("\nPress <Return> to continue: ");
            <STDIN>;
            last;
        }
        else
        {
            last;
        }
    }

    print("Installing...\n") if (! $quiet);
    $installer = Installer->new(0, 0, 0755, 0755, 0644);

    # Apply patches if necessary.

    system("patch -p0 -o mtn-browse-sv2 < mtn-browse-Gtk2-SourceView2.patch")
        if (! -f "mtn-browse-sv2" && $use_source_view_2);
    system("patch -p0 -o lib/perl/HistoryGraph-with-labels.pm < "
           . "HistoryGraph.pm-Use-Labels.patch")
        if (! -f "lib/perl/HistoryGraph-with-labels.pm");

    # Install `executable'.

    if ($use_source_view_2)
    {
        die("IO::File failed with $!")
            if (! defined($infile = IO::File->new("mtn-browse-sv2", "r")));
    }
    else
    {
        die("IO::File failed with $!")
            if (! defined($infile = IO::File->new("mtn-browse", "r")));
    }
    die("IO::File failed with $!")
        if (! defined($outfile = IO::File->new("mtn-browse.out", "w")));
    while (defined($input = $infile->getline()))
    {
        $input =~ s/\@INST:FILE_COMPARE_CMD\@/$file_comparison/g;
        $input =~ s/\@INST:GLOBS_FILE\@/$globs_file/g;
        $input =~ s/\@INST:GRAPHVIZ_LAYOUT_PROGRAM\@/$graphviz_dot/g;
        $input =~ s/\@INST:HTML_VIEWER_CMD\@/$html_viewer/g;
        $input =~ s/\@INST:LIB_DIR\@/$lib_prefix_dir/g;
        $input =~ s/\@INST:PREFIX_DIR\@/$prefix_dir/g;
        $outfile->print($input);
    }
    $infile->close();
    $outfile->close();
    $infile = $outfile = undef;
    $installer->install("mtn-browse.out",
                        "${prefix_dest_dir}/bin/mtn-browse",
                        0755);
    unlink("mtn-browse.out");

    # Install modules and libraries.

    die("IO::Dir failed with $!")
        if (! defined($dir = IO::Dir->new("lib/perl")));
    while (defined($file = $dir->read()))
    {
        if ($file =~ m/^HistoryGraph(?:-with-labels)?.pm/)
        {
            if ($use_labels_in_graph)
            {
                $installer->install("lib/perl/$file",
                                    "${lib_dest_dir}/perl/HistoryGraph.pm")
                    if ($file eq "HistoryGraph-with-labels.pm");
            }
            else
            {
                $installer->install("lib/perl/$file",
                                    "${lib_dest_dir}/perl/$file")
                    if ($file eq "HistoryGraph.pm");
            }
        }
        elsif ($file =~ m/.*\.pm$/)
        {
            $installer->install("lib/perl/$file",
                                "${lib_dest_dir}/perl/$file");
        }
    }
    $dir->close();
    if ($use_dists_mas_lib)
    {
        my $mas_dir;
        if (-r "lib/perl/Monotone/AutomateStdio.pm")
        {
            $mas_dir = "lib/perl/Monotone";
        }
        elsif (-r "../automate-stdio/lib/Monotone/AutomateStdio.pm")
        {
            $mas_dir = "../automate-stdio/lib/Monotone";
        }
        else
        {
            die("Been asked to use my MAS library but cannot find it.");
        }
        die("IO::Dir failed with $!")
            if (! defined($dir = IO::Dir->new($mas_dir)));
        while (defined($file = $dir->read()))
        {
            $installer->install("${mas_dir}/$file",
                                "${lib_dest_dir}/perl/Monotone/$file")
                if ($file =~ m/.*\.pm$/ || $file =~ m/.*\.pod$/);
        }
        $dir->close();
    }

    # Install GUI components.

    $installer->install("lib/ui/mtn-browse.glade",
                        "${prefix_dest_dir}/share/mtn-browse/glade/"
                            . "mtn-browse.glade");
    die("IO::Dir failed with $!")
        if (! defined($dir = IO::Dir->new("lib/ui/pixmaps")));
    while (defined($file = $dir->read()))
    {
        $installer->install("lib/ui/pixmaps/$file",
                            "${prefix_dest_dir}/share/mtn-browse/glade/$file")
            if ($file =~ m/.*\.png$/);
    }
    $dir->close();

    # Install translation files.

    die("IO::Dir failed with $!")
        if (! defined($dir = IO::Dir->new("locale")));
    while (defined($file = $dir->read()))
    {
        if ($file =~ m/(.*)\.po$/)
        {
            my $locale = $1;
            system("msgfmt --output-file=locale/${locale}.mo locale/$file");
            $installer->install("locale/${locale}.mo",
                                "${prefix_dest_dir}/share/locale/${locale}/"
                                    . "LC_MESSAGES/mtn-browse.mo");
        }
    }
    $dir->close();

    # Install help files.

    die("IO::Dir failed with $!")
        if (! defined($dir = IO::Dir->new("locale/help")));
    while (defined($locale = $dir->read()))
    {
        next if ($locale eq "." || $locale eq "..");
        my ($figures_dir,
            $img_file);
        if ($html_viewer ne "")
        {
            my ($html_file,
                $subdir);
            die("IO::Dir failed with $!")
                if (! defined($subdir =
                              IO::Dir->new("locale/help/${locale}")));
            while (defined($html_file = $subdir->read()))
            {
                next unless ($html_file =~ m/^[^.]+\.html$/);
                $installer->install("locale/help/${locale}/$html_file",
                                    "${prefix_dest_dir}/share/gnome/help/"
                                        . "mtn-browse/${locale}/$html_file");
            }
            die("IO::Dir failed with $!")
                if (! defined($subdir =
                              IO::Dir->new("locale/help/${locale}/images")));
            while (defined($img_file = $subdir->read()))
            {
                next if ($img_file eq "." || $img_file eq "..");
                $installer->install("locale/help/${locale}/images/$img_file",
                                    "${prefix_dest_dir}/share/gnome/help/"
                                        . "mtn-browse/${locale}/images/"
                                        . "$img_file");
            }
        }
        else
        {
            $installer->install("locale/help/${locale}/mtn-browse.xml",
                                "${prefix_dest_dir}/share/gnome/help/"
                                    . "mtn-browse/${locale}/mtn-browse.xml");
        }
        die("IO::Dir failed with $!")
            if (! defined($figures_dir =
                          IO::Dir->new("locale/help/${locale}/figures")));
        while (defined($img_file = $figures_dir->read()))
        {
            next if ($img_file eq "." || $img_file eq "..");
            $installer->install("locale/help/${locale}/figures/$img_file",
                                "${prefix_dest_dir}/share/gnome/help/"
                                    . "mtn-browse/${locale}/figures/"
                                    . "$img_file");
        }
        die("IO::Dir failed with $!")
            if (! defined($figures_dir =
                          IO::Dir->new("locale/help/${locale}/figures")));
        while (defined($img_file = $figures_dir->read()))
        {
            next if ($img_file eq "." || $img_file eq "..");
            $installer->install("locale/help/${locale}/figures/$img_file",
                                "${prefix_dest_dir}/share/gnome/help/"
                                    . "mtn-browse/${locale}/figures/"
                                    . "$img_file");
        }
    }
    $dir->close();

    exit(0);

}
#
##############################################################################
#
#   Routine      - program_is_valid
#
#   Description  - Check that the specified program exists.
#
#   Data         - $command     : The command that is to be validated.
#                  Return Value : True for valid, otherwise false for invalid.
#
##############################################################################



sub program_is_valid($)
{

    my $command = $_[0];

    my ($found,
        $program);

    # Check full paths or scan $PATH for it.

    $program = (split(/[[:blank:]]/, $command))[0];
    if ($program =~ m/^\//)
    {
        $found = 1 if (-x $program);
    }
    else
    {
        foreach my $dir (split(/:/, $ENV{PATH}))
        {
            if (-x "${dir}/$program")
            {
                $found = 1;
                last;
            }
        }
    }

    if (! $found)
    {
        print("Warning: Program `" . $program
              . "' cannot be found or is not executable.\n");
    }

    return $found;

}
#
##############################################################################
#
#   Embedded Documentation For This Module
#
##############################################################################



__END__

=head1 linux-installer

linux-installer - Linux installer for the mtn-browse application

=head1 SYNOPSIS

linux-installer [options]

  Options:
    --confirm             -c           Print out what will happen first
    --dep-level=              LEVEL    Specify dependency checking type
    --destdir=            -d  DIR      Specify temp installation location
    --file-comparison     -f  COMMAND  File comparison command
    --globs-file=         -g  FILE     Location of MIME globs file
    --graphviz-dot=           PROGRAM  Graphviz dot program
    --html-viewer             COMMAND  Viewer used to display help files
    --interactive         -i           Ask questions interactively
    --libdir=             -l  DIR      Library dir relative to PREFIX_DIR
    --prefix=             -p  DIR      Installation location
    --quiet               -q           Keep quiet
    --(no)-use-dists-mas               Use/don't use MAS within distro
    --(no)-use-labels                  Use Gtk2::Labels in history graphs
    --help                -?           Print this help text
    --man                 -m           Print full help text

=head1 DESCRIPTION

This program is the installer for the mtn-browse application. It is assumed
that this script will be run in the top level directory of the hierarchy made
by unpacking the tar file that mtn-browse was shipped in.

=over 8

=item B<--confirm or -c>

Print out what will be done before actually doing it.

=item B<--dep-level=LEVEL>

Specify type of dependency checking. Valid values are:

    0 - No dependency checking at all.
    1 - Do dependency checking only.
    2 - Do dependency checking and exit with an error code if
        they are not met. This is the default.
    3 - Do dependency checking and continue with the installation
        regardless.

=item B<--destdir=DIR or -d DIR>

Specify the destination directory for the installation process. In effect this
path is prepended onto the PREFIX_DIR path. Normally this is blank. Any
directory paths in the application will still assume that it is installed in
the PREFIX_DIR directory. This option is particularly useful for package
maintainers.

=item B<--file-comparison=COMMAND or -f COMMAND>

Specify the command used for doing detailed, side by side, file comparisons.
This defaults to meld.

=item B<--globs-file=FILE or -g FILE>

Specify the location of the system's MIME globs file. The default is
/usr/share/mime/globs.

=item B<--graphviz-dot=PROGRAM>

Specify the location of Grpahviz's dot program. Do not specify any switches.
The default is dot.

=item B<--html-viewer=COMMAND>

Specify the the command used for displaying the HTML help pages and
URLs. Normally Yelp style help documentation is installed (in the form of XML
files) and Gnome's default help and web browser are used. Whilst Yelp is the
Gnome desktop's help system there are some installations where Yelp itself or
parts of Gnome are not available. In these situations one can specify the
command to use to launch a web browser that can be used to display the help
documentation in the form of HTML files (i.e. there is no need for the web
browser to understand Yelp XML files) and normal URLs.

Specifying this switch has two effects. Firstly mtn-browse is configured to run
the specified web browser when it needs to display help documentation or URLs,
and secondly HTML documentation is installed in the place of the Yelp XML
documentation.

The default is to use Yelp, use Gnome's default web browser and install the XML
documentation files.

=item B<--interactive or -i>

Ask for all of the installation options interactively rather than getting
settings from the command line.

=item B<--libdir=DIR or -l DIR>

Specify the library directory, relative to <PREFIX_DIR>, for the the mtn-browse
application. The default is (<PREFIX_DIR>)/lib/mtn-browse.

=item B<--prefix=DIR or -p DIR>

Specify the final base installation directory. mtn-browse will assume it is
running from the <PREFIX_DIR>/bin directory and its libraries are located in
<PREFIX_DIR>/<LIB_DIR>.

=item B<--quiet or -q>

Do not output anything to the screen.

=item B<--(no)-use-dists-mas>

Select whether to use the Monotone::AutomateStdio library that shipped with
mtn-browse or use one already installed on your system. The default is to use
the one that comes with mtn-browse.

=item B<--(no)-use-labels>

Select whether to use Gtk2::Labels in history graphs instead of Gnome2::Canvas
text items. Gnome2::Canvas text items can be much slower but will always work
whereas Gtk2::Labels are much faster but may not work correctly depending upon
the combination of libraries installed on your system. The fault usually
manifests itself as labels not being rendered in very tall graphs. This can be
tested by using the test database at:

    https://code.monotone.ca/p/mtn-browse/source/tree/
        h:net.venge.monotone.contrib.mtn-browse.test.long-graph/

Graph the entire branch's history and scroll to the bottom and check that text
is displayed correctly in box.

The default is to use Gnome2::Canvas text items.

=item B<--help or -?>

Print a brief help message and exits.

=item B<--man or -m>

Prints the manual page and exits.

=back

=cut
