#!/usr/local/bin/perl
# $country        = '7';
# $representative = '7';
# $host           = 'random.chem.psu.edu';
# $port           = 1618;
#
# $Header: pei,v 1.8.1.3 95/05/12 13:38:22 stevens Exp $
#
#                           Perl Empire Interface
#        
#               Based on eif by Doug Hay and hpc by Drake Diedrich
#
#                           written by Ken Stevens
#
#
# DESCRIPTION:
# pei is a powerful, fully extendible smart client for playing Empire.
# Features include:
#   - Tools for managing and feeding civs, auto-exploring, setting dist-paths,
#     calculating the new efficiency of ships/planes/units, reading/sending
#     telegrams and announcements, keeping track of enemy sectors and units,
#     and managing production deltas.
#   - Full perl integration.  You can type perl commands at the empire prompt.
#   - File redirection, piping, exec (read commands from a file), and
#     runfeed (pipe the output of a program to pei).
#   - Aliases.  You can define your own aliases with arguments.
#   - !!, !abc, and ^a^b history substitution
#   - Multiple games.  You may be connected to more than one game at a time,
#     and switch between them.
#   - Function mapping.  You can call your own perl functions from within
#     the client by defining your own function maps.
#   - Output parsing.  Server output is automatically parsed and stored in
#     an internal database.  There is a separate database for each game
#     connected to.
#   - Readline.  If pei finds readline.pl and can load it, then it will use it.
#     But pei does not require readline.
#   - Interrupt handling.  ^C should be handled properly in most cases.
#
# INSTALLATION:
# Before you run this script, make sure that you have typed:
# chmod +x pei
# where pei is the name of this file.
#
# HOW TO RUN IT:
# To get the client up and running, do one of the following:
# 1) Uncomment and edit the four variables $country, $representative,
#    $host, and $port at the top of this file.  Then in your operating system,
#    type 'pei'.
#
# Or, you can specify these four variables as command-line arguments:
# 2) In your operating system type:
#    pei [country] [rep] [host] [port]
#
# Once in the client, type "help" for more information.  The man page
# for pei may be found at the bottom of this file.
#
# BUG REPORTS:
# mail your bug-reports and comments to:
# children@empire.net (Ken Stevens)
# tetherow@cse.unl.edu (Sam Tetherow)

#       --- Glossary ---
#
# These are the terms used in the # comments in this program:
# cprompt           The main Empire command prompt  ($mode eq '6')
# sprompt           An Empire sub-prompt            ($mode eq '4')
# prompt            Either a cprompt or an sprompt
# ecommand          An Empire command sent to a cprompt
# scommand          Text sent to a sub-prompt
# command           A command understood by this client (includes ecommands)
# commandline       A command with its arguments
# line              One or more commandlines separated by semicolons

#       --- Global variables ---
#
# Connection stuff:
# %games            A list of games we can connect to
# $game             The name of the game I am currently connected to
# $country          Our country name
# $representative   Our password
# $host             The host we're connecting to
# $port             The port we're connecting to
# $this             Our socket information
# $that             The socket information for the server
# $thataddr         The TCPIP address of the host
# %connected        A list of games we're connected to
# *S                The socket for the current game
# $terse            Run tools in terse mode?
# *UNTERSEOUT       Where to send output when not running a tool
# NEWOUT            A socket where output is being temprarily redirected
# $handling_broken_pipe Used to prevent infinite regression in &broken_pipe
# $EDITOR           Name of system editor to use while in pei.
# $PAGER            Name of sytem pager to use while in pei.
# $mailflag         If true, mail.pl will not remove mail from the
#                   empire server.
# %mailflag         Same as mailflag, but you can set it just for a game
# $debug            If true, then print connect debug info.
#
# User defined associative arrays:
# %functionmap      A list of commands which map to perl functions
# %parsemap         Defined in parse.pl -- parse the output of this ecommand
# %interruptlev     Interrupt level of an pei subroutine (used in &dokill)
# %alias            User-defined aliases
#
# These i/o variables are used in the main loop:
# $serverline       A line of output from the server
# @history          A history of recent commands (used only in mainloop)
# $maxhistorysize   The number of commands stored in the history list
# $command          The current command
# $commandarg       The arguments of the current command
# @expanded         Expanded aliases (prevents infinite recursion)
# *FIN              Filehandle of file we're readong commands from
# $quiet            Don't print commands read from files (set to 1 for .peirc)
# $interrupt         Did we just interrupt in mainloop?
#
# These three variables are sent by the server every time we get a cprompt:
# $timeused         The amount of time we've been playing so far
# $btus             How many btu's we have left
# $mode             6 = cprompt, 4 = sprompt, 1 = output
#
##CF##
#     ---- FireWall Variables ---
# $proxyname         Name of the proxy server
# $proxyport         Port to connect to on the proxy server
# $proxyprompt       Prompt that triggers us to send address of server
# $firewall          Boolean Flag Used to Indicate if Firewall Exists.

#      --- Functions ---
#
# Main:
# connectloop       Whenever we connect to a new game, we enter this loop
#
# Initialization functions:
# parse_argv        Parse arguments to this program
# init              Initialise variables
# readline'F_MoveCursor Readline cursor movement
# prepare_connect   Prepares a connection to a server
# connect           Connect to the server
# got_from_server   Print an error message if we didn't get what we expected
#
# Read and parse user input:
# parse_commandline Parse a single commandline
# singlecommand     Send a single ecommand to the server unparsed
# parse_line        Parse one line of input
# mainloop          Read lines from standard input and parse them
# fmainloop         Read lines from a file and parse them
# rmainloop         Read lines from readline and parse them
#
# Read and parse server output:
# parse_serverline  Parse output from the server
# slurp             Read & parse server output until prompt
# suck              Read & parse server output until cprompt
#
# Local prompt:
# set_game          Set the coun, rep, host, and port from game name
# prompt            Prompt the user for input
# get_country       Get a country name and representative
# local_prompt      Ask which game the user would like to connect to
#
# Interrupt handler:
# dokill            Called when user hits ^C
# broken_pipe       Called when a pipe breaks
# flush_server_output Flush server output without parsing
# abort_command     Abort ecommand
# do_exit           Exit nicely
#
# Read init file:
# read_peirc        Read variables, aliases, and games from ~/.peirc
#
# Local Commands:
# addgame           add name, coun, rep, host and port to game database
# echo              Print text to the terminal
# alias             Set or show aliases
# print_alias       Print an alias
# unalias           Remove the definition of an alias
# quotify           Put something in quotes so that it can be eval'ed
# strip_quotes      Remove bracketing quotes from text
#
# Function maps:
# execfile          Execute lines from a file
# runfeed           Run a program and pipe the output to pei
# runcommands       Called by execfile and runfeed;
# reconnect         Connect to the host again
# history           print history of our lines (readline only)
#
# Help:
# help              print help
# help_local        print local commands
# help_pei          full pei man page

#       --- Main ---
#
# This is the entry point for pei.  It all starts here...

print "\nWelcome to PEI\nPerl Empire Interface Version 1.8\n\n";

&init;				# initialize variables
&read_peirc;			# read & parse commands from ~/.peirc
&parse_argv;			# parse command-line arguments
&connectloop;			# connect to a game
&do_exit;			# exit nicely

# This is the main pei loop.  Whenever we switch between games, we re-enter
# this loop.
sub connectloop {
  local ($line);
  
  if (defined($AUTOGAME) && defined($games{$AUTOGAME})) {
    &set_game($AUTOGAME);
  }
  if (!defined($EDITOR)) {
    if (defined($ENV{'EDITOR'})) {
      $EDITOR = $ENV{'EDITOR'};
    } else {
      $EDITOR = "vi";
    }
  }
  if (!defined($PAGER)) {
    if (defined($ENV{'PAGER'})) {
      $PAGER = $ENV{'PAGER'};
    } else {
	$PAGER = "more";
    }
  }
 connectloop:			# This label is referenced in parse_commandline
  while (1) {
    # If $country has not been defined yet (e.g. at the top of this file),
    # enter local prompt and ask for a game to connect to.
    if (!$country) {
      do {
	$line = &prompt('Local% ');
	($command,$commandarg) = split(/\s/,$line,2);
	&local_prompt(1);
      } while (1);
    }
    # $game is used as a part of the pei prompt
    ($game) = ($country=~/([A-Za-z]+[1-9]*)/) unless $game;
              # Set game from country using only the packagable part -Drazz'zt
    if (!$game) {		# game had no letters in it e.g. '7'
      ($game) = ($country=~/([A-Za-z1-9]*)/);
      $game = 'game_'.$game;
    }
    # Each game has its own socket.
    *S = eval("SOCKET_$game");
    if ($connected{$game}) { # We are already connected to that game
      print STDERR "Reconnected.\n";
      $mode = '6';
    } else {
      &prepare_connect;		# prepare for the connection
      # If we are able to connect to the host, start playing.
      if (&connect) {
	$connected{$game} = 1;
	if ($debug) {
	  print STDERR "\nplay\n";
	}
	print S "play\n";	# start playing!
	&close_connection unless &got_from_server("2 2");
	print STDERR "Connected!\n";
	&slurp;			# read and print motd
	
	if (!$no_tools) {
	  # Load parse.pl and tools.pl for this game.
	  undef $tools_loaded;
	  eval("package $game; do 'tools.pl';");
	  &nofind_error("tools.pl",$@) if !$tools_loaded;
	  undef $parse_loaded;
	  eval("package $game; do 'parse.pl';");
	  &nofind_error("parse.pl",$@) if !$parse_loaded;
	  eval("package $game; do 'simu.pl';");
	  &nofind_error("simu.pl",$@) if !$simu_loaded;

	  if ($tools_loaded) {
	    &find_country_number;	# find my country number -- needed for tools.pl
	    &tools_init;
            eval ("&".$game."'load_DB");
	  }
	  if (-f "start.exec") {	# if there is a start.exec file
	    &parse_commandline("exec start.exec"); # execute it
	  }
	}
      } else {			# we werent' able to connect
	&close_connection;
      }
    }

    # Set these three variables for the current game.
    eval('$country = $'.$game."'country");
    eval('$coun = $'.$game."'coun");
    eval('$btus = $'.$game."'btus");
    eval('$timeused = $'.$game."'timeused");

    if (-f STDIN) {		# if pei was called as "pei < filename"
      &fmainloop(*STDIN);	# read commands using fmainloop
    } elsif (defined(&readline'readline)) { #' otherwise, if we have readline
      &rmainloop;               # read commands using rmainloop
    } else {                    # otherwise
      &mainloop;                # read commands using &mainloop
    }
  }
}

sub nofind_error {
  local ($file, $error) = @_;

  print "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n";
  if ($error) {
    print "ERROR: pei could not load the file \"$file\" because of the following error:\n";
    warn $error;
  } else {
    print "WARNING:  pei could not locate the file \"$file\"\n";
    print "          It should be in the same directory as pei.  You will not be able to\n";
    print "          use any of the tools in pei until you fix this problem.\n";
  }
  print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n";
}
    

#       --- Initialization Functions ---

# Parse command-line arguments to this program
sub parse_argv {
  if (@ARGV[0] eq '-n') {
    $no_tools = 1;
    shift(@ARGV);
  }
  local ($c,$r,$h,$p) = @ARGV;

  if ($games{$c}) {
    &set_game($c);
    return;
  }
  if ($p) {
    $country = $c;
    $representative = $r;
    $host = $h;
    $port = $p;
  } elsif ($c) {
    print STDERR <<'EOF';
Usage:
  pei [ -n ] [country] [representative] [host] [port]
    -or-
  pei [ -n ] [gamename]
EOF
    &do_exit;
  }  
  if (!$country) {
    $country = $ENV{'EMPIRECOUNTRY'};
    $representative = $ENV{'EMPIREREP'};
    $host = $ENV{'EMPIREHOST'};
    $port = $ENV{'EMPIREPORT'};
  }
}

# Initialize variables
sub init {
  local ($pei_pathname) = $0;

  # Handle having a symbolic entry in, say, ~/bin, pointing to real pei dir.
  if ( -l $pei_pathname ) {
    local($sym_link) = readlink($pei_pathname);
    if ($sym_link =~ s/^(\S+)\/(\S+)$/$1/) {
      push(@INC,$sym_link);
    }
  }
      
  # This code adds the directory from which pei was run to @INC:
  if ($pei_pathname =~ s/^(\S+)\/(\S+)$/$1/) {
    push(@INC,$pei_pathname);
  } else {
    local ($pwd) = `pwd`;
    chop($pwd);
    push(@INC,$pwd);
  }

  # pei will work fine without readline.
  eval("require 'readline.pl'"); # warn $@ if $@; # readline package
  eval("require 'mail.pl'");
  &nofind_error("mail.pl") if !$mail_loaded;

  # Map the following commands to perl functions:
  $functionmap{"eval"} = 'eval';
  $functionmap{"print"} = 'eval';
  $functionmap{"undef"} = 'eval';
  $functionmap{"quit"} = '&quit';
  $functionmap{"exec"} = '&execfile';
  $functionmap{"runfeed"} = '&runfeed';
  $functionmap{"reconnect"} = '&reconnect';
  $functionmap{"history"} = '&history';

  # This is the length of our history list.
  $maxhistorysize = 100;

  # Set the interrupt level for various pei subroutines (used by &dokill):
  $interruptlev{"dokill"} = 99;
  $interruptlev{"fmainloop"} = 5;
  $interruptlev{"mainloop"} = 2;
  $interruptlev{"rmainloop"} = 2;
  $interruptlev{"local_prompt"} = 1;
  $interruptlev{"connectloop"} = 1;
  $interruptlev{"tools_supernova"} = 6;  
  $interruptlev{"tools_xmvr"} = 6;  
  $interruptlev{"tools_router"} = 6;  
  $interruptlev{"tools_setfood"} = 6;  
  
  # If we recieve an interrupt, call &dokill
  $SIG{'INT'} = 'dokill';	# call &dokill if the user types ^C

  # If our pipe breaks, call &broken_pipe
  $SIG{'PIPE'}='broken_pipe';
  
  # These variables are reserved words and are used for parsing:
  $quoted_material = "_Q_M_";	     # material between quotes
  $semicolon_in_quotes = "_S_I_Q_";  # semicolon inside quotes
  
  # Special perl variables
  $; = ',';   # array multiple index emulation
  $, = ", ";  # for printing arrays

  # The following variables are used by &connect.
  eval("require 'sys/socket.ph'");
  if (!defined(&AF_INET)) {
    eval 'sub AF_INET {2;}';
  }
  if (!defined(&SOCK_STREAM)) {
    eval 'sub SOCK_STREAM {1;}';
  }    
  eval("require 'sys/errno.ph'");
  if (!defined(&ECONNREFUSED)) {
    eval 'sub TCP_EBASE {100;}';
    eval 'sub ENETUNREACH {(17+ &TCP_EBASE);}';
    eval 'sub EISCONN {(22+ &TCP_EBASE);}';
    eval 'sub ETIMEDOUT {(26+ &TCP_EBASE);}';
    eval 'sub ECONNREFUSED {(27+ &TCP_EBASE);}';
  }
  # This fixes the cursor-keys for readline.pl
  if (defined(&readline'rl_bind)) { #'
    &readline'rl_bind('C-c', 'PeiInterrupt'); #'
#    &readline'rl_bind('"\\M-[A"', 'PreviousHistory'); #'
#    &readline'rl_bind('"\\M-[B"', 'NextHistory'); #'
#    &readline'rl_bind('"\\M-[C"', 'ForwardChar'); #'
#    &readline'rl_bind('"\\M-[D"', 'BackwardChar'); #'
  }
}

# Don't redraw the prompt if the user hits ^C.
sub readline'F_PeiInterrupt {
  &readline'ResetTTY;
  kill ("INT", 0);
  $readline'pending = $readline'pending . "\n";
}

# This function contains all kinds of "socket magic" that you don't need to 
# understand.
sub prepare_connect {
  local($sockaddr) = 'S n a4 x8';
  local($inet) = 2;
  local($echo) = 7;
  local($name, $aliases, $type, $len, $thisaddr);
  
  chop($hostname = `hostname`);	# get the name of my machine from the system
  ($name, $aliases, $type, $len, $thisaddr) = gethostbyname($hostname);
  if ($host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) {
    $thataddr = sprintf("%c%c%c%c", $1, $2, $3, $4);
  } else {
    ($name, $aliases, $type, $len, $thataddr) = gethostbyname($host);
  }
  $this = pack($sockaddr,$inet,0,$thisaddr);

  ##CF##
  if ($firewall) {
     ($name, $aliases, $type, $len, $thataddr) = gethostbyname($proxyname);
     $that = pack($sockaddr,$inet,$proxyport,$thataddr);
  } else {
     $that = pack($sockaddr,$inet,$port,$thataddr);
  }
}

# Connect to the server
sub connect {
  local ($user, *ME, *NEW_ME);
  local ($mypath) = $INC[$#INC]."/pei";
  if ($debug) {
    print STDERR "Creating socket...";
  }
  if (!socket(S,&AF_INET,&SOCK_STREAM,6)) { # Note eif uses protocol 0 not 6
    print STDERR "socket error: $!\n";
    if (&SOCK_STREAM == 1) {
      print STDERR "Please stand by while I modify myself to solve this problem...";
      if (!open(ME,"<$mypath")) {
	print STDERR "\nERROR: I was unable to find myself--aborting self-modification.\n";
	return 0;
      }
      system ("rm -f _new_pei_.tmp");
      if (!open(NEW_ME,">_new_pei_.tmp")) {
	print STDERR "\nERROR: I was unable to open my new self--aborting self-modification.\n";
	return 0;
      }
      while (<ME>) {
	s/^    eval 'sub SOCK_STREAM {1;}';/    eval 'sub SOCK_STREAM {2;}';/;
	print NEW_ME;
      }
      system ("mv -f _new_pei_.tmp $mypath");
      system ("chmod +x $mypath");
      print STDERR "done\n";
      print STDERR "I will now shut myself off so you can try running my new self.  Goodbye.\n";
      exit (0);
    }      
    return 0;
  }
  if ($debug) {
    print STDERR "done\nBinding socket...";
  }
  if (!bind(S,$this)) {
    print STDERR "bind error: $!\n";
    return 0;
  }
  if ($debug) {
    print STDERR "done\nConnecting to socket...";
  }
  if (!connect(S,$that)) {
    print STDERR "$!.\n";
    print STDERR "Game is probably not up.\n" if $! == &ECONNREFUSED;
    print STDERR "Network problems?\n" if $! == &ETIMEDOUT;
    print STDERR "Good luck.\n" if $! == &ENETUNREACH;
    print STDERR "Already connected.\n" if $! == &EISCONN;
    &close_connection;
  }
  if ($debug) {
    print STDERR "done\n";
  }
  select(S); $| = 1;		# force a flush after every print S
  select(stdout);

 ##CF##
  if ($firewall) {
     local($inp,$x);
     $x = 100;
     while ($x > 0) { # my timeout doesn't work though.
        $inp = <S>;
        chop($inp);
        if ($inp =~ m/^.*$proxyprompt.*$/o) { #Its time to connect
           print S "$host $port\n";           #Send the server address
           last;
        } else {
           $x--;
        }
     }
     &close_connection if ($x <= 0);
  }

  # Send my country information to the server:
  $user = $ENV{'USER'};
  $user = "nobody" if !$user;
  if ($debug) {
    print STDERR "user $user";
  }
  print S "user $user\n";
  &close_connection unless &got_from_server(".*2 Empire server ready");
  print STDERR "\n";  
  &close_connection unless &got_from_server("0 hello $user");
  if ($debug) {
    print STDERR "coun $country\n";
  }
  print S "coun $country\n";
  &close_connection unless &got_from_server("0 country name $country");
  print STDERR "\n";
  if ($debug) {
    print STDERR "pass $representative\n";
  }
  print S "pass $representative\n";
  &close_connection unless &got_from_server("0 password ok");
  print STDERR "        -=O=-\n";
  return 1;
}

sub got_from_server {
  local($expect) = (@_);
  if ($debug) {
    print STDERR "$_\n";
  }
  $_ = <S>;
  chop;
  return 1 if /^$expect$/;
  if (/^a / || $expect eq '2 2' && /^3 /) {
    print $'."\n";
  } else {
    print STDERR "Expected: \"$expect\"\nGot: \"$_\"\n";
  }
  0;
}

#       --- Read and Parse User Input ---

# Parse a single commandline
sub parse_commandline {
  local ($_) = @_;		# the command to be parsed
  local ($oldout);		# save the old filehandle
  local ($redir)="";		# output redirection
  local ($al);			# expanded alias

  if (/^\s*$/) {		# if commandline is all whitespace
    return;
  }
  s/^\s*(\S.*)$/$1/;		# remove whitespace at beginning
  s/^(.*\S)\s*$/$1/;		# remove whitespace at end

# Send scommand to sprompt if there is one:
  if ($mode eq '4') {		# sprompt
    if (/^\?/) {                      # commandline starts with ?
      print S $' . "\n";	# send text to server
      &slurp;			# read & parse server output until prompt
      return;
    }
    else {
      local ($commandline) = $_; # store $_ temprarily because &suck changes it
      &abort_command; 		# send abort to server and read until cprompt
      $_ = $commandline;
    }
  } elsif (/^\?/) {           # ? encountered but not in sprompt
    return;			# ignore the command
  }

  if (/^:/) {			# command starts with :
    local ($syscommand) = $';
    $syscommand =~ s/~/$ENV{'HOME'}/g; # replace ~ with value of $HOME
    system($syscommand);	# send the command to the system
    return;
  }

# Split command and commandargs:
  ($command,$commandarg)=split(/\s/,$_,2);

# Parse Aliases:
  if (defined($alias{$command}) && # is the command aliased to anything?
      !grep(/^$command$/, @expanded)) { # we haven't expanded this alias before
    push(@expanded, $command);	# add $command to list of expanded aliases
    $al = $alias{$command};	# expand the alias
    # Check if there are any $1 type variables in the alias:
    if ($al =~ /\@_\[[0-9]\]/ ||   # If there are $1 or $+ variables in
	$al =~ /\@\{_\[[0-9]\]/ || # the expansion
	$al =~ /\$\+/) {
      /^\s*\S+\s+(\S.*)$/;	# prepare for $+ substitution
      split;			# prepare for $1, $2, ... substitution 
      if ($al =~ /^print S/) {	# this takes care of ${$var:-x} substitutions
	eval ($al);
	&slurp;
	return;
      }
      $al = eval(''.&quotify($al)); # substitute $1, $2, ... and $variables
    } else {
      &strip_quotes(*al);	# remove bracketing quotes from expansion
      if ($commandarg) {	# if we called the alias with arguments
	$al = "$al $commandarg"; # put the arguments after the expansion
      }
    }
    &parse_line($al);	# send the expanded alias to the line parser
    pop(@expanded);
    return;
  }

# File Redirection
  # If we have a > or a | which is not in quotes, redirect the output:
  if (/ (>!|>>|>|\|)/ &&
      !/\"[^\"]*[>|\|][^\"]*\"/ &&
      !/\'[^\']*[>|\|][^\']*\'/) {
    local ($commandline, $rtype, $fname) = ($`, $1, $');
    $fname =~ s/~/$ENV{'HOME'}/g; # replace ~ with $HOME
    # If redirecting to file, check for valid unix filename
    if ($rtype ne '|') {
      if ($fname =~ /^\s*([\/\.\w]+)\s*$/) {
	$fname = $1;
      } else {
	print STDERR "Illegal filename: \"$fname\".  Redirection failed.\n";
	return;
      }
    }
    # If you want to overwrite an existing file, you must use >!
    if ($rtype eq '>' && -e $fname && !(-c $fname)) {
      print STDERR "Error: File \"$fname\" exists.  Redirection failed.\n";
      return;
    }
    elsif ($rtype eq '>!') {
      $rtype = '>';
    }
    $redir= $rtype . $fname;	# where we will send output

    # Open the file or pipe:
    open(NEWOUT,$redir) || print STDERR "Unable to open $redir\n";
    $_ = $commandline;		# our commandline
    $oldout=select(NEWOUT);	# send all new output to NEWOUT
    ($command,$commandarg)=split(/\s/,$_,2); # split command and commandargs
    if ($command ne "echo") {
      print $_ . "\n";		# print our commandline to NEWOUT
    }
  }

  if ($command =~ /^[\$\&\@\%\{]/) { # if command starts with $, &, @, %, or {
    eval($_);			     # it's perl -- eval it!
  } elsif (defined($functionmap{$command})) { # is my command a function map?
    if ($functionmap{$command} =~ /^&tools_/) {
      # Call the function in the tool package for this game
      if ($terse) {
        $| = 1;
	*UNTERSEOUT = select(DEVNULL);
      }
      eval('&'.$game."'tools_".$'); 
      if ($terse) {
	select(UNTERSEOUT);
	$| = 0;
      }
    } else {
      eval($functionmap{$command});
    }
  } elsif (/\S/) {		# if my commandline is not empty
    if (!&local_prompt(0)) {    # Parse local command
# The commandline is an ecommand.  Put double quotes around it and eval it.
# This will ensure that any $variables will get replaced with their values
      print S eval(''.&quotify($_)) . "\n"; # send the command to the server
      &slurp;			# read & parse server output until prompt
    }
  }
  if ($redir) {			# do we have file redirection?
    close(NEWOUT);		# close the file or pipe (this will flush)
    select($oldout);		# revert to the old filehandle output
  }
}

# Send a single ecommand to the server
sub singlecommand {
  local ($commandline) = @_;
  print S $commandline . "\n";	# Go directly to server.  Do not collect $200
  ($command,$commandarg)=split(/\s/,$commandline,2);
  &suck;			# read & parse server output until cprompt
}

# Parse one line of input.  We assume the newline has been stripped.
# This function splits an line into commandlines separated by
# semicolons.  Most of the work in this function consists of taking
# special care of semicolons that are in quotes.
sub parse_line {
  local ($_) = @_;		# put the line in $_
  local (@stack);		# what was between quotes
  local (@commandlines);	# commandlines delimited by semi-colon

  return if /^\s*\#/;		# ignore comments

  if (!/;/) {			# line has no semi-colons.  parse it!
    &parse_commandline($_);	# the line is a commandline
    return;
  }

  # Replace semicolons in ' quotes by the value of $semicolon_in_quotes:
  while (s/(\'[^\']*\')/$quoted_material/) {
    push(@stack, $1);		# put the ' quoted stuff into @stack
  }
  for (@stack) {
    s/;/$semicolon_in_quotes/g;	# codify semi-colons in ' quotes
  }
  s/$quoted_material/shift(@stack)/ge; # put quoted stuff back in

# The following few lines replace semicolons inside quotes by the value
# of $semicolon_in_quotes:
  while (s/(\"[^\"]*\")|(\'[^\']*\')/$quoted_material/) {
    push(@stack, $1);		# put the quoted stuff onto @stack
  }
  for (@stack) {
    s/;/$semicolon_in_quotes/g;	# codify semi-colons in quotes
  }
  s/$quoted_material/shift(@stack)/ge; # put quoted stuff back in

  @commandlines = split(/;/);	# chop the line into commandlines
  for (@commandlines) {
    s/$semicolon_in_quotes/;/g;	# put the semicolons back
    &parse_commandline($_);	# parse each commandline
  }
}

# Read input lines from standard input and parse them
sub mainloop {
  local ($line);		# line of my input
  local ($i);			# history index

  while (1) {			# infinite loop
    if ($mode eq '6') {		# cprompt      
      print "\n$game [$timeused:$btus]% "; # print the cprompt
      $line = <STDIN>;	# read a line of input from STDIN
      chop $line;		# remove newline
      $interrupt = 0 if $line;	# ^C twice in mainloop shuts pei down.

      # The following code does !!, !abc, and ^a^b history expansion:

      if ($line eq '!!' && @history) { # repeat last command
	$line = $history[$#history];
	print $line . "\n";
      } else { 
	if ($line =~ /^!(\S+)/) {	# !abc type substitution
          local ($search) = $1;
          local ($histarg) = $';
	  for ($i = $#history; $i >= 0; --$i) {
	    if ($history[$i] =~ /^$search/) {
	      $line = $history[$i].$histarg;
	      print $line . "\n";
	      last;
	    }
	  }
	} elsif ($line =~ /^\^(.*)\^(.*)$/) { # ^a^b type substitution
	  local ($replace) = $2;
	  $line = $history[$#history];
	  $line =~ s/$1/$replace/;
	  print $line . "\n";
	}
	if ($line =~ s/!!/$history[$#history]/ge) {
	  print $line . "\n";
	}
	# Put $line on our history list
	shift(@history) if @history == $maxhistorysize;
	push(@history, $line);
      }

      @expanded = ();		# reset expanded alias array
      &parse_line($line);	# parse our line of input
    } elsif ($mode eq '4') {	# sprompt
      chop($serverline);	# remove newline
      print "$serverline";	# print the sprompt
      $line = <STDIN>;		# read a line of input
      next unless $line;	# input was aborted
      $scommand=$line;		## Added in for advanced parse.pl stuff -D
      print S $line;		# send the input unparsed to the server
      &slurp;			# read & parse server output until prompt
    } else {
      &suck;			# read & parse server output until cprompt
    }
  }
}

# Read lines from a file and parse them
sub fmainloop {
  local (*IN) = pop(@_);	# The filehandle we're reading input from

  while (<IN>) {		# while there's still lines to read in the file
    if ($mode eq '6' && !$quiet) { # if cprompt and not quiet
      print "\n$game [$timeused:$btus]% $_"; # print cprompt and line
    }
    chop;			# remove newline
    @expanded = ();		# reset expanded alias array
    &parse_line($_);	# parse our line of input
  }
}

# Read lines from readline and parse them
sub rmainloop {
  local ($line);		# line of my input
  
  while (1) {
    if ($mode eq '6') {		# cprompt
      print "\n";
      # read a line of input using readline:
      $line = &readline'readline("$game [$timeused:$btus]% "); #'
      $interrupt = 0 if $line;	# ^C twice in rmainloop shuts pei down.

      # The following code does !!, !abc, and ^a^b history expansion:
      if ($line =~ /!/) {
        if ($line eq '!!' && @readline'rl_History) { #' repeat last command
          $line = $readline'rl_History[$#readline'rl_History - 1]; 
	  print $line . "\n";
          pop(@readline'rl_History); #'
        } else {
	  if ($line =~ /^!(\S+)/) {
	    local ($search) = $1;
            local ($histarg) = $';
	    for ($i = $#readline'rl_History - 1; $i >= 0; --$i) { #'
	      if ($readline'rl_History[$i] =~ /^$search/) {
	        $line = $readline'rl_History[$i].$histarg; #'
		last;
	      }
	    }
	  }
	  $line =~ s/!!/$readline'rl_History[$#readline'rl_History - 1]/ge;
	  print $line . "\n";
	  $readline'rl_History[$#readline'rl_History] = $line;
	}
      } elsif ($line =~ /^\^(.*)\^(.*)$/) { # ^a^b type substitution
	local ($replace) = $2;
	$line = $readline'rl_History[$#readline'rl_History - 1];
	$line =~ s/$1/$replace/;
	print $line . "\n";
        $readline'rl_History[$#readline'rl_History] = $line;
      }

      @expanded = ();		# reset expanded alias array
      &parse_line($line); # parse our line of input
    } elsif ($mode eq '4') {	# sprompt
      chop($serverline);	# remove newline
      if ($line = &readline'readline($serverline)) { #' read a line of input
        pop(@readline'rl_History); # don't keep scommands in history
      }
      print S $line . "\n";
      &slurp;			# read & parse server output until prompt
    } else {
      &suck;			# read & parse server output until cprompt
    }
  }
}

#       --- Read and Parse Server Output ---

# This command assumes that the serverline is in $_.  It sets the
# $mode, $timeused, and $btus.  It returns 1 if $_ is a prompt.
# Otherwise it prints $_.
sub parse_serverline {
  $mode=substr($_,0,1);		# grab the mode
  $serverline = $_ = substr($_,2); # cut the mode part off

  if (defined($parsemap{$command})) {
    eval("&".$game."'$parsemap{$command}");
  }
    
  if ($mode eq '1') {		# normal data
    print $_;
    return 0;
  }
  if ($mode eq '6') {		# cprompt
    ($timeused,$btus) = split(' ');
#*# this stuff is for clients that generate commands
#    if ($btus<200) {
#	    &reconnect;
#    }
	return 1;
  }
  if ($mode eq '4') {		# sprompt
    return 1;
  }
  if ($mode eq '3') {		# server is closing connection
    print STDERR "$_\n";
    &close_connection;
  }
  if (!$mode) {
    print STDERR "Server EOF\nGame died.\n\n";
    &close_connection;
  }
  print STDERR "Strange Empire packet: mode = ($mode) data = ($_)\n";
}

# This function reads lines from the server until a prompt
# is encountered.  If we have written a perl function to parse the output
# of our command, then we call that function.  Note that when the parsemap
# is called, the serverline is in $_.
sub slurp {
  $mode = -1;
  while (<S>) {
    last if &parse_serverline;	# we got a prompt
    $mode = -1;
  }
}

# This function is exactly the same as &slurp except that it ignores sprompts.
sub suck {
  do {
    $mode = -1;
    while (<S>) {
      last if &parse_serverline; # we got a prompt.
      $mode = -1;
    }
    if ($mode ne '6') {		# if not a cprompt
      print S "aborted\n";	# send abort to server
    }
  } while ($mode eq '4');	# while we're at an sprompt
}

#       --- Local Prompt ---

# Given a value of %game, set the country, rep, host, port.  Also, try to 
# change directory to this game's directory.
sub set_game {
  ($game) = @_;
  local ($dir);

  split(/\s/, $games{$game});	# the five fields are separated by a whitespace
  $country        = @_[0];	# removed the eval quotify stuff -Drazz'zt
  $representative = @_[1];	# ""
  $host           = eval(''.&quotify(@_[2]));
  $port           = eval(''.&quotify(@_[3]));
  # Change to the game's directory
  $dir = eval(''.&quotify(@_[4]));
  $dir =~ s/~/$ENV{'HOME'}/g;
  if (!chdir($dir)) {
    print STDERR "Cannot change directory to: \"".eval(''.&quotify(@_[4]))."\"\n";
  }
}

# Prompt the user for input
sub prompt {
  if (defined(&readline'readline)) { #'
    return &readline'readline(pop(@_)); #'
  }
  print pop(@_);
  local ($line);
  $line = <STDIN>;
  chop $line;
  $line;
}

# This function is used by the "try" and "trykill" functions.  It reads a
# country name and password from the user.
sub get_country {
  $country = &prompt("Country: ");
  $representative = &prompt("Representative: ");
}

# Parse local commands
sub local_prompt {
  local ($verbose) = (@_);
  local ($dir, $gamesav);

  &do_exit if $command eq 'exit' || $command eq 'bye';

  if ($command eq 'help') {
    &help;
  } elsif ($command eq 'addgame') {
    &addgame;
  } elsif ($command eq 'reload' && $connected{$game}) {
    print STDERR "reloading tools.pl...";
    # Reload parse.pl and tools.pl for this game.
    undef $tools_loaded;
    eval("package $game; do 'tools.pl';");
    &nofind_error("tools.pl",$@) if !$tools_loaded;
    print STDERR "done\n";
    print STDERR "reloading parse.pl...";
    undef $parse_loaded;
    eval("package $game; do 'parse.pl';");
    &nofind_error("parse.pl",$@) if !$parse_loaded;
    print STDERR "done\n";
    print STDERR "reloading simu.pl...";
    undef $simu_loaded;
    eval("package $game; do 'simu.pl';");
    &nofind_error("simu.pl",$@) if !$simu_loaded;
    print STDERR "done\n";
  } elsif ($command eq 'echo') {
    &echo;
  } elsif ($command eq 'alias') {
    &alias;
  } elsif ($command eq 'quiet') {
    $quiet = !$quiet;
    print "Quiet mode ".($quiet?"on":"off")."\n";
  } elsif ($command eq 'terse') {
    $terse = !$terse;
    print "Terse mode ".($terse?"on":"off")."\n";
    if ($terse) {
      if (!open(DEVNULL,">/dev/null")) {
	die "Unable to open /dev/null for output.\n";
      }
    }
  } elsif ($command eq 'unalias') {
    &unalias;
  } elsif ($command eq "games") { # print a list of games they can connect to
    printf "%8s %-14s %-10s %20s %6s %-14s\n", "name", "country", "password",
    "host", "port", "directory";
    print "-----------------------------------------------------------------------------\n";
    for (sort keys %games) {
      split(/\s/, $games{$_}); # values of %games are separated by whitespace
      $dir = eval(''.&quotify(@_[4]));
      $dir =~ s/^$ENV{'HOME'}/~/; # replace home directory with ~
      if (length($dir) > 14) {
	$dir = "..." . substr($dir,-11);
      }
      printf "%8s %-14s %-10s %20s %6s %-14s\n",$_,
        @_[0],
        @_[1],
        eval(''.&quotify(@_[2])),
        eval(''.&quotify(@_[3])),
        $dir;
    }
  } elsif ($games{$command}) {	# if the game exists
    &set_game($command); # chdir and set coun, rep, host, port
    next connectloop;
  } elsif ($command eq "try") {
    &set_game($commandarg);
    &get_country;		# ask user for country name and password
    $game = $commandarg."_".$country;
    next connectloop;
  } elsif ($command eq "kill" || $command eq "trykill") {    
    if ($games{$commandarg}) {	# if the second word is a game name
      $gamesav = $game;
      &set_game($commandarg);	# chdir and set coun, rep, host, port
      if ($command eq "trykill") {
	&get_country;	# ask user for country name and password
      }
    } else {
      print "No such game: $commandarg\n" if $verbose;
      return 0;
    }
    *S = eval("SOCKET_$game");
    if ($connected{$game}) {
      print "Sending quit to $game\n";
      print S "quit\n";
      close(S);
      $connected{$game} = 0;
    } else {
      &prepare_connect;		# prepare the connection
      &connect;			# login to the host
      print S "kill\n";		# kill our process
      $_ = <S>;
      print;
      close(S);
    }
    &set_game($gamesav);
    next connectloop;
  ##CF##
  } elsif ($command eq 'firewall') {
     print STDERR "Entering Firewall Mode!\n\n";
     $firewall = 1;
     ($proxyname,$proxyport,$proxyprompt)=split(/\s+/,$commandarg,3);
     $proxyprompt =~ s/.*"(.*)".*/"$1"/e; #Only keep what's between the quotes.
  } else {
    print "Not a local command, and not connected to a game.\n" if $verbose;
    return 0;
  }
  1;
}

#       --- Interrupt Handler ---

# Call this function if our process is killed
sub dokill { 
  local (@called_from);		# The output of the caller perl function.
  local ($subroutine);		# Name of the subroutine that was interrupted.
  local ($level);		# Level of the interrupt
  local ($flush);		# Flush server output
  
  $SIG{'INT'} = 'dokill';	# call &dokill if the user types ^C
  kill 9,$child if $child;	# kill child processes

  # Hack in attempts to take care of wait ^C stuff
  if ($main'command eq 'wait') {
    print S "aborted\n";
    return;
  }

  # Get a list of subroutines that we were in and find the one having the
  # highest interrupt level:
  for ($i = 1; @called_from = caller($i); $i++) {
    if (@called_from[3] =~ /^main\'/ || @called_from[3] =~ /^$game\'/) {
      $flush = 1 if $' eq 'parse_serverline' || $' eq 'slurp' || $' eq 'suck';
      if ($interruptlev{$'} > $level) {
	$level =$interruptlev{$'};
	$subroutine = $';
      }
    }
  }

  # Specific interrupt handlers:
  print STDERR "\n";
  if ($subroutine eq 'dokill' || 
      $subroutine eq 'flush_server_output') { # interrupt interrupted
    print STDERR "Interrupted while handling another interrupt.\n";
    &close_connection;
  }

  # Interrupt handler for server output
  if ($mode == -1) {
    print STDERR "Interrupt received while waiting for data from server.\n";
    &flush_server_output;
  } elsif ($flush) {
    print STDERR "Interrupted while reading output from server.\n";
    &flush_server_output;
  }
  
  if (!$level) {		# if none of the subs had an interrupt level
    print STDERR "*** Interrupt occurred in a subroutine that has no interrupt handler:\n";
    for ($i = 1; @called_from = caller($i); $i++) {
      for (@called_from) {	# print the subroutines we ere in
	print STDERR "$_ ";
      }
      print STDERR "\n";
    }
    &do_exit;
  }
    
  # Interrupt handler for user input
  if ($subroutine eq 'mainloop' ||
      $subroutine eq 'rmainloop') {
    if ($command && $mode eq '4') { # we are in sprompt
      &abort_command;		# get out of the sprompt
      if ($readline'prompt) { # if we have readline
	$readline'prompt = "$game [$timeused:$btus]% "; # fix prompt
      }
      return;
    }
    if ($interrupt) {
      &close_connection;
    }
    print STDERR "Interrupt received at main prompt -- one more to close connection.\n";
    $interrupt = 1;
    return;
  }

  # interrupt handler for file input
  if ($subroutine eq 'fmainloop') {
      print STDERR "Interrupted while reading commands from a file.\n";
      if (fileno(FIN) == fileno(STDIN)) { # if in unix "pei < filename"
        &do_exit;		# die nicely
      }
      print STDERR "Closing input file.\n";
      close(FIN);
      return;
    }

  # Interrupt handler for local prompt
  if ($subroutine eq 'local_prompt' ||
      $subroutine eq 'connectloop') {
    print STDERR "Interrupt received at local prompt.\n";
    &do_exit;			# die nicely
  }

  # Interrupt handler for tools
  if ($subroutine eq 'tools_supernova') {
    print STDERR "Interrupt received by nova tool.\nStopping nova.\n";
    $stop_nova = 1;
  }
  elsif ($subroutine eq 'tools_xmvr') {
    print STDERR "Interrupt received by xmvr tool.\nStopping xmvr.\n";
    $stop_xmvr = 1;
  }
  elsif ($subroutine eq 'tools_router') {
    print STDERR "Interrupt received by router tool.\nStopping router.\n";
    $stop_router = 1;
  }
  elsif ($subroutine eq 'tools_setfood') {
    print STDERR "Interrupt received by setfood tool.\nStopping setfood.\n";
    $stop_setfood = 1;
  }
}
  
# Called when a pipe breaks
sub broken_pipe {
  return if $handling_broken_pipe;
  $handling_broken_pipe = 1;
  print STDERR "Broken pipe.\n";
  $SIG{'PIPE'}='broken_pipe';
  &flush_server_output;
  $handling_broken_pipe = 0;
}

# Flush server output without parsing
sub flush_server_output {
  local($syncronized);

  print STDERR "Flushing server output...";
  if ($mode ne '6') {
    if ($mode eq '4') {
      print S "aborted\n";
    }
    print S "spy -1\n";
    $mode = -1;
    do {
      while (<S>) {               # flush server output until cprompt
	$mode = substr($_,0,1);   # grab the mode
	if ($mode eq '4') {
	  print S "aborted\n";
	}
	$syncronized = 1 if $_ eq "1 Usage: spy <SECTS>\n";
	last if ($mode eq '6');   # we got a cprompt
	$mode = -1;
      }
    } while (!$syncronized);
    &parse_serverline;          # set $timeused and $btus
  }
  print STDERR "done\n";
}

# Send an abort to the server, read & parse server output until cprompt.
# Assume we're at an sprompt of $command.
sub abort_command {
  print STDERR "$command aborted.\n";
  print S "aborted\n";
  &suck;
}

sub save_and_close {
  eval ("&".$game."'save_DB");
  print STDERR "Closing connection for $game...";
  close(S);
  print STDERR "*CLICK*\n\n";
  $connected{$game} = 0;
  undef $country;
  undef $game;
}

# close connection and go back to the local prompt.
sub close_connection {
  &save_and_close;
  next connectloop;	# try again
}

# Exit nicely
sub do_exit {
  for (keys %connected) {
    next if !$connected{$_};
    &set_game($_);
    *S = eval("SOCKET_$game");
    &save_and_close;
  }
  print STDERR "pei shutting down...\n";
  exit(0);
}

#       --- Read Init File ---

# Read commands from ~/.peirc.  If there is no ~/.peirc, then try to make
# one from an existing ~/.eifrc.
sub read_peirc {
  local (*PEIRC, *EIFRC);

  if (!(-f "$ENV{'HOME'}/.peirc")) {   # if no ~/.hprcr
    if (!(-f "$ENV{'HOME'}/.eifrc")) { # if no ~/.eifrc
      return;
    } else {			       # we have a ~/.eifrc
      print STDERR "Converting ~/.eifrc to ~/.peirc ...";
      if (!open(EIFRC,"<$ENV{'HOME'}/.eifrc")) { # open ~/.eifrc for read
	print STDERR "Unable to open ~/.eifrc for input\n";
	return;
      }
      if (!open(PEIRC,">$ENV{'HOME'}/.peirc")) { # open #/.peirc for write
	print STDERR "Unable to open ~/.eifrc for output\n";
	close(EIFRC);
	return;
      }
      # convert ~/.eifrc to ~/.peirc
      while (<EIFRC>) {
	next if /connect/;
	s/;\s*&/; ?/g;		# replace sub-prompts
	s/@/:/g;		# replace @ with :
	# Replace ${1:x} variables where possible.  Warn if not possible.
	s/\${(\w+):\+}([\s|\"|\'])/\$$1$2/g;
	s/\${(\w+):\+}/${$1}/g;
        if (s/\${(\w+):\-([^}]+)}/".(\$$1?\$$1:"$2")."/g) {
	  s/^\s*alias\s+(\w+)\s+(.+)$/alias $1 $2/;
	}
        if (/(\${.*:.*\})/) {
	  print STDERR "Unable to convert string \"$&\" on line number $.:\n$_\n\n";
	  next;
        }
      	if (/^\s*addgame/) {	# convert addgame lines
	  split;
	  $_ = "addgame @_[1] @_[4] @_[5] @_[2] @_[3] @_[6]\n";
	} elsif (/^\s*setvar\s+(\S+)\s+(\S.*)\n$/) { # convert setvar lines
	  $_ = "\$$1 = " . &quotify($2) . "\n";
	}
	print PEIRC;
      }
      close(PEIRC);
      close(EIFRC);
      print STDERR "done\n";
    }
  }

  # Now we have ~/.peirc, open it for read.
  if (!open(PEIRC,"<$ENV{'HOME'}/.peirc")) {
    print STDERR "Unable to open ~/.peirc for input\n";
    return;
  }
  $quiet = 1;			# don't print commandlines read from ~/.peirc
  while (<PEIRC>) {
    if (/^\s*$/ || /^\s*#/) {	# if all whitespace or commant
      next;			# skip this line of input
    }
    if (/^[\$\&\@\%\{]/) {	# if it's perl
      eval($_);			# evaluate it
      next;
    }
    chop;			# remove newline
    ($command,$commandarg)=split(/\s/,$_,2); # split command and commandargs
    &local_prompt(1);
  }
  $quiet = 0;			# print commandlines read from future files
  close (PEIRC);
}

#        --- Function Maps ---

# Quit game and go back to the local prompt:
sub quit {
  print S "quit\n";
  &slurp;
}

# This function reads and parses commands from a specified file
sub execfile {
  local ($filename) = $commandarg;
  $filename =~ s/~/$ENV{'HOME'}/g;
  &runcommands ("<$filename");
  print STDERR "End of file \"$filename\"\n" unless $quiet;
}

# This function runs a program and pipes the output to pei
sub runfeed {
  local ($filename) = $commandarg;
  $filename =~ s/~/$ENV{'HOME'}/g;
  eval('&runcommands("'.$filename.'|")');
  print STDERR "End of runfeed \"$filename\"\n";
}

# This function runs commands from a file or pipe
sub runcommands {
  local (*IN);

  if (!open(IN,pop(@_))) {
    print STDERR "Unable to open $commandarg\n";
    return;
  }
  *FIN = IN;			# global variable used in interrupt handling
  &fmainloop(*IN);		# parse the commands using fmainloop
  close(IN);
}

# Reconnect to the server
sub reconnect {
  print S "quit\n";
  close(S);			# close the socket
  $connected{$game} = 0;
  next connectloop;
}

# Print text to the terminal.
sub echo {
  print eval(''.&quotify($commandarg)) . "\n";
}

# Either set an alias, show an alias, or show all aliases
sub alias {
# Either set $name and $val, just $name, or nothing depending on $commandarg
  local ($name,$val) = ($commandarg =~ /^(\S*)\s*((\S+(\s+\S+)*)*)/);
  if ($val) {
    $val =~ s/\$([0-9]+)/\@_[$1]/g;           # convert $1 to @_[1]
    $val =~ s/\${([0-9]+)}/\@{_[$1]}/g;       # convert ${1} to @{_[1]}
    $alias{$name} = $val;
  }
  elsif ($name) {	# if only name is specified, show the alias
    if ($alias{$name}) {
      &print_alias($name);
    } else {
      print "$name undefined.\n";
    }
  }
  else {
    foreach $name (sort(keys %alias)) { # show all aliases
      &print_alias($name);
    }
  }
}

# Print one alias.  This function converts aliases back into what they
# would have looked like when the user typed them in.
sub print_alias {
  local($name) = @_;
  local($val) = $alias{$name};
  $val =~ s/\@_\[([0-9]*)\]/\$$1/g;             # convert @_[1] to $1
  $val =~ s/\@{_\[([0-9]*)\]}/\${$1}/g;         # convert @_{[1]} to ${1}
  print "alias $name " . &quotify($val) . "\n"; # \ escape quotes
}

# Remove an alias
sub unalias {
  if ($alias{$commandarg}) {
    delete $alias{$commandarg};
    print "Alias \'$commandarg\' deleted.\n";
  } else {
    print "Alias \'$commandarg\' not found.\n";
  }
}

# Prepare a commandline for evaluation by putting double quotes around it.
sub quotify {
  local ($val) = @_;
  if ($val =~ /^\"|^\'/) {	# it's already enclosed in quotes
    return $val;		# leave it alone
  }
  $val =~ s/\"/\\\"/g;		# \ escape internal quotes
  return '"' . $val . '"';	# enclose in double quotes
}

# Remove bracketing quotes from text
sub strip_quotes {
  local (*s) = @_;

  $s =~ s/^\"(.*)\"$/$1/;
  $s =~ s/^\'(.*)\'$/$1/;
}

# Print a history of recent commands.
sub history {
  local ($i);			# history index

  if (@readline'rl_History) {#'
    for ($i = 0; $i < $#readline'rl_History; ++$i) { #'
      printf "%-4d  %s\n", $i, $readline'rl_History[$i];#'
    }
  } else {
    for ($i = 0; $i < $#history; ++$i) {
      printf "%-4d  %s\n", $i, $history[$i];
    }
  }
}

sub addgame {
  local ($name, $rest) = split(/\s/, $commandarg, 2);
  if ($name =~ /^\w+$/) {
    $games{$name} = $rest;
  } else {
    print STDERR "Invalid game name: \"$name\"\n";
  }
} 

#       --- Help ---

sub help {
  if ($commandarg eq "local") {
    &help_local;
  } elsif ($commandarg eq "tools") {
    &help_tools;
  } elsif ($commandarg eq "pei") {
    &help_pei;
  } else {
    print <<'EOF';
  Help is available for the following subjects:
    help local    - local commands
    help tools    - pei tools
    help pei      - complete pei man page
EOF
  }
}

sub help_local {
  print <<'EOF';
  These commands are local commands which may be typed at the local prompt
  or at the pei prompt:
    help [subject]          - Print help on [subject].
    addgame [game] [country] [password] [host] [port] [directory]
                            - Add [game] to pei where [directory] is a unix
			      directory that pei cd's to before connecting
                              to the game.
    games                   - List games.
    [game]                  - Connect to [game].
    try [game]              - Connect to [game] as a different country.
    kill [game]             - Kill process [game] on host.
    trykill [game]          - Kill process [game] on host with a different
                              country.
    echo [text]             - Print [text] to the terminal.
    alias [name] [stuff]    - Define [name] to mean [stuff] (see "Aliases"
                              below).
    unalias [name]          - Remove alias definition for [name].
    terse                   - Toggle terse mode.  If terse is on, then tools
                              run quietly (useful for modems with low BAUD).
    quiet                   - Toggle quiet mode.  If quiet is on, then commands
                              read from exec files are not printed.
    firewall [proxyname] [proxyport] ["proxyprompt"]
                            - Use the proxy server to connect to the empire
                              server. [proxyprompt] is the prompt from the
                              server telling pei its ready to accept an address.
                              proxyprompt should be in double quotes.
    exit                    - Exit pei.
    bye                     - Exit pei.
EOF
}

# Use "more" to print the pei tools man page.
sub help_tools {
  local (*MORE);
  if (!open(MORE, "|$PAGER")) {
    print STDERR "Unable to find \"$PAGER\" on your system.\n";
    return;
  }
  print MORE <<'EOF';
These commands are "tool commands" and may be typed at the pei prompt when
you are conencted to a game.  Note that if <SECTS>, <SHIPS>, or <UNITS> are
not specified in any of the tools, then '*' is used.  Default <REALM> is '#'.
Note also that ?switches may always be used in <SECTS> specifications.
  These tools change your country:
    nova <REALM> - Auto-explore all adjacent wilderness sectors in <REALM>.
                    If <REALM> is not specified, #0 is used.
                    e.g. nova #1
    cmvr <SECTS> <THRESH>
                  - Move civs in <SECTS> to try to get <THRESH> in each
                    sector.  If <THRESH> is not specified, then the value of 
                    "max safe civs" from the nation report is used.
                    e.g. cmvr
                    e.g. cmvr #3
                    e.g. cmvr #12 ?newd=g 300
    umvr <SECTS> <THRESH> - Move uws (just like cmvr).
    imvr <SECTS>, dmvr <SECTS>, lmvr <SECTS>, smvr <SECTS>, ...
                  - Move iron, dust... in <SECTS> according to dist thresholds.
    NOTE: The above tools will never lower the mobility of a sector below the
    value specified in the $minmob array.  If you don't like the default values
    for $minmob, then edit $minmob in the xmvr_init function of tools.pl.
    setfood <SECTS>
                  - Set food thresholds for maximum civ growth.  (setfood will
                    not reduce any existing thresholds.)  After calling
                    "setfood", you can call "fmvr" to move food into the
                    sectors that need it.  Won't set thresholds in warehouses.
    jack <SECTS>  - Set iron thresholds in your "j" and "k" sectors to the
                    value of "max1" in your production report.  After calling
                    "jack", you can call "imvr" to move iron into the sectors
                    that nead it.
    fus <SHIPS>   - Fuel ships in harbours and generate an error message if
                    the harbour needs more fuel.  If the ship is at sea, and
                    it is not completely fueled, then the number of sectors
                    it will be able to sail before needing to refuel is shown.
    router <SECTS> 
                  - Dist all sectors to the nearest warehouse.  If there isn't
                    a warehouse within 10 sectors, make a useless sector into
                    a warehouse and dist to it.
    foreach <SECTS> [ecommand]
                  - Perform the Empire command [ecommand] on <SECTS>
                    where instances of the variables $sect, $civ,
                    $mil, $uw, $food, $iron, $lcm, $hcm, $iron, $dust,
                    $bar, $oil, $rad, $pet, $shell, and $gun in
                    [ecommand] will be replaced by their values for
                    each sector.
                    e.g. foreach #1 ?mil<5 move m 0,0 5-$mil $sect
                    (Make sure that each sector in #1 has at least 5 mil
                    in it--if it doesn't, move the necessary amount of mil
                    from 0,0 into the sector.)
  These tools tell you information about your country:
    civs <SECTS>  - Produces a report showing which sectors have too many
                    civs, and which sectors have too few (based on how
                    many commodities are in the sector and what the neweff
                    will be).
    rebel <SECTS> - Tells you how many more mil conquered sectors need
                    to keep them from rebelling.
    crew <SHIPS>  - This command specifies exactly how many civs or mil your
                    ships will need so that they won't decay when they are out
		    at sea.
    reach         - Tells you the range of your forts, radar stations, and 
                    coastwatch.
    sreach <SHIPS>
                  - Tells you the fire range of your ships, and how many
                    sectors they can nav this update and next update.
    lreach <UNITS>
                  - Tells you the fire range of your land units, and how many
                    100% (non-road non-mountain) sectors they can march this
                    update and next update.
    sneweff <SECTS>
    pneweff <SECTS>
    lneweff <SECTS>
                  - Calculates the new efficiency that ships, planes,
                    or land units will have after the next update.  If
                    the sector doesn't have enough commodities to
                    build all the units, then the extra commodities
                    needed will show up in brackets in the
                    commodities' column.  The avail column will show
                    the ammount of avail that this ship, plane, or
                    unit will use.  If the value in the avail column
                    is in brackets it indicates the ammount of avail
                    that this ship, plane, or unit needs for maximum
                    production.  Lastly, if a ship needs more mil on
                    board to prevent efficiency decay, that will
                    appear in brackets in the "Crew" column.  The "total"
                    at the bottom sums the total commodities consumed in the
                    sector for building.  Any extra commodities in the sector
                    (i.e. not being used for building) will be preceded by a +.
                    e.g. sneweff #1
                    e.g. pneweff 11,11
    delta <SECTS>
    fdelta <SECTS>
    wdelta <SECTS>
                  - These three commands calculate production deltas in 
                    different ways.  The <SECTS> argument refers to
                    distribution centres.  So only sectors which distribute
                    into <SECTS> are included in the calculations.  So a sector
                    in <SECTS> which distributes outside <SECTS> will not
                    be included, while a sector outside <SECTS> which
                    distributes into <SECTS> will be included.
                  - "delta" adds up the total amount of commodities produced
                    and subtracts that from the total amount consumed.  The
                    "supply" column says how many updates of supply you have
                    left of that commodity.
                  - "fdelta" projects civ/uw growth versus food production
                    over the next 15 updates.
                  - "wdelta" is like "delta" except that it bases its
                    calculation on what will happen inside the warehouses.
                    i.e. how much of each commoditiy is going into storage
                    versus how much is being drawn out of storage.
    simu <SECTS>  - "simulate" an update.  Pei runs your country through a 
                    simulated update, showing how many of your commodities
                    will get distributed where at the update.  The update is
                    always simulated for *all* the sectors in your country.
                    The <SECTS> argument specifies which sectors you'd like
                    displayed.  The "mob" field specified how much mobility
                    will be left in the sector after stuff is distributed out
                    but before new mobility is added.  The "commodities" field
                    shows which commodities will end up in which sectors.  A
                    "+" after a commodity means that not all of that commodity
                    got disted out.  A "-" means that the sector didn't get all
                    that it wanted.  An "a" in the "make" field refers to how
                    much avail will be created in that sector.
    cmap <REALM>  - Like bmap, but prints a letter representing the owner of
                    sectors not owned by you, so you can see who is where.
    stat <SECTS> (?switches)
                  - Report known information on all sectors in <SECTS>
                    that statisfy ?switches (if defined).  All normal
                    sector switches apply although if you do not know
                    the information about the sector in question the
                    check will probably fail.
                    e.g. stat * ?own=27
    sstat <REALM> (?switches)
    pstat <REALM> (?switches)
    lstat <REALM> (?switches)
                  - Report known information on all land units, planes,
                    or ships within <REALM> that satisfy ?switches (if
                    definded).  Valid switches for land units are:
                    own, tech, eff, and type.  The only difference
                    between these switches and normal empire switches
                    are that type will handle a partial match.
                    e.g. lstat * ?own=27&type=inf
    mail, wmail   - mail-like interface for reading telegrams and
                    announcements.  Allows replying, forwarding, and saving
                    to a file.  To reply to an announcement as an announcement,
                    forward without a country number.  Type "?" in the mailer
                    for help.
    tele, anno    - Use the editor specified in the $EDITOR variable to compose
                    a telegram or announcement.  When you have finished editing
                    your tele or anno, pei will chop it into 1024 byte blocks
                    and send it off.
EOF
}

# Use "more" to print the pei man page.
sub help_pei {
  local (*MORE);
  if (!open(MORE, "|$PAGER")) {
    print STDERR "Unable to find \"$PAGER\" on your system.\n";
    return;
  }
  print MORE <<'EOF';
NAME
  pei - Perl Empire Interface

SYNOPSIS
  pei [ -n ] [country] [representative] [host] [port]
    -or-
  pei [ -n ] [gamename]

DESCRIPTION
  pei is a powerful, fully extendible smart client for playing Empire.
  Features include:
    - Tools for managing and feeding civs, auto-exploring, setting dist-paths,
      calculating the new efficiency of ships/planes/units, reading/sending
      telegrams and announcements, keeping track of enemy sectors and units,
      and managing production deltas.
    - Full perl integration.  You can type perl commands at the empire prompt.
    - File redirection, piping, exec (read commands from a file), and
      runfeed (pipe the output of a program to pei).
    - Aliases.  You can define your own aliases with arguments.
    - !!, !abc, and ^a^b history substitution
    - Multiple games.  You may be connected to more than one game at a time,
      and switch between them.
    - Function mapping.  You can call your own perl functions from within
      the client by defining your own function maps.
    - Output parsing.  Server output is automatically parsed and stored in
      an internal database.  There is a separate database for each game
      connected to.
    - Readline.  If pei finds readline.pl and can load it, then it will use it.
      But pei does not require readline.
    - Interrupt handling.  ^C should be handled properly in most cases.

  If the option -n is specified, then pei will be loaded without any tools.
  This is useful if you just want to log on quickly and check your mail.

  An instance of pei begins by executing commands from the file .peirc in
  the home directory of the invoker.  If a game has not been specified, then
  the user is presented with a 'Local%' prompt at which they can choose
  any game that was specifed by an 'addgame' line in .peirc.

  Once a game has been specified, pei tries to connect to the game.  If it
  was unable to connect, it will exit with a message explaining what went
  wrong.

  Once connected, pei begins executing commands from the file start.exec
  in the current directory.  Note that 'addgame' lines in .peirc can
  associate to each game its own directory.
    
  Then pei repeatedly performs the following actions:  Lines are read from
  the server and parsed until a prompt is encountered, then terminal input
  is parsed until an Empire command is encountered at which point pei sends
  the command to the server.

Lexical Structure
  An input line can come from one of three places:
    (1) You typed it in.
    (2) It was read in from a file (e.g. "start.exec").
    (3) It was the value of an alias.

  In case (1), if the input line is of one of the following forms, it will
  undergo history substitution:
    !!			    - Repeat the last command.
    ![text]                 - Repeat the last command starting with [text].
                              e.g. !cen
    ^[text]^[replace]       - Replace [text] with [replace] in last command.
                              e.g. ^jjh^jnh
      
  Input lines beginning with # are comments and are ignored.

  Each input line consists of sequence of command lines separated by semicolons
  (note that semicolons inside single or double quotes don't count):
    commandline1; commandline2; commandline3; ...

  Each commandline has the following syntax:
    ?[scommand]             - If we are at a sub-prompt, send [scommand] to it,
                              otherwise, ignore.
                              e.g. attack 3,3; ?10; ?2
    [ecommand]              - Send the empire command [ecommand] to the
                              server.  If we were expecting ?[scommand],
                              abort the previous command before sending.
                              e.g. cen 0,0
                              e.g. move; cen 2,0 (aborts move before doing cen)
    :[syscmd]               - Send [syscmd] to your operating system.
                              e.g. :ls
    [command] > [filename]  - Write the output of [command] to [filename]
                              only if the file does not exist.
                              e.g. cen #1 > cenfile
    [command] >! [filename] - Write the output of [command] to [filename]
                              (if the file exists, overwrite it).
                              e.g. cen #1 >! cenfile
    [command] >> [filename] - Append the output of [command] to [filename].
                              e.g. spy 3,3 >> spyfile
    [command] | [syscmd]    - Pipe the output of [command] to the system
                              command [syscmd].
                              e.g. news | more

  Any commandline beginning with "$", "&", "@", "%", "{", "print ", "undef ",
  or "eval " will be evaluated as a perl expression.

Commands
  These commands are local commands which may be typed at the local prompt
  or at the pei prompt:
    help [subject]          - Print help on [subject].
    addgame [game] [country] [password] [host] [port] [directory]
                            - Add [game] to pei where [directory] is a unix
			      directory that pei cd's to before connecting
                              to the game.
    games                   - List games.
    [game]                  - Connect to [game].
    try [game]              - Connect to [game] as a different country.
    kill [game]             - Kill process [game] on host.
    trykill [game]          - Kill process [game] on host with a different
                              country.
    echo [text]             - Print [text] to the terminal.
    alias [name] [stuff]    - Define [name] to mean [stuff] (see "Aliases"
                              below).
    unalias [name]          - Remove alias definition for [name].
    terse                   - Toggle terse mode.  If terse is on, then tools
                              run quietly (useful for modems with low BAUD).
    quiet                   - Toggle quiet mode.  If quiet is on, then commands
                              read from exec files are not printed.
    firewall [proxyname] [proxyport] ["proxyprompt"]
                            - Use the proxy server to connect to the empire
                              server. [proxyprompt] is the prompt from the
                              server telling pei its ready to accept an address.
                              proxyprompt should be in double quotes.
    exit                    - Exit pei.
    bye                     - Exit pei.

  These commands are "function maps", and may be typed at the pei prompt when
  you are connected to a game:
    quit                    - Log out of the current game.
    exec [filename]         - Read input lines from [filename].  If you don't
                              want the commands printed to the terminal, then
                              before the exec, you should have "$quiet = 1".
                              e.g. exec pei_commands
    runfeed [syscmd]        - Run the program [syscmd] and pipe the output
                              to pei.
                              e.g. runfeed mycivmover
    reconnect               - Disconnect and reconnect to the server.
    history                 - Print the last $maxhistorysize input lines.
  Note that any [filename] or [syscmd] may use ~ to refer to the home
  directory of the user.

These commands are "tool commands" and may be typed at the pei prompt when
you are conencted to a game.  Note that if <SECTS>, <SHIPS>, or <UNITS> are
not specified in any of the tools, then '*' is used.  Default <REALM> is '#'.
Note also that ?switches may always be used in <SECTS> specifications.
  These tools change your country:
    nova <REALM> - Auto-explore all adjacent wilderness sectors in <REALM>.
                    If <REALM> is not specified, #0 is used.
                    e.g. nova #1
    cmvr <SECTS> <THRESH>
                  - Move civs in <SECTS> to try to get <THRESH> in each
                    sector.  If <THRESH> is not specified, then the value of 
                    "max safe civs" from the nation report is used.
                    e.g. cmvr
                    e.g. cmvr #3
                    e.g. cmvr #12 ?newd=g 300
    umvr <SECTS> <THRESH> - Move uws (just like cmvr).
    imvr <SECTS>, dmvr <SECTS>, lmvr <SECTS>, smvr <SECTS>, ...
                  - Move iron, dust... in <SECTS> according to dist thresholds.
    NOTE: The above tools will never lower the mobility of a sector below the
    value specified in the $minmob array.  If you don't like the default values
    for $minmob, then edit $minmob in the xmvr_init function of tools.pl.
    setfood <SECTS>
                  - Set food thresholds for maximum civ growth.  (setfood will
                    not reduce any existing thresholds.)  After calling
                    "setfood", you can call "fmvr" to move food into the
                    sectors that need it.  Won't set thresholds in warehouses.
    jack <SECTS>  - Set iron thresholds in your "j" and "k" sectors to the
                    value of "max1" in your production report.  After calling
                    "jack", you can call "imvr" to move iron into the sectors
                    that nead it.
    fus <SHIPS>   - Fuel ships in harbours and generate an error message if
                    the harbour needs more fuel.  If the ship is at sea, and
                    it is not completely fueled, then the number of sectors
                    it will be able to sail before needing to refuel is shown.
    router <SECTS> 
                  - Dist all sectors to the nearest warehouse.  If there isn't
                    a warehouse within 10 sectors, make a useless sector into
                    a warehouse and dist to it.
    foreach <SECTS> [ecommand]
                  - Perform the Empire command [ecommand] on <SECTS>
                    where instances of the variables $sect, $civ,
                    $mil, $uw, $food, $iron, $lcm, $hcm, $iron, $dust,
                    $bar, $oil, $rad, $pet, $shell, and $gun in
                    [ecommand] will be replaced by their values for
                    each sector.
                    e.g. foreach #1 ?mil<5 move m 0,0 5-$mil $sect
                    (Make sure that each sector in #1 has at least 5 mil
                    in it--if it doesn't, move the necessary amount of mil
                    from 0,0 into the sector.)
  These tools tell you information about your country:
    civs <SECTS>  - Produces a report showing which sectors have too many
                    civs, and which sectors have too few (based on how
                    many commodities are in the sector and what the neweff
                    will be).
    rebel <SECTS> - Tells you how many more mil conquered sectors need
                    to keep them from rebelling.
    crew <SHIPS>  - This command specifies exactly how many civs or mil your
                    ships will need so that they won't decay when they are out
		    at sea.
    reach         - Tells you the range of your forts, radar stations, and 
                    coastwatch.
    sreach <SHIPS>
                  - Tells you the fire range of your ships, and how many
                    sectors they can nav this update and next update.
    lreach <UNITS>
                  - Tells you the fire range of your land units, and how many
                    100% (non-road non-mountain) sectors they can march this
                    update and next update.
    sneweff <SECTS>
    pneweff <SECTS>
    lneweff <SECTS>
                  - Calculates the new efficiency that ships, planes,
                    or land units will have after the next update.  If
                    the sector doesn't have enough commodities to
                    build all the units, then the extra commodities
                    needed will show up in brackets in the
                    commodities' column.  The avail column will show
                    the ammount of avail that this ship, plane, or
                    unit will use.  If the value in the avail column
                    is in brackets it indicates the ammount of avail
                    that this ship, plane, or unit needs for maximum
                    production.  Lastly, if a ship needs more mil on
                    board to prevent efficiency decay, that will
                    appear in brackets in the "Crew" column.  The "total"
                    at the bottom sums the total commodities consumed in the
                    sector for building.  Any extra commodities in the sector
                    (i.e. not being used for building) will be preceded by a +.
                    e.g. sneweff #1
                    e.g. pneweff 11,11
    delta <SECTS>
    fdelta <SECTS>
    wdelta <SECTS>
                  - These three commands calculate production deltas in 
                    different ways.  The <SECTS> argument refers to
                    distribution centres.  So only sectors which distribute
                    into <SECTS> are included in the calculations.  So a sector
                    in <SECTS> which distributes outside <SECTS> will not
                    be included, while a sector outside <SECTS> which
                    distributes into <SECTS> will be included.
                  - "delta" adds up the total amount of commodities produced
                    and subtracts that from the total amount consumed.  The
                    "supply" column says how many updates of supply you have
                    left of that commodity.
                  - "fdelta" projects civ/uw growth versus food production
                    over the next 15 updates.
                  - "wdelta" is like "delta" except that it bases its
                    calculation on what will happen inside the warehouses.
                    i.e. how much of each commoditiy is going into storage
                    versus how much is being drawn out of storage.
    simu <SECTS>  - "simulate" an update.  Pei runs your country through a 
                    simulated update, showing how many of your commodities
                    will get distributed where at the update.  The update is
                    always simulated for *all* the sectors in your country.
                    The <SECTS> argument specifies which sectors you'd like
                    displayed.  The "mob" field specified how much mobility
                    will be left in the sector after stuff is distributed out
                    but before new mobility is added.  The "commodities" field
                    shows which commodities will end up in which sectors.  A
                    "+" after a commodity means that not all of that commodity
                    got disted out.  A "-" means that the sector didn't get all
                    that it wanted.  An "a" in the "make" field refers to how
                    much avail will be created in that sector.
    cmap <REALM>  - Like bmap, but prints a letter representing the owner of
                    sectors not owned by you, so you can see who is where.
    stat <SECTS> (?switches)
                  - Report known information on all sectors in <SECTS>
                    that statisfy ?switches (if defined).  All normal
                    sector switches apply although if you do not know
                    the information about the sector in question the
                    check will probably fail.
                    e.g. stat * ?own=27
    sstat <REALM> (?switches)
    pstat <REALM> (?switches)
    lstat <REALM> (?switches)
                  - Report known information on all land units, planes,
                    or ships within <REALM> that satisfy ?switches (if
                    definded).  Valid switches for land units are:
                    own, tech, eff, and type.  The only difference
                    between these switches and normal empire switches
                    are that type will handle a partial match.
                    e.g. lstat * ?own=27&type=inf
    mail, wmail   - mail-like interface for reading telegrams and
                    announcements.  Allows replying, forwarding, and saving
                    to a file.  To reply to an announcement as an announcement,
                    forward without a country number.  Type "?" in the mailer
                    for help.
    tele, anno    - Use the editor specified in the $EDITOR variable to compose
                    a telegram or announcement.  When you have finished editing
                    your tele or anno, pei will chop it into 1024 byte blocks
                    and send it off.

Variables
  Any pei command may contain perl variables.  These variables will be replaced
  by their values when the command is parsed.  For example:
    $a = "0,0"
    cen $a           (evaluates to "cen 0,0")
  If you want to isolate a variable name from surrounding text, then you may
  use {} brackets as in:
    $x = "ti"
    na${x}on         (evaluates to "nation")

  Here are some special pei variables which you can define:
  $AUTOGAME       - If defined in ~/.peirc, then pei will automatically connect
                    to this game when it starts up.
  $maxhistorysize - The number of input lines remembered by pei in the history
                    mechanism (default is 100).
  $EDITOR         - The editor that you would like to use for composing
                    telegrams and announcements (default is "vi" or
                    environment variable EDITOR if defined).
  $PAGER          - Program to display output one screenful at a time.
                    (default is "more" or environment variable PAGER
                    if defined).
  $mailflag       - If $mailflag = 1 or $mailflag{gamename} = 1, then when you
  %mailflag         use "mail" to read your telegrams, pei will send "n" to
                    the server so that your telegrams won't get deleted.
		    Useful for co-rulers.
  $debug          - If $debug = 1, then pei will display the login conversation
                    between pei and the server when connecting to a game.
Aliases
  You can define aliases in pei using the syntax:
    alias [name] [stuff]
  For example:
    alias d- des * ?newd=- +

  If you want to put multiple commands in an alias, then you must enclose
  [stuff] in quotes.  For example:
    alias cod "res * ?des=o&ocon<10; res * ?des=g&gold<10"

  If you put perl variables in [stuff], then they will be evaluated at
  runtime.  For example:
    alias ca cen $a
    $a = "0,0"
    ca                    (evaluates to "cen 0,0")

  The following variables are also defined:
    $0                    - The alias name
    $1, $2, $3, ...       - Arguments 1, 2, 3, ... that were used when
                            the alias was called
    $+                    - All of the arguments
  For example, if you define the alias foo as follows:
    alias foo first: $1, second: $2, all: $+, This should be foo: $0
  then:
    foo a b c d e f
  would evaluate to:
    first: a, second: b, all: a b c d e f, This should be foo: foo
  Here's a more useful example:
    alias bridge "build b $1; ?$2; expl c $1 1 ${2}h"

  Alias recursion is permitted.  For example:
   alias xp explore
   alias xx xp c 0,0 1
   xx jjh         (evaluates to "explore c 0,0 1 jjh")

Readline
  If you are using readline, you will be able to use the following keys:
    C-a     beginning of line
    C-b     cursor backward
    C-c     interrupt
    C-d     delete char
    C-e     end of line
    C-f     cursor forward
    C-g     abort
    C-h     backspace
    TAB     complete filename
    RETURN  accept line
    C-k     kill line
    C-l     clear screen
    C-n     next history
    C-p     previous history
    C-q     quoted insert
    C-r     reverse search history
    C-s     forward search history
    C-t     transpose characters
    C-y     yank
    C-z     suspend
    C-_     undo
    ESC <   beginning of history
    ESC >   end of history
    ESC B   backward word
    ESC D   kill word
    ESC F   forward word
    ESC R   revert line
    ESC T   transpose words
    ESC Y   yank pop
    ESC ?   possible completions
    ESC TAB tab insert
  
Comparison to eif
  For those of you who are used to using eif, I've included this section to
  describe how eif syntax translates into pei syntax.

  The easiest way to see how eif syntax translates into pei syntax is to see
  how pei converts your ~/.eifrc into ~/.peirc (this is automatically done
  if pei doesn't find ~/.peirc).  For your reference, here is a translation
  table of eif syntax to pei syntax:

  When converting ~/.eifrc to ~/.peirc, the following translations are made:
    addgame a b c d e f               addgame a d e b c f
    &                                 ?
    @                                 :
    setvar name "string"              $name = "string"
    ${N:+}                            ${N}
    ${N:-word}                        (tricky -- watch how pei converts it)

  These translations you'll have to make by hand:
    connect gamename                  gamename
    unsetvar name                     undef $name
    $%name                            $ENV{'name'}
    !?str?                            C-r str (in readline)

  The following eif syntax should simply be rewritten in perl:
    ${N:?word}
    ${<:%word}
    ${name:=word}    

  The eif syntax !n and !-n is currently not supported.

ENVIRONMENT VARIABLES
  PERLLIB       List of directories searched when locating PERL
                libraries.  Used to find tools.pl, parse.pl,
                and readline.pl on systems that cannot automatically
                locate these libraries.
  EMPIRECOUNTRY, EMPIREREP, EMPIREHOST, EMPIREPORT
                If specified pei will use these variables to connect
                to an empire game.
  EDITOR        Default editor for pei.
  PAGER         Default screen pager to be used in pei.

FILES
  README        Things that users of older versions of pei will need
                to know about this latest version.
  INSTALL       Installation instructions.
  BUGS          A list of known bugs.
  CHANGES       A record of what got changed with version upgrades.
  WISH_LIST     Changes that we hope to implement in the future.
  pei           The program you run.
  readline.pl   Readline package.
  parse.pl      Parses server output into internal database.
  tools.pl      Contains tools (nova, cmvr, ...).
  mail.pl       Contains mail, wmail, tele, and anno tools.
  simu.pl       Contains the "simu" tool.
  example.peirc Stuff for you to put in your ~/.peirc file (NOTE: don't
                make a ~/.peirc file until pei has had a chance to
                convert your ~/.eifrc into ~/.peirc).
  pei.man       Pei users manual--you can also get this by typing "help pei"
                from within pei.
  tools.man     Manual for pei tools--you can also get this by typing
                "help tools" from within pei.
  hacker.man    Information useful to hackers.
  $HOME/.peirc              Pei commands read when pei starts up.
  $HOME/.eifrc              Converted to ~/.peirc if ~/.peirc doesn't exist.
  $gamedir/start.exec       Commands executed when you first connect to a game.
  $gamedir/$gamename.dump   Database of enemy sectors (used by "stat").
  $gamedir/$gamename.ships  Database of enemy ships (used by "sstat").
  $gamedir/$gamename.planes Database of enemy planes (used by "pstat").
  $gamedir/$gamename.units  Database of enemy land units (used by "lstat").
  $gamedir/$gamename.teles  Your mailbox of Empire telegrams (used by "mail").
  $gamedir/$gamename.wires  Announcements (used by "wmail").

AUTHORS
  Drake Diedrich (harmless@empire.net)
    Wrote the original version of this program (called hpc.pl) which included
    perl integration, file redirection, piping, function mapping,
    parse mapping, readline support, shell escape, and exec.  He also wrote
    parse.pl and user.pl.

  Ken Stevens (children@empire.net)
    Added .peirc, aliases, history substitution, runfeed, interrupt handling,
    documentation, multiple game support (addgame, kill, try), multiple
    commands separated by semicolon, sub-prompting using ?,
    and variable substitution.

  Sam Tetherow (tetherow@cse.unl.edu)
    Added the following tools: sneweff, pneweff, lneweff, tele, anno,
    mail, wmail, and foreach.  Sam also completely rewrote parse.pl,
    adding many new parse maps.

EOF
  close(MORE);
}
	
1;
