#!/opt/perl/bin/perl
#---------------------------------------------------------------------------# ## File:
# @() install.me 1.6 97/09/17 14:31:19 @(#) ## Author:
## Earl Hood, ehood@medusa.acs.uci.edu ## Summary:
## Installation program for Perl applications. #---------------------------------------------------------------------------# ## Copyright (C) 1997 Earl Hood, ehood@medusa.acs.uci.edu ##
## 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., 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA
#---------------------------------------------------------------------------#

package InstallMe;

use vars qw(

$MSDOS $MACOS $UNIX $VMS $WINDOWS
$DIRSEP $DIRSEPRX $CURDIR
$PROG $PATHSEP
$OSType
);
use Config;
use FileHandle;
use Getopt::Long;

############################################################################### ## OS Configuration Code
###############################################################################

BEGIN {

%DirSep = (

        macos   => ':',
        msdos   => '\\',
        unix    => '/',
        vms     => '/', # ??
        windows => '\\',

);
%CurDir = (

        macos   => ':', # ??
        msdos   => '.',
        unix    => '.',
        vms     => '.', # ??
        windows => '.',

);
%PathSep = (

        macos   => ';', # ??
        msdos   => ';',
        unix    => ':',
        vms     => ':', # ??
        windows => ';',

);

my $dontknow = 0;

## Init variables

    $MACOS      = 0;    $MSDOS  = 0;
    $UNIX       = 0;    $VMS    = 0;
    $WINDOWS    = 0;
    $DIRSEP     = '/';  $CURDIR = '.';

$PATHSEP = ':';

## See if ostype can be determined from osname in Config if (defined $^O) {

$_ = $^O;
} else {

        require Config;
        $_ = $Config::Config{'osname'};

}

if (/mac/i) {

        $MACOS = 1;
        $OSType = 'macos';
    } elsif (/vms/i) {
        $VMS = 1;
        $OSType = 'vms';
    } elsif (/msdos/i) {
        $MSDOS = 1;
        $OSType = 'msdos';
    } elsif (/mswin/i) {
        $WINDOWS = 1;  $MSDOS = 1;
        $OSType = 'windows';
    } elsif (/unix/i or
             /aix/i or
             /dynix/i or
             /hpux/i or
             /solaris/i or
             /sunos/i or
             /ultrix/i or
             /linux/i) {
        $UNIX = 1;
        $OSType = 'unix';
    } else {
        $dontknow = 1;

}

## If we do not know now what the ostype is, make a guess. if ($dontknow) {

my($tmp);

        ## MSDOG/Windoze
        if (($tmp = $ENV{'windir'}) and ($tmp =~ /[A-Z]:\\/i) and (-d $tmp)) {
            $MSDOS = 1;
            $WINDOWS = 1;
            $OSType = 'windows';

        } elsif (($tmp = $ENV{'COMSPEC'}) and ($tmp =~ /[a-zA-Z]:\\/) and
                 (-e $tmp)) {
            $MSDOS = 1;
            if ($tmp =~ /win/i) {
                $WINDOWS = 1;
                $OSType = 'windows';
            } else {
                $OSType = 'msdos';
            }

        ## MacOS
        } elsif (defined($MacPerl::Version)) {
            $MACOS = 1;
            $OSType = 'macos';

        ## Unix (fallback case)
        } else {
            $UNIX = 1;
            $OSType = 'unix';
        }

}

## Set other variables
$DIRSEP = $DirSep{$OSType};
if ($MSDOS or $WINDOWS) {

$DIRSEPRX = "\\\/";
} else {

($DIRSEPRX = $DIRSEP) =~ s/(\W)/\\$1/g; }
$CURDIR = $CurDir{$OSType};
$PATHSEP = $PathSep{$OSType};

## Store name of program
($PROG = $0) =~ s%.*[$DIRSEPRX]%%o;

## Flag to prompt for command-line options on a Mac $MacCLPrompt = 1;
}

#---------------------------------------------------------------------------#

##      CLinit() initializes @ARGV.  Currently, it does nothing under
##      MSDOS and Unix.
##
##      If running under a Mac and the script is a droplet, command-line
##      options will be prompted for if $MacCLPrompt is set to a
##      non-zero value.

##
sub CLinit {

## Ask for command-line options if script is a Mac droplet ## Code taken from the MacPerl FAQ ##
if ($MacCLPrompt && ( $MacPerl::Version =~ /Application$/ )) {

        # we're running from the app
        my( $cmdLine, @args );
        $cmdLine = &MacPerl::Ask( "Enter command line options:" );
        require "shellwords.pl";
        @args = &shellwords( $cmdLine );
        unshift( @::ARGV, @args );

}
}

#---------------------------------------------------------------------------#

##      path_join takes an array of path components and returns a string
##      with components joined together by the directoy separator.

##
sub path_join {

join($DIRSEP, @_);
}

#---------------------------------------------------------------------------#

##      path_split takes a string representing a pathname and splits
##      it into an array of components.  The pathname is interpreted
##      with respect to the OS we are running under.

##
sub path_split {

split(/[$DIRSEPRX]/o, $_[0]);
}

#---------------------------------------------------------------------------# ## is_absolute_path() returns true if a string is an absolute path ##
sub is_absolute_path {

if ($MSDOS or $WINDOWS) {

return $_[0] =~ /^(?:[a-z]:)?[\\\/]/i; }

    if ($MACOS) {               ## Not sure about Mac
        return $_[0] =~ /^:/o;
    }
    if ($VMS) {                 ## Not sure about VMS
        return $_[0] =~ /^\w+:/i;
    }
    $_[0] =~ m|^/|o;            ## Unix

}

############################################################################### ## Initialize Globals
###############################################################################

my %Files = ();
my %DefValues = (

    binpath     => $Config{'installbin'},
    docpath     => $Config{'prefix'} . $DIRSEP . 'doc',
    libpath     => $Config{'installsitelib'},
    manifest    => 'MANIFEST',
    manpath     => $Config{'installman1dir'},

perlpath => $Config{'perlpath'}, );
$DefValues{'manpath'} =~ s|(${DIRSEPRX}man)${DIRSEPRX}.*|$1|; my %OptValues = ();

############################################################################### ## Parse Command-line
###############################################################################

{

CLinit();
my $ret =
GetOptions(\%OptValues,

               qw(
                   batch
                   binpath=s
                   libpath=s
                   manpath=s
                   perlpath=s
                   manifest=s
                   nobin
                   nodoc
                   nolib
                   noman

                   help));

if (!$ret or $OptValues{"help"}) {

        usage();
        exit !$ret;

}
}

############################################################################### ## Do It
###############################################################################

{

my($dobin, $dolib, $dodoc, $doman);

## Get isntallation files
## ----------------------
read_manifest($OptValues{'manifest'} || $DefValues{'manifest'},

\%Files);

$DefValues{'docpath'} .= $DISRSEP . $Files{'name'}[0]

if $Files{'name'}[0];
$dobin = scalar(@{$Files{'bin'}}) && !$OptValues{'nobin'}; $dolib = scalar(@{$Files{'lib'}}) && !$OptValues{'nolib'}; $dodoc = scalar(@{$Files{'doc'}}) && !$OptValues{'nodoc'}; $doman = scalar(@{$Files{'man'}}) && !$OptValues{'noman'}; die "Nothing to install!\n"

unless $dobin or $dolib or $dodoc or $doman;

## Get path stuff
## --------------
if ($OptValues{'batch'}) {

        $OptValues{'perlpath'} = $DefValues{'perlpath'}
            unless $OptValues{'perlpath'};
        $OptValues{'binpath'}  = $DefValues{'binpath'}
            unless $OptValues{'binpath'};
        $OptValues{'libpath'}  = $DefValues{'libpath'}
            unless $OptValues{'libpath'};
        $OptValues{'docpath'}  = $DefValues{'docpath'}
            unless $OptValues{'docpath'};
        $OptValues{'manpath'}  = $DefValues{'manpath'}
            unless $OptValues{'manpath'};
    } else {
        while (1) {
            $OptValues{'perlpath'} =
                get_perl_from_user($OptValues{'perlpath'},
                                   $DefValues{'perlpath'},
                                   "Pathname of perl executable:");
            $OptValues{'binpath'} =
                get_path_from_user($OptValues{'binpath'},
                                   $DefValues{'binpath'},
                                   "Directory to install executables:")
                                   if $dobin;
            $OptValues{'libpath'} =
                get_path_from_user($OptValues{'libpath'},
                                   $DefValues{'libpath'},
                                   "Directory to install library files:")
                                   if $dolib;
            $OptValues{'docpath'} =
                get_path_from_user($OptValues{'docpath'},
                                   $DefValues{'docpath'},
                                   "Directory to install documentation:")
                                   if $dodoc;
            $OptValues{'manpath'} =
                get_path_from_user($OptValues{'manpath'},
                                   $DefValues{'manpath'},
                                   "Directory to install manpages:")
                                   if $doman;

            print STDOUT "You have specified the following:\n";
            print STDOUT "\tPerl path: $OptValues{'perlpath'}\n";
            print STDOUT "\tBin directory: $OptValues{'binpath'}\n"
                if $dobin;
            print STDOUT "\tLib directory: $OptValues{'libpath'}\n"
                if $dolib;
            print STDOUT "\tDoc directory: $OptValues{'docpath'}\n"
                if $dodoc;
            print STDOUT "\tMan directory: $OptValues{'manpath'}\n"
                if $doman;

            last  if prompt_user_yn("Is this correct?", 1);

            $DefValues{'perlpath'} = $OptValues{'perlpath'};
            $DefValues{'binpath'}  = $OptValues{'binpath'};
            $DefValues{'libpath'}  = $OptValues{'libpath'};
            $DefValues{'docpath'}  = $OptValues{'docpath'};
            $DefValues{'manpath'}  = $OptValues{'manpath'};
            $OptValues{'perlpath'} = '';
            $OptValues{'binpath'}  = '';
            $OptValues{'libpath'}  = '';
            $OptValues{'docpath'}  = '';
            $OptValues{'manpath'}  = '';
        }

}

## Install files
# -------------
print STDERR "($OptValues{'perlpath'})\n"; my $plprefix = "
!$OptValues{'perlpath'}\n";

       $plprefix .= "use lib qw($OptValues{'libpath'});\n"
                    if $OptValues{'libpath'};

my($file, $destfile);
if ($dobin) {

        print STDOUT qq(Installing programs to "$OptValues{'binpath'}":\n);
        foreach (@{$Files{'bin'}}) {
            print STDOUT "    $_ => ";
            ($file = $_) =~ s%./%%o;
            $destfile = join('', $OptValues{'binpath'}, $DIRSEP, $file);
            print STDOUT $destfile, "\n";
            cp($_, $destfile, $plprefix);
            eval q{chmod 0755, $destfile;};
        }

}
if ($dolib) {

        print STDOUT qq(Installing lib files to "$OptValues{'libpath'}":\n);
        foreach (@{$Files{'lib'}}) {
            print STDOUT "    $_ => ";
            ($file = $_) =~ s%./%%o;
            $destfile = join('', $OptValues{'libpath'}, $DIRSEP, $file);
            print STDOUT $destfile, "\n";
            cp($_, $destfile);
        }

}
if ($dodoc) {

        print STDOUT qq(Installing docs to "$OptValues{'docpath'}":\n);
        foreach (@{$Files{'doc'}}) {
            print STDOUT "    $_ => ";
            ($file = $_) =~ s%./%%o;
            $destfile = join('', $OptValues{'docpath'}, $DIRSEP, $file);
            print STDOUT $destfile, "\n";
            cp($_, $destfile);
        }

}
if ($doman) {

        my $sect;
        print STDOUT qq(Installing manpages to "$OptValues{'manpath'}":\n);
        foreach (@{$Files{'man'}}) {
            print STDOUT "    $_ => ";
            ($file = $_)    =~ s%./%%o;
            ($sect = $file) =~ s%.*\.%%o;
            $destfile = join('', $OptValues{'manpath'},
                             $DIRSEP, "man", $sect, $DIRSEP, $file);
            print STDOUT $destfile, "\n";
            cp($_, $destfile);
        }

}
}

############################################################################### ## Subroutines
###############################################################################

##------------------------------------------------------------------------

##      read_manifest() reads file giving list of all files to
##      install.

##
sub read_manifest {

my $file = shift;
my $href = shift;
my($key, $pathname);

open FILE, $file or die qq(Unable to open "$file"\n); while (<FILE>) {

        next  if /^#/;
        next  unless /\S/;
        chomp;
        ($key, $pathname) = split(/:/, $_, 2);
        push @{$href->{$key}}, $pathname;

}
close FILE;
}

##------------------------------------------------------------------------

##      perl_exe() returns true if pathname argument is a perl
##      interpreter.

##
sub perl_exe {

my $pathname = shift;

return 0 unless open PERL, "perl -v |"; while (<PERL>) {

        if (/\bperl\b/) {
            close PERL;
            return 1;
        }

}
close PERL;
0;
}

##------------------------------------------------------------------------ ## get_perl_from_user() gets the pathname of the perl executable. ##
sub get_perl_from_user {

    my $value   = shift;        # Current value (if set, batch mode)
    my $default = shift;        # Default value
    my $prompt  = shift;        # User prompt

if ($value =~ /\S/) {

        die qq(ERROR: "$value" is not perl.\n)
            unless perl_exe($value);
    } else {
        while (1) {
            $value = interpolate_path(prompt_user($prompt, $default));
            last  if perl_exe($value);
            warn qq(Warning: "$value" is not perl.\n);
        }

}
$value;
}

##------------------------------------------------------------------------

##      get_path_from_user() gets a path from the user.  The function
##      insures the path exists.

##
sub get_path_from_user {

    my $value   = shift;        # Current value (if set, batch mode)
    my $default = shift;        # Default value
    my $prompt  = shift;        # User prompt

if ($value =~ /\S/) {

        die qq(ERROR: Unable to create "$value".\n)
            unless create_dir($value, 1);
    } else {
        while (1) {
            $value = interpolate_path(prompt_user($prompt, $default));
            last  if create_dir($value);
            warn qq(Warning: Unable to create "$value".\n);
        }

}
$value;
}

##------------------------------------------------------------------------ ## create_dir() creates a directory path ##
sub create_dir {

    my $d       = shift;        # Directory path
    my $noask   = shift;        # Don't ask to create flag

return 1 if -e $d;

my(@a) = grep($_ ne '', split(/[$DIRSEPRX]/o, $d)); my($path, $dir, $curpath);

if (!$noask) {

return 0 unless prompt_user_yn(qq{"$d" does not exist. Create}, 1); }
if ($MSDOS) {

        if ($d =~ m%^\s(?:[a-zA-Z]:)?[/\\]%) {
            $path = shift @a;
        } else {
            $path = $CURDIR;
        }
    } else {
        if ($d =~ /^\s\//) {
            $path = '';
        } else {
            $path = $CURDIR;
        }

}
foreach $dir (@a) {

        $curpath = "$path$DIRSEP$dir";
        if (! -e $curpath) {
            if (!mkdir($curpath, 0777)) {
                warn "Unable to create $curpath: $!\n";
                return 0;
            }
        } elsif (! -d $curpath) {
            warn "$curpath is not a directory\n";
            return 0;
        }
        $path .= $DIRSEP . $dir;

}
if (! -w $d) {

        warn "$d not writable\n";
        return 0;

}
1;
}

##------------------------------------------------------------------------

##      interpolate_path() expands any special characters in a
##      pathname.

##
sub interpolate_path {

my($path) = shift;

$path =~ s/^~/$ENV{'HOME'}/e;
$path =~ s/\$(\w+)/$ENV{$1}/ge;
$path =~ s/\$\{(\w+)\}/$ENV{$1}/ge; $path;
}

##------------------------------------------------------------------------ ## cp() copies a file, or directory. ##
sub cp {

my($src, $dst, $prepend) = @_;

if (-d $src) {

        if (! -e $dst) {
            mkdir($dst,0777) or die "Unable to create $dst: $!\n";
        }
        opendir(DIR, $src) or die "Unable to open $src: $!\n";
        my @files = grep(!/^(sccs|\.|\..)$/i, readdir(DIR));
        closedir(DIR);
        my($file, $srcpn, $dstpn);
        foreach $file (@files) {
            $srcpn = "$src$DIRSEP$file";
            $dstpn = "$dst$DIRSEP$file";
            if (-d $srcpn) {
                cp($srcpn, $dstpn, $prepend);
            } else {
                cpfile($srcpn, $dstpn, $prepend);
            }
        }

} else {

cpfile($src, $dst, $prepend);
}
}

##------------------------------------------------------------------------

##      cpfile() copies a file.  Any text in $prepend will be prepending
##      to the destination file.

##
sub cpfile {

my($src, $dst, $prepend) = @_;

if (-d $dst) {

        my $tmp;
        ($tmp = $src) =~ s%.*[$DIRSEPRX]%%o;
        $dst .= $DIRSEP . $tmp;
    }
    open(SRC, $src)     or die "Unable to open $src: $!\n";

open(DST, "> $dst") or die "Unable to create $dst: $!\n"; if (-B $src) { binmode( SRC ); binmode( DST ); } if ($prepend) {

print DST $prepend;
}
print DST <SRC>;
close(SRC);
close(DST);
}

##------------------------------------------------------------------------

##      prompt_user() prompts the user for some input.  The first
##      argument is the prompt string, the second is the default
##      value is the user specifies nothing.

##
sub prompt_user {

my $prompt = shift;
my $default = shift;

my($answer);

print STDOUT $prompt;
print STDOUT qq{ ("$default")} if defined($default); print STDOUT " ";
$answer = <STDIN>;
chomp $answer;
$answer = $default if $answer !~ /\S/; $answer;
}

##------------------------------------------------------------------------ ## prompt_user_yn() prompts the user for a yes or no question. ##
sub prompt_user_yn {

my $prompt = shift;
my $default = shift;

my($answer);

print STDOUT $prompt, " ";
print STDOUT $default ? "['y']" : "['n']"; print STDOUT " ";
$answer = <STDIN>;
chomp $answer;
if ($answer !~ /\S/) {

$answer = $default;
} elsif ($answer =~ /y/i or $answer =~ /yes/i) {

$answer = 1;
} else {

$answer = 0;
}
$answer;
}

#---------------------------------------------------------------------------# ##
sub usage {

print STDOUT <<EOF;
Usage: $PROG [options]
Options:

  -batch                : Run in batch mode
  -binpath <path>       : Path to bin directory
  -help                 : This message
  -libpath <path>       : Path to lib directory
  -manifest <file>      : List of files to install (def="MANIFEST")
  -manpath <path>       : Path to man directory
  -nobin                : Do not install programs
  -nodoc                : Do not install documentation
  -nolib                : Do not install library files
  -noman                : Do not install manpages

-perlpath <pathname> : Pathname of perl interpreter EOF
}