#!/usr/bin/perl -w
#
# halfd install script
#
# Change History:
#
# rabbott 2001-11-15 - Add version number
#
# rabbott 2001-11-12 - Remove halfd.gdb_commands - will be created if needed
#
# rabbott 2001-10-26 - Fix upgradeConfigFile now that changelog is @ EOF
#
# rabbott 2001-10-15 - Don't overwrite GUI .cfg files if they exist
#
# rabbott 2001-08-21 - Weapon and suicide taunts are now upgraded
#
# rabbott 2001-08-13 - Upgrade halfd.cfg if it exists
#                      Find installed mods instead of hard-coding them
#
# rabbott 2001-06-06 - Don't check 'tclsh' - this causes install to hang.
#
# rabbott 2001-05-24 - Don't install GUIs at all if TK interp doesn't exist
#
# rabbott 2001-05-10 - Updated for halfd
#
# rabbott 2001-03-24 - Add 'hlds_ld.gdb_commands' file, goes in $HLDIR
#                      Check permissions before installing a file
#
# rabbott 2001-03-17 - Add 'hlds_ld.teammap' file, goes in $HLDIR/mod
#
# rabbott 2000-12-03 - Make it work with FreeBSD ;-)
#                      Many thanks to Eric Harris!
#
# rabbott 2000-11-11 - Make permissions on executables 0755
#
# rabbott 2000-09-27 - Don't allow HLDIR to be a relative pathname.
#                    - Install script no longer considered beta ;-) 
#
# rabbott 2000-09-19 - all temporary files now include PID
#                      (people really install executables in /tmp??)
# 

######################################################################
# setDefaults - set default/global variables
#
sub setDefaults {
	$version = "2.14";

	$mods = "";

	$delimiter = "-------------------------------------------------------------\n";

	$bindir = "/usr/local/bin";
	$tk     = "/usr/bin/wishx";

	# Files that need to be backed-up
	@bakfiles   = ( "hlgui.cfg", "hlclustermon.cfg", "halfd.cfg",
	                "halfd.teammap" );

	# Files that install in HLDIR
	@hldirfiles = ( "halfd.txt", "hlgui.cfg", "hlclustermon.cfg" );

	# Files that install in HLDIR/mod
	@modfiles   = ( "halfd.cfg", "halfd.teammap" );

	# Files that need a Tcl interp at the top, install in bin/
	@tclfiles   = ( "halfd", "hlcmd" );

	# Files that need a Tk interp at the top, install in bin/
	@tkfiles    = ( "hlgui", "hlclustermon" );
}

sub runCmd {
	local( $cmd ) = @_;
	local( $data, $tmp );

 	$tmp = "/tmp/tmp.$$";
	system "$cmd > $tmp 2>&1";

	$data = "";
 	if( open( TMP, "<$tmp" )) {
		if( eof( TMP )) {
			$data = "";
		} else {
			chop( $data = <TMP> );
		}
		close( TMP );
		unlink( $tmp );
	} else {
		die "Couldn't open $tmp: $!\n";
	}

	return $data;
}

sub checkForExtendedTcl {
	local( $interp ) = @_;

	$testcmd = "if { [string length [info commands cequal]] > 0 } " .
		"{ echo 1 } else { echo 0 }";

	$rc = runCmd( "$interp -c \"$testcmd\"" );

	if( ! $rc ) {
		print $delimiter;
		print "\nYour Tcl interpreter '$interp' doesn't support Extended Tcl.\n";
		print "Please install Extended Tcl before installing halfd!\n\n";
		print "TclX/TkX install images can be obtained here:\n";
		print "http://www.halfd.org/download.shtml\n\n";
		print "See the FAQ and halfd Forums for more information on installing TclX.\n";
		die;
	}
}

sub findMods {
	local( $hldir ) = @_;
	local( @allfiles, $file, $first );

	$mods = "";
	$first = 1;

	opendir DIR, $hldir || return "";
	@allfiles = readdir DIR;
	closedir DIR;

	foreach $file ( @allfiles ) {
		if( -e "$hldir/$file/server.cfg" ) {
			if( $first ) {
				$mods = $file;
				$first = 0;
			} else {
				$mods = $mods . " $file";
			}
		}
	}

	return $mods
}

sub findTcl {
	local( $tk ) = @_;
	local( $cmd, $myinterp, $interp, $rc, @interps );

	@interps = ( "tclx", "tcl" );
	@interps = ( "wishx", "wish" ) if $tk;

	$interp = "";
	foreach $myinterp ( @interps ) {
		$interp = &runCmd( "which $myinterp" );
		next if( $interp eq "" );
		last if( ! grep { /^which/ } $interp );
		$interp = "";
	}
	print ".";

	if( ! $tk ) {
		if( $interp eq "" ) {
			print "\nCan't find a Tcl interpreter in your PATH!\n";
			print "\nPlease install Tcl before installing halfd.\n";
			print "\nCheck the FAQ for details on installing Tcl.\n";
			die;
		}

		print "\nfound '$interp', checking for TclX functionality...\n";

		&checkForExtendedTcl( $interp );
		print "'$interp' has TclX, good!\n";
	} else {
		if( $interp eq "" ) {
			print "\nCouldn't find a Tk interpeter in your PATH.\n";
			print	"You won't be able to run hlgui from X-windows.\n";
			print	"The halfd script should run fine, however.\n";
		} else {
			print "found '$interp'\n";
		}
	}

	return $interp;
}

sub getHldir {
	local( @list ) = keys( %ENV );
	if( ! grep { /HLDIR/ } @list ) {
		return "";
	} else {
		return $ENV{HLDIR};
	}
}

sub setFirstLine {
	local( $file, $interp ) = @_;
	local( $tmp ) = "/tmp/$file.$$";
	local( $junk );

	open( IN,  "<$file" ) || die "Can't open $file: $!\n";
	open( OUT, ">$tmp" ) || die "Can't open $tmp: $!\n";

	print OUT "#\!$interp\n";
	print OUT "#\n# Installed on " . `date`;

	# Ignore first line
	$junk = <IN>; 

	while( <IN> ) {
		print( OUT );
	}

	close( IN  );
	close( OUT );

	print "Set interpreter for '$file' to '$interp'...\n";

	return $tmp;
}

sub installFile {
	local( $file, $fname, $dir, $exec ) = @_;
	local( $path, $rc );

	$path = "$dir/$fname";

	&checkPerms( $path ) if( -e $path );

	if( -e $path && grep { /\b${fname}\b/ } @bakfiles ) {
		if( $fname ne "halfd" ) {
			print "Backing up $path to $fname.bak...\n";
			rename  "$path", "${path}.bak";
		}
	}

	print "INSTALLING $path...\n";
	system "cp $file $path" || die( "Copy failed: $!\n" );

	if( $exec ) {
		# Change permissions to make it executable...
		$mode = 0755;
		chmod $mode, $path;
	}
}

sub upgradeConfigFile {
	local( $oldfile, $newfile, $dir ) = @_;
	local( $path, $first, $myfile, $key, $val, %keys );

	# Don't upgrade files that don't exist :-)
	return if( ! -e "$dir/$oldfile" );

	print "Upgrading $newfile in $dir...\n";

	# First read the "old" configuration file and store values in 'keys' hash
	$myfile = "$dir/$oldfile";
	open( IN, "<$myfile" ) || die "Can't open $myfile: $!\n";
	while( $data = <IN> ) {
		chop( $data );
		last if( $data eq "EOF" );
		next if !isKeyVal( $data );
		($key, $val) = getKeyVal( $data );
		$keys{$key} = $val;
	}
	close( IN );

	# Now process the "new" config file, pasting "old" values as we go
	$myfile = "/tmp/$newfile.$$";
	open( IN,  "<$dir/$newfile" ) || die "Error $!\n";
	open( OUT, ">$myfile" ) || die "Error $!\n";;
	print OUT "# Automatically upgraded by halfd install script on " . `date`;

	while( $data = <IN> ) {
		chop( $data );

		if( !isKeyVal( $data )) {
			if( $data eq "[CHANGELOG]" ) {
				# Now print out the leftover keys (if there are any)
				$first = 1;
				foreach $key (sort(keys %keys)) {
					if( $first ) {
						print OUT "\n#\n# Other values from previous halfd.cfg:\n#\n";
						$first = 0;
					}
					print OUT "$key=$keys{$key}\n";
					delete $keys{$key};
				}
				print OUT "\n";
			}
			print OUT "$data\n";
		} else {
			# Check to see if this key exists in our hash
			($key, $val) = getKeyVal( $data );
			if( exists( $keys{$key} )) {
				# It exists.  Use the previous value.
				print OUT "$key=$keys{$key}\n";

				# Delete it.
				delete $keys{$key};
			} else {
				# It does not exist.  Use default.
				print OUT "$key=$val\n";
			}
		}
	}

	# Now print out the leftover keys (if there are any)
	$first = 1;
	foreach $key (sort(keys %keys)) {
		if( $first ) {
			print OUT "\n#\n# Other values from previous halfd.cfg:\n#\n";
			$first = 0;
		}
		print OUT "$key=$keys{$key}\n";
	}

	close( IN  );
	close( OUT );

	# Our temp file has the upgraded values.  Copy it over hldir/mod/halfd.cfg.
	$path = "$dir/$newfile";
	unlink "$path";
	system "cp $myfile $path" || die( "Copy failed: $!\n" );
	unlink "$myfile";
}

sub getKeyVal {
	local( $data ) = @_;
	local( @fields, $key, $val, $myval, $first );

	@fields = split( /=/, $data );


	$key = shift( @fields );

	$first = 1;
	$val = "";

	foreach $myval ( @fields ) {
		if( $first ) {
			$val = $myval;
			$first = 0;
		} else {
			$val = $val . "=" . $myval;
		}
	}

	$val = "" if( !defined( $val ));

	return( $key, $val );
}


sub isKeyVal {
	local( $data ) = @_;
	local( $first );
	local( $rc ) = 1;

	# Throw away blank lines
	$rc = 0 if( $data eq "" );

	# Throw away comments and M$-style ini constructs
	$first = substr( $data, 0, 1 );
	$rc = 0 if( $first eq "#" );
	$rc = 0 if( $first eq "[" );

	# Only use if it's got an = in it
	$first = index( $data, "=" );
	$rc = 0 if( $first eq -1 );

	return $rc;
}


sub installComplete {
	print $delimiter;
	print "\n\nInstallation complete!\n";

	print "\nIf you have problems, please check the website for updates:\n";
	print "http://www.halfd.org\n";

	print "\nIf you want to use another language (e.g. French or German), you'll\n";
	print "need to copy the <language>/halfd.txt file to \$HLDIR.\n";

	print "\nCOMPLETE THESE STEPS BEFORE STARTING ANYTHING:\n";

	print "\nEdit the halfd.cfg file for each mod to reflect your settings.\n";
	print "Read the FAQ.\n";
	print "Read the FAQ :-)\n";

	print "\nMake sure the following TWO lines are in the 'autoexec.cfg'\n";
	print "for each mod:\n\n";
	print "fullserverinfo\n";
	print "log on\n";
	print "(if the autoexec.cfg doesn't exist, create it.)\n";

	print "\nAdd the following line to your '/etc/profile'\n";
	print "file (you need to be root to do this):\n";

	print "\nexport HLDIR=$hldir\n\n";
}

sub checkDir {
	local( $desc, $dir ) = @_;
	local( $first_byte );

	$first_byte = substr( $dir, 0, 1 );

	if( "$first_byte" ne "/" ) {
		print "\n\n$desc must NOT be a relative pathname!\n";
		print "Please enter another value.  It MUST start with a slash '/'.\n";
		return -1;
	} else {
		return 0;
	}
}

sub checkPerms {
	local( $target ) = @_;

	if( -w $target ) {
		# we're ok
	} else {
		print "\n\n$target isn't writable!\n";
		print "\n\nPlease fix your permissions or install as another user.\n";
		exit( 1 );
	}
		
}


sub main {
	local( $tmp );

	&setDefaults;
	select STDOUT;
	$| = 1;

	print "halfd install script version $version\n\n";

	print "NOTE for users upgrading from hlds_ld to halfd - make sure you\n";
	print "run 'perl ./halfd-upgrade' ONLY ONCE before running this install!\n";
	print "\n\n";

	print "NOTE for users upgrading from pre-2.10 releases - halfd's command-line\n";
	print "arguments have changed.  If you have any custom startup scripts you'll\n";
	print "need to modify them!\n";
	print "\n$delimiter\n";

	print "This script will install halfd version $version.\n\n";
	print "If you encounter installation problems, post to the forum at\n";
	print "http://www.halfd.org\n\n";

	print "Existing configuration files will be backed up and UPGRADED.\n";
	print "All defaults are enclosed in [square-brackets].\n";

	&checkPerms( "/tmp" );

	print "\nHave you stopped all halfd (or hlds_ld) processes? (Y/N) [n] ";
	chop( $ans = <STDIN> );
	die "\nPlease stop all halfd processes before install!\n" .
	    "(Running 'killall -9 halfd' should do it.)\n\n" . 
		"You can leave hlds_run running if it was started by halfd.\n" 
		if ( ! grep { /^y/i } $ans );

	# exec 'which tcl' and 'which tk' to look for Tcl interps, run test proggy
	print "\n\nChecking for Extended Tcl interpreter..";
	$tclinterp = &findTcl( 0 );

	print "Checking for Extended Tk interpreter..";
	$tkinterp = &findTcl( 1 );

	print "\nWould you like to change these interpreter settings? (Y/N) [n] ";
	chop( $ans = <STDIN> );
	if( $ans =~ /^y/i ) {
		print "Enter the location of your Extended Tcl interpreter:\n";
		print "[$tclinterp] ";
		chop( $ans = <STDIN> );
		$tclinterp = $ans if( ! $ans eq "" );

		print "Enter the location of your Extended Tk interpreter:\n";
		print "[$tkinterp] ";
		chop( $ans = <STDIN> );
		$tkinterp = $ans if( ! $ans eq "" );
	}

	# Loop until they enter a valid directory
	while( 1 ) {
		print "\n\nEnter the directory where you have 'hlds_run' installed:\n";
		print "(note that this is your HLDIR)\n";
		$hldir = getHldir;
		print "[$hldir] ";
		chop( $myhldir = <STDIN> );
		$hldir = $myhldir if( ! $myhldir eq "" );

		$tmp = $hldir . "/hlds_run";
		if( ! -x $tmp ) {
			print "\nI couldn't find an executable hlds_run in '$hldir'!\n";
			next;
		}

		next if( &checkDir( "HLDIR", $hldir ) < 0 );
		last;
	}
	&checkPerms( $hldir );

	print "\n\n";
	print "Enter the directory where you would like the executables ";
	print "installed\n(enter \"none\" if you just want to install cfg files):";
	print "\n[$bindir] ";
	chop( $mybindir = <STDIN> );
	$bindir = $mybindir if( ! $mybindir eq "" );

	&checkPerms( $bindir ) if( $bindir ne "none" );

	$mods = findMods( $hldir );

	print "\n\n";
	print "Enter the MODs that you would like to use with halfd.\n";
	print "Separate each item with a space.\n";
	print "(If a mod doesn't exist it will be ignored)\n";
	print "(enter \"none\" for no mods):\n";
	print "[$mods] ";
	chop( $mymods = <STDIN> );
	$mods = $mymods if( ! $mymods eq "" );

	print "\n\nInstallation parameters:\n";
	print "HLDIR      = '$hldir'\n";
	print "BINDIR     = '$bindir'\n";
	print "MODs       = '$mods'\n";
	print "Tcl Interp = '$tclinterp'\n";
	print "Tk  Interp = '$tkinterp'\n";

	print "\nOK to install? (Y/N) [y]: ";
	chop( $ans = <STDIN> );
	die if ( $ans=~ /^n/i );

	print "\n";

	# Install files in HLDIR
	foreach $file ( @hldirfiles ) {
		
		# Don't install if client files already exist
		if( "$file" eq "hlgui.cfg" ) {
			next if( -e "$hldir/hlgui.cfg" );
			next if( -e "$hldir/halfd_client.cfg" );
		} else {
			if( "$file" eq "hlclustermon.cfg" ) {
				next if( -e "$hldir/hlclustermon.cfg" );
				next if( -e "$hldir/halfd_cluster.cfg" );
			}
		}

		&installFile( $file, $file, $hldir, 0 );
	}

	if( "$bindir" ne "none" ) {
		printf "\n";
		# Install Tcl files 
		foreach $file ( @tclfiles ) {
			$myfile = &setFirstLine( $file, $tclinterp );
			&installFile( $myfile, $file, $bindir, 1 );
			unlink( $myfile );
		}

		# Install Tk files 
		if( ! $tkinterp eq "" ) {
			foreach $file ( @tkfiles ) {
				$myfile = &setFirstLine( $file, $tkinterp );
				&installFile( $myfile, $file, $bindir, 1 );
				unlink( $myfile );
			}
		}
	}


	if( "$mods" ne "none" ) {
		print "\n\n";
		# Install mod-specific files
		foreach $mod ( split( / /, $mods )) {
			$moddir = "$hldir/$mod";
			if( -d $moddir ) {
				foreach $file ( @modfiles ) {
					$mymodfile = "$mod/$file";
					$mymodfile = $file if( ! -e "$mod/$file" );
					&installFile( $mymodfile, $file, $moddir, 0 );
					if( $file eq "halfd.cfg" ) {
						upgradeConfigFile( "$file.bak", $file, $moddir );
					}
				}
			} else {
				print "Skipping $mod, not found...\n";
			}
		}
	}

	&installComplete;

	exit( 0 );
}

&main;

