#!/usr/bin/perl -w
# If your copy of perl is not in /usr/bin, please adjust the line above.
#
# Copyright (C) 1998, 1999 VMware, Inc.  All Rights Reserved.
#
# Loopback device for vmware - patch installer
#

use strict;

# name of the dirname binary
my $dirname;

# Recorded location of the binary directory
my $bindir;

# Size of a terminal line
my $lineSize = 80;

# Wordwrap system: append some content to the output
sub append_output {
  my $output = $_[0];
  my $pos = $_[1];
  my $append = $_[2];

  $output = $output . $append;
  $pos = ($pos + length($append)) % $lineSize;

  return ($output, $pos);
}

# Wordwrap system: process the last encountered word
sub process_word {
  my $output = $_[0];
  my $pos = $_[1];
  my $word = $_[2];

  if ((($pos + length($word)) >= $lineSize) && (length($word) < $lineSize)) {
    # The word is too large to fit in the end of line, but can fit in a new
    # empty line, let's create one for it
    ($output, $pos) = append_output($output, $pos, "\n");
    $pos = 0;
  }

  ($output, $pos) = append_output($output, $pos, $word);
  $word = "";

  return ($output, $pos, $word);
}

# Wordwrap system: deal with the next character
sub wrap_one_char {
  my $output = $_[0];
  my $pos = $_[1];
  my $word = $_[2];
  my $char = $_[3];

  if ($char eq "\n") {
    ($output, $pos, $word) = process_word($output, $pos, $word);
    ($output, $pos) = append_output($output, $pos, "\n");
    $pos = 0;
  } else {
    if (($char eq " ") || ($char eq "-")) {
      ($output, $pos, $word) = process_word($output, $pos, $word);
      ($output, $pos) = append_output($output, $pos, $char);
    } else {
      $word = $word . $char;
    }
  }

  return ($output, $pos, $word);
}

# Wordwrap system: word-wrap a string
sub wrap {
  my $input = $_[0];
  my $output;
  my $pos;
  my $word;
  my $i;

  $output = "";
  $pos = 0;
  $word = "";
  for ($i = 0; $i < length($input); $i++) {
    ($output, $pos, $word) = wrap_one_char($output, $pos, $word, substr($input, $i, 1));
  }
  ($output, $pos, $word) = process_word($output, $pos, $word);

  return $output;
}

# Prompt the user for a value, providing a default value
sub query {
  my ($message, $defaultreply) = @_;
  my $reply;
  my $optSpace = substr($message, -1) eq "\n" ? "" : " ";

  print "\n";
  if ($defaultreply ne "") {
    print wrap($message . $optSpace . "[" . $defaultreply . "] ");
  } else {
    print wrap($message . $optSpace);
  }
  
  chop($reply = <STDIN>);
  $reply =~ s/^\s*//;			# Kill leading whitespace
  $reply =~ s/\s*$//;			# Kill trailing whitespace
  $reply = $defaultreply if $reply eq "";
  return $reply;
}

# Determine whether a file name is in the PATH
# Return value is:
#  0: no
#  1: yes
sub DoesBinaryExist {
  my $bin;

  $bin = $_[0];
  if (system("which " . $bin . "> /dev/null")) {
    return 0;
  }
  return 1;
}

# Prompts the user if a binary is not found
# Return value is:
#  "": the binary has not been found
#  the binary name if it has been found
sub DoesBinaryExist_Prompt {
  my $bin;
  my $ans;

  $bin = $_[0];
  while (DoesBinaryExist($bin) == 0) {
    $ans = query("Setup is unable to find the " . $bin . " program on your machine. Do you want to specify the location of this program by hand?", "Y");
    if ($ans !~ /^[yY]/) {
      return "";
    }

    $bin = query("What is the location of the " . $bin . " program on your machine?", "");
  }
  return $bin;
}

# Prompts the user if a file is not readable
# Return value is:
#  "": the file is not readable
#  the file path if it has been found
sub IsFileReadable_Prompt {
  my $bin;
  my $ans;

  $bin = $_[0];
  while (not -r $bin) {
    $ans = query("Setup is unable to read the " . $bin . " file. Do you want to specify the location of this file by hand?", "Y");
    if ($ans !~ /^[yY]/) {
      return "";
    }

    $bin = query("What is the location of the " . $bin . " file on your machine?", "");
  }
  return $bin;
}

# Print an error message and exit
sub error {
  my $msg;

  $msg = $_[0];

  print STDERR wrap("\n" . $msg . "Installation cancelled\n");
  exit 1;
}

# Tell if the user is the super user
sub is_root {
  return $> == 0;
}

# Get the binary directory
sub getBindir {
  my $vmware_locations;
  my $is_addon;
  my $already_here;

  $vmware_locations = IsFileReadable_Prompt("/etc/vmware/locations");
  if ($vmware_locations eq "") {
    error("Perhaps VMware is not installed on your computer\n");
  }  
  open(LOCATIONS, "+<" . $vmware_locations);

  $bindir = "";
  $is_addon = 0;
  $already_here = 0;
  while (<LOCATIONS>) { 
    if (/^answer BINDIR (\S+)/) {
      $bindir = $1;
    }
    if (/^Loopback device addon installation completed/) {
      $is_addon = 1;
    }
    if (/vmware-loop/) {
      $already_here = 1;
    }
    if (/vmware-mount\.pl/) {
      $already_here = 1;
    }
  }
  
  if ($already_here == 1) {
    if ($is_addon == 1) {
      error("This add-on is already installed. If you want to remove it, remove VMware with 'install.pl uninstall', and re-install VMware without re-installing the add-on.\n\n");
    } else {
      error("There is no need to install this add-on. The features it contains are already included in your current VMware distribution. You can safely remove this add-on from your computer.\n\n");
    }
  }

  if ($bindir eq "") {
    error("This script is unable to find the BINDIR entry in the " . $vmware_locations . " file.\n");
  }
}

sub Log { 
  my $string = shift;
  print LOCATIONS $string;
}

sub LogFile {
    my $filename = shift;
    my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
	      $atime, $mtime, $ctime) = stat($filename);
    Log("file $filename $mtime\n");
}
    
sub Install {
  my ($mode, $src, $dst) = @_;

  if (-e $dst) {
      die "\n### File $dst should not exist.\n" .
	  "### Please remove it and try again.\n";
  }

  open(SRC, "<$src") or die "\n### open: $src: $!.\n";
  open(DST, ">$dst") or die "\n### open: $dst: $!.\n";
  chmod $mode, $dst or die "\n### chmod $dst: $!.\n";
  while(defined($_ = <SRC>)) {
    print DST $_;
  }

  close(SRC);
  close(DST);

  LogFile($dst);
}

sub patchConfig {
  my $vmware_config;

  $vmware_config = IsFileReadable_Prompt("/etc/vmware/config");
  if ($vmware_config eq "") {
    error("Perhaps VMware is not installed on your computer\n");
  }
  open(CONFIGFILE, ">>" . $vmware_config);
  print CONFIGFILE "loop.fullpath = \"$bindir/vmware-loop\"\n";
  close(CONFIGFILE);
}

sub main {
  my $srcdir;

  if (not is_root()) {
    error("Please re-run this script as the super user.\n");
  }

  # Force the path to reduce the risk of using "modified" external helpers
  # If the user has a special system setup, he will will prompted for the
  # proper location anyway
  $ENV{'PATH'} = "/bin:/usr/bin:/sbin:/usr/sbin";

  getBindir();
  umask 022;
  $dirname = DoesBinaryExist_Prompt("dirname");
  if ($dirname eq "") {
    error("Unable to continue\n");
  }
  $srcdir = `$dirname $0`;
  chop $srcdir;
  Install(0755, $srcdir . "/vmware-loop", $bindir . "/vmware-loop");
  Install(0755, $srcdir . "/vmware-mount.pl", $bindir . "/vmware-mount.pl");
  patchConfig();
  Log("Loopback device addon installation completed\n");
  print "Loopback device addon Installation successful\n";
  close(LOCATIONS);
}

main();
