#!/local/bin/perl
#
# fspcli, by Nicolai Langfeldt (janl@ifi.uio.no)
# Copyleft 1992, Nicolai Langfeldt
#
# Credits:
#   Me (janl@ifi.uio.no): writing this thingy
#   Ove Ruben R Olsen (ruben@uib.no): We wrote our clients at the same time,
#     talked a lot about them on irc and read eachothers code. It's impossible
#     to keep account of who invented what feature. Hei Ruben! :)
#     This cooperation has resulted in some creeping featurism, but the
#     shells are much better now than before... :)
#   Christer Jansson (svarten@Abacus.HGS.SE): testing
#

$version="1.1";

################################ Configuration #############################
#
# Actually, if you can stand to wait the time it takes to search the path
# (a eternety :) for these (following) progs you can leave out the path
# part, but the correct thing to do is put the path part in there.

$fspprogs = '/hom/janl/bin/sun4';    # Placement of fsp binaries
$uncompr  = '/local/bin/uncompress'; # Full path/name of uncompress
$compress = '/local/bin/compress';   # Full parh/name of compress
$ping     = '/usr/etc/ping';	     # Full path/name of ping

# Use readline for input? The readline library for perl has been posted
# on alt.sources and on comp.lang.perl. If you don't have it just
# set readline to 0. Setting it to 0 makes for faster fspcli startup too.
# Also: readline.pl don't work right on all machines, another reason to
# leave it out.

$readline=1;			     # 1: use readline, 0: don't

require '/hom/janl/lib/readline.pl' if ($readline);
				     # You'd better set this path right too

# These are only used for the batch/submit commands, so if you
# don't use them you can leave these variables alone.
$nice = '/bin/nice -15';	# If jobs need to be
	# niced to be alowed to live after you logout this should be defined,
	# if you don't want the batches niced leave it empty ("").
$at = '/usr/bin/at -sm';	# At/batch with any option to make it use sh
$batch = '/usr/bin/batch -sm';	# as shell and send e-mail no matter what.

############################################################################
# 			      Here be dragons				   #
#			    Do not venture here				   #
############################################################################
#
######################### Programmer documentation #########################
#
# These globals are used:
#   $readline	See below
#   $interactive 0 if reading commands from CMDFILE
#   $dir	The working directory on the fsp server
#   $server	The fsp server in use now, decorational purposes only
#   $servern	The nickname of current server ("" if nickname wasn't used)
#   $version	fspcli version
#   $verbose	0: quiet, 1: normal, 2: debugging
#   $usage	The "Usage:" string for flaming fumblers :-)
#   $trace	FSP_TRACE set or unset
#   $tmp	Name of the tmp directory we use
#   $fspprogs   See below
#   $uncompr	See below
#   $compress	See below
#   $ping	See below
#   $rm		See below
#   $sh		See below
#   $nice	See below
#   $at		See below
#   $batch	See below
#   $beep	Beep when file x-fers are finnished
#   $prompt	What we eval to get the prompt
#   $PG		Pager, either == $ENV{'PAGER'} or "more"
#   $ED		Editor, either == $ENV{'EDITOR'} or "vi"
#   @help	Contains the help texts
#   %hostname	Array of servers, lookup by nickname
#   %portno	Array of ports, lookup by nickname
#   %comment	Array of descriptions by nickname
#   %initdir	Initial directory on server by nickname
#   @ignoresigs Signals we want to ignore while spawned program is running
#   $batching	1 if I'm generating a script(batch) file.
#		2 if a script was generated but at/batch reported a error
#   $batchno	The batch file number for this invocation of fspcli
#   BATCH	The script we're generating if any.
#   $bf		Name of current (or last) batch file
#   %commands	Directory of commands and how to do them
#   %aliases	Directory of aliases. Commands of a alias is seaprated by "\n"
#
# These environment variables are used:
#   HOME	Where to find .fspclirc file
#   PAGER	Used for less/more/zmore. If not set more is used.
#   EDITOR	Used for message/redit. If not set vi is used.
#
# These environment variables are maintained:
#   FSP_TRACE	  (set to "yes"). Get progress info when x-fering files
#   FSP_LOCALPORT 0, unix finds local port that can be used.
#   FSP_DIR	  Same value/meaning as $dir
#   FSP_HOST	  Full name of the fsp server/host
#   FSP_PORT	  Port on the fsp server to connect to
#   FSP_TIMEOUT	  If set contains command timeout
#   FSP_PASSWORD  The password
#
################################ Initiation ################################

$sh = '/bin/sh';	# Place to find sh
$rm = '/bin/rm -f';	# No questions asked rm command

$interactive = 1;	# Good starting point, let readcmds worry about setting
			# it otherwise.
$verbose = 1;		# Verboseness level
$trace = 1;		# Dictates setting of FSP_TRACE
$beep = 0;		# Don't beep

@ignoresigs = ('INT','QUIT','PIPE'); # We ignore these signals sometimes.

$server = 0;		# no server yet.
$dir = 0;		# no cwd yet
$batching = 0;		# Nope, were not making a script.
$prompt = '$server:$dir>'; # Default prompt. Keep the tick quotes!
$batchno = 0;

# Some handy stuff:
$tmp = "/tmp/fspcli.$$";
$mtmp = "mkdir $tmp";
$ctmp = "cd $tmp";
$rtmp = "cd /;$rm -r $tmp";
$mctmp = "$mtmp;$ctmp";

$PG = $ENV{'PAGER'} || 'more';
$ED = $ENV{'EDITOR'} || 'vi';

$ENV{'FSP_TIMEOUT'} = '7';

@onoff = ('off','on'); # Used for printing 'on' and 'off' in &onoff

#
# Array of commands, and how they are to be done in perl.
#
%commands = (
# Command, Perl expression
  ('alias','&alias($args);'),
  ('batch','&batch;'),
  ('beep','$beep=&onoff($args,"beep",$beep);'),
  ('brief','$verbose=0;'),
  ('cat','&traceoff;&mysys("$fspprogs/fcatcmd $rargs");&settrace;'),
  ('cd','&cd($rargs)'),
  ('del','&bsys("$fspprogs/frmcmd $rargs");'),
  ('echo','print "$args\n";'),
  ('edit','&mysys("$mctmp;$fspprogs/fgetcmd $args;$ED $args;$fspprogs/fput $args;$rtmp");'),
  ('forget','&forgetbatch;'),
  ('get','&bsys("$fspprogs/fgetcmd $rargs");&beep;'),
  ('help','&help($args);'),
  ('hinfo','&hinfo;'),
  ('lcd','&lcd($args);'),
  ('local','$ENV{"FSP_LOCALPORT"}=$args;'),
  ('quit','&quit;'),
  ('more','&traceoff;&mysys("$fspprogs/fcatcmd $rargs | $PG");&settrace;'),
  ('lservers','&listserv;'),
  ('ls','&mysys("$fspprogs/flscmd $rargs");'),
  ('message','$args =~ s/\s+/_/;&mysys("$mctmp;$ED $args;$fspprogs/fput $args;$rtmp");'),
  ('mkdir','&bsys("$fspprogs/fmkdir $rargs");'),
  ('msg','&msg($args);'),
  ('normal','$verbose=1;'),
  ('password','&password($args);'),
  ('ping','&mysys("$ping $ENV{FSP_HOST}");'),
  ('pro','&bsys("$fspprogs/fprocmd $rargs");'),
  ('prompt','$prompt=$args;'),
  ('put','&bsys("$fspprogs/fput $args");&beep;'),
  ('pwd','print "$dir\n";'),
  ('rmdir','&bsys("$fspprogs/frmdircmd $rargs");'),
  ('server','&getservername($args);'),
  ('submit','&submitbatch($args);'),
  ('trace','$trace=&onoff($args,"trace",$trace);&settrace;'),
  ('timeout','&timeout($args);'),
  ('unalias','&unalias($args);'),
  ('ver','&ver;'),
  ('verbose','$verbose=2;'),
  ('zcat','&zcat($rargs);'),
  ('zmore','&zmore($rargs)'),
  ('zput','&zputfile($args);'),
  ('zget','&bsys("$fspprogs/fgetcmd $rargs;$uncompr $rargs");&beep;'));

#
# Array of aliases, and their 'real' counterparts.
#
%aliases = (
# Alias, real command
  ('v','ls -l'),
  ('dir','ls -l'),
  ('ll','ls -l'),
  ('redit','edit'),
  ('mget','get'),
  ('less','more'),
  ('open','server'),
  ('o','server'),
  ('zless','zmore'),
  ('rd','rmdir'),
  ('rm','del'),
  ('mput','put'),
  ('send','put'),
  ('chmod','pro'),
  ('list','lservers'),
  ('md','mkdir'),
  ('touch','msg'),
  ('to','timeout'),
  ('pw','password'),
  ('bye','quit'));

# Read list of nicknames, servers, and ports
&readserverlist(1,"$ENV{'HOME'}/.fsphosts");

# Then rc file
&readcmds("$ENV{'HOME'}/.fsprc");

if ($readline) {
  # Readline initiation
  
  &rehash; # For command completion

  # But the defaults seem to be good so that's all :)
}

# Parse arguments
&parseargs(split(/\s/,$ENV{'FSPCLI'}));
&parseargs(@ARGV);

&settrace;

# ############################## Main loop ##############################

&output(1,'print "No server set, use \"server\" command to set.\n";')
							   unless ($server);

while (1) { # We continue foever and ever, only way out is &quit
  while (1) {
    eval "\$rdlnprompt = \"$prompt\""; # Kinda ugly, expands the prompt...
    $input = &getinput($rdlnprompt);
    last unless (defined($input));
    &parse($input);
  }
  print "\n";
  &quit; # Does exit if we want to
}

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

sub parse {
  # 1. Push command to command stack.
  # 2. If empty stack exit
  # 3. Pop the stack, examine
  #      if command: do it, goto 2
  #      if simple alias: resolve it, push contents, goto 2
  #	 if compound alias: resolve it, push contents a line at a time, goto 2

  # Special case, handle separately.
  if ($_[0] =~ /^!(.*)/) {
    &mysys("$1");
    return;
  }

  @cstk = ($_[0]);

  while (1) {

    $_=pop(@cstk);
    last if (!defined($_));

    # Remove \n and blanks at start/end of command
    s/^\s+|\s+$//g;

    return unless ($cmd)=/^(\w+)/;
    $args="" unless ($args)=/^\w+\s+(.*)$/;

#    print "Command /$cmd/ Args /$args/\n";

    if (defined($aliases{$cmd})) { # Check if it's a alias
      $_ = $aliases{$cmd};
      $subst=0;
      if (/\$/) {		   # Substitute arguments if any
	s/\$\*/$args/;
	@args=split(/\s+/,$args);
	s[\$(\d)]($args[\1])g;
	$subst=1;
      }
      if (/\n/) {
	# Push on commands on the stack
        push(@cstk,reverse(split(/\n/,$_)));
      } else {
        $_.=" ".$args unless $subst;
        push(@cstk,$_);
      }
      next; # Start loop over again
    }

    if (defined($commands{$cmd})) {
      # Escape shell chars for remote use
      ($rargs=$args)=~s/([\*"'`\]\[])/\\$1/g;
      eval $commands{$cmd};
    } else {
      print "Unknown command: $cmd.\n";
    }
  }
}


#### These handle the commands if the entire command isn't in the command array

sub cd {
  # Change remote directories
  local($tmpd); # Don't want to nuke old dir before new dir is secured
  local($ndir)=@_[0];

  $ndir="/" if ($ndir eq "");
  
  &signals('IGNORE');
  chop($tmpd=`$fspprogs/fcdcmd $_[0]`);
  &signals('DEFAULT');

  if ($?) {
    print "Chdir command ($fspprogs/fcdcmd) failed with error '$!', code $?\n";
  } else {
    $ENV{'FSP_DIR'}=$dir=$tmpd;
  }
}


sub hinfo {
  if (!$servern=="") {
    print "Server nickname: $servern\n";
    print "Description: $comment{$servern}\n";
    print "Initial directory: $initdir{$servern}\n";
  }
  print "Full name of server: $ENV{'FSP_HOST'}\n";
  print "Current directory: $ENV{'FSP_DIR'}\n";
  print "Port: $ENV{'FSP_PORT'}\n";
  print "Local port: $ENV{'FSP_LOCALPORT'}\n";
}


sub msg {
  $_ = @_[0];

  s/\s+/_/;
  &bsys("$mctmp;touch $_;$fspprogs/fput $_;$rtmp");
}


sub ver {
  &mysys("$fspprogs/fver");
  &mysys("$fspprogs/fver -l");
  print "Your fsp shell is fspcli version $version\n";
}


sub zputfile {
  # Send files to remote, compressing them first
  $files=@_[0];

  &bsys("$mtmp;cp $files $tmp;$ctmp;$compress $files;$fspprogs/fput *;$rtmp");
  &beep;
}


sub lcd {
  # Change local directory

  chdir(@_[0]);
  printf("Local directory is now: %s",`pwd`);
  print BATCH "cd ",`pwd` if ($batching==1);
}


sub quit {
  # Quit, make cleanup, submit batch if any and so on
  if ($batching) {
    print "A batch file is still unsubmitted, really quit (Y/N)?";
    $_=<STDIN>;
    if (/^y|^Y/) {
      &closebatch;
      print "$bf is unsubmitted\n";
    } else {
      return;
    }
  }
  print "*poof*\n";
  exit 0;
}


sub batch {
  $bf = "/tmp/fspclibatch.$$.$batchno";
  $batchno++;
  if (!open(BATCH,">$bf")) {
    print "Could not open batchfile $bf\n";
    return;
  }
  chmod 0600, $bf; # Now the batchfiles might contain passwords
  $batching=1;
  &output(1,'print "Writing $bf\n";');
  print BATCH "#!$sh\ncd ",`pwd`;
  &hinfobatch;
  &output(2,'print "Deposited current state in batch file\n";');
}


sub hinfobatch {
  # Just call this to update any environment variables in the batch script
  # Lazy huh? This would be better if it just wrote the changes from
  # last time. But that's hardly a necesarry optimization
  if (defined($ENV{'FSP_HOST'}) && (batching==1)) {
    print BATCH "FSP_HOST=$ENV{'FSP_HOST'}\n";
    print BATCH "FSP_DIR=$ENV{'FSP_DIR'}\n";
    print BATCH "FSP_PORT=$ENV{'FSP_PORT'}\n";
    print BATCH "FSP_LOCALPORT=$ENV{'FSP_LOCALPORT'}\n";
    print BATCH "export FSP_HOST FSP_DIR FSP_PORT FSP_LOCALPORT\n";
    if ($ENV{'FSP_TIMEOUT'}) {
      print BATCH "FSP_TIMEOUT=$ENV{'FSP_TIMEOUT'};export FSP_TIMEOUT\n";
    } else {
      print BATCH "unset FSP_TIMEOUT\n"
    }
    if ($ENV{'FSP_PASSWORD'}) {
      print BATCH "FSP_PASSWORD=$ENV{'FSP_PASSWORD'};export FSP_PASSWORD\n";
    } else {
      print BATCH "unset FSP_PASSWORD\n";
    }
  }
}


sub onoff {
  $_ = $_[0];
  local($name,$curval) = @_[1,2];

  if ($_ eq "") {
    print "$name is $onoff[$curval]\n";
    return $curval;
  } elsif ((/^on$/i)||(/^1$/)) {
    return 1;
  } elsif ((/^off$/i)||(/^0$/)) {
    return 0;
  } else {
    print "Expected a 'on', 'off', '1' or '0'.\n";
    return $curval;
  }
}


sub zcat {
  &traceoff;
  &mysys("$fspprogs/fcatcmd @_[0] | $uncompr");
  &settrace;
}


sub zmore {
  &traceoff;
  &mysys("$fspprogs/fcatcmd @_[0] | $uncompr | $PG");
  &settrace;
}


sub getservername {
  # Get a new server name and set some globals accordingly, if servername
  # isn't given we return having done nothing.
  # We know two kinds of server specs, with port number, then we take server
  # name literaly, and without port number, then we use the associative
  # arrays to look the server up.

  local($nick)=@_;
  local($port,$gotserver);

  ($nick,$port)=split(/\s+/,$nick);
  if ($port) { # In that case there were two arguments
    $server=$nick;
    $dir="/";
    $gotserver=1;
    $servern="";
  } elsif ($hostname{$nick}) {
    $servern=$nick;
    $server=$hostname{$nick};
    $port=$portno{$nick};
    $dir=$initdir{$nick};
    $gotserver=1;
  } else {
    print "No such server: \"$nick\". \"lservers\" will print a list of known servers\n";
    $gotserver=0;
    $servern="";
  }
  if ($gotserver) {
    &output(2,'print "Server is $server, port $port\n";');
    $ENV{'FSP_HOST'}=$server;
    $ENV{'FSP_PORT'}=$port;
    $ENV{'FSP_LOCALPORT'}="0";
    &hinfobatch;
    $ENV{'FSP_DIR'}="/";
    &cd($dir);
    
    &output(2,'&mysys("$fspprogs/fver");');
  }
}


sub help {
  # Show help texts.
  local($cmd) = @_;
  local($dummy);

  $cmd='cmds' if ($cmd !~ /\w|\!/);
  $ocmd=$cmd;

  # If alias try to resolve it
  while (1) {
    $_=$cmd;

    ($cmd)=/^\s*(\w+)/;

    if (defined($aliases{$cmd})) {  # Check if it's a alias
      $alias=$aliases{$cmd};
      if ($alias =~ /\n/) {
        print "Compound alias:\n";
	&alias($cmd);
	return;
      }
      print "\'$cmd\' is a alias for \'$alias\'\n";
      $cmd=$alias;
    } else {
      last; # No more levels to look thru.
    }
  }

  # Initiate help array if necesary
  if (!defined(@help)) { push (@help,$_) while (<DATA>); }
  
  # Print help
  foreach (grep(/^$cmd -/,@help)) {
    /^$cmd -(.*)/;
    print "$1\n";
  }

  # Print any aliases for the command
  $any=0;
  foreach (sort(keys %aliases)) {
    next if ($aliases{$_} =~ /\n/); # Skip compound aliases
    if ($aliases{$_} =~ /^$cmd$|^$cmd\s/) {
      if (!$any) {
        print "Aliases for '$cmd':";
	$any=1;
      }
      print " $_";
    }
  }
  print "\n" if ($any);
}


sub alias {
  # Process the alias command

  if ($_[0] eq '') {  		      # No arguments, print all aliases
    foreach $alias (sort(keys %aliases)) {
      if ($aliases{$alias} =~ /\n/) {
        print "$alias {\011",join("\n\011",split(/\n/,$aliases{$alias}))," }\n";
      } else {
        print "$alias\011$aliases{$alias}\n";
      }
    }
  } elsif ($_[0] =~ /^\w+$/) { 	      # One word argument, print def. of alias
    if (!defined($aliases{$_[0]})) {
      print "$_[0] is undefined\n";
    } elsif ($aliases{$_[0]} =~ /\n/) {
      print "$_[0] {\011",join("\n\011",split(/\n/,$aliases{$_[0]}))," }\n";
    } else {
      print "$_[0]\011$aliases{$_[0]}\n";
    }
  } elsif ($_[0] =~ /^(\w+)\s+\{$/) { # Block alias
    if ((defined($aliases{$1})) || (defined($commands{$1}))) {
      print "$1 is already a command or alias\n";
    } else {
      $alias = $1;
      $aliases{$alias}=&inputalias;
      &output(2,'print "$1 defined\n";');
      &rehash;
    }
  } elsif ($_[0] =~ /^(\w+)\s+(.*)$/) { # Simple alias
    if ((defined($aliases{$1})) || (defined($commands{$1}))) {
      print "$1 is already a command or alias\n";
    } else {
      $aliases{$1}=$2;
      &output(2,'print "$1 defined\n";');
      &rehash;
    }
  } else {
    print "I didn't quite understand that alias command\n";
  }
}


sub inputalias {
# Input a alias, whatever the prefered input method is
#  output(1,'print "Type alias, end with \'}\' on a line by itself.\n";');
  while (1) {
    $nl = &getinput('alias:');
    $nl =~ s/^\s+|\s+$//g;
    last if ($nl eq "}");
    $def .= $nl . "\n";
  }
  chop $def;
  return $def;
}


sub unalias {
  if ($_[0] eq '') {
    print "I would like an argument please!\n";
    return;
  }
  foreach $alias (split(/\s+/,$_[0])) {
    if (defined($aliases{$alias})) {
      delete $aliases{$alias};
      &output(2,'print "Forgot: $alias\n";');
    } else {
      print "No such alias: $alias\n";
    }
  }
  &rehash;
}


sub listserv {
  # Print list of servers
  local($listform)="%15s  %-60s\n";
  local($server,$i);
  
  $i=0;
  while (($nick,$server)=each %hostname) {
    if (!$i) {
      printf($listform,"Server","Description");
      printf($listform,"------","-----------");
    }
    printf($listform,$nick,$comment{$nick});
    $i++;
  }
  print "Hmm, seems I know of no servers, do \"help server\" on how to get to
one anyway, and \"help fspcli\" on how to tell me of servers.\n" unless ($i);
}

###

sub readserverlist {
  # Read serverlist. If called more than once it's
  # additive since the hosts are stored in associative arrays.
  local($complain,$file)=@_;
  local($nick,$server,$port,$dir,$comment);

  if (!open(SERVERS,$file)) {
    print "Can't open $file.\nDo \"help fspcli\" for help on how to make it\n"
      if ($complain);
  } else {
    while (<SERVERS>) {
      chop;
      s/#.*//; 			# Remove comments
      next if (/^\s*$/);	# Ignore empty lines
      if (/^\s*\+\s*(.*)/) {
	next if ($nick eq 'MagicIgnoreNick');
        $comment{$nick}.=" ".$1;
	next;
      }
      die "Error in $file on line $.\n"
        unless ($server,$port,$nick,$dir,$comment)=
	                   /^\s*(\S+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(.*)\s*$/;
      if ($comment =~ /\*/) {
        # If the comment starts with a '*' we want to ignore
	$nick='MagicIgnoreNick';
	next;
      }
      $hostname{$nick}=$server;
      $portno{$nick}=$port;
      $comment{$nick}=$comment;
      $initdir{$nick}=$dir;
    }
    close(SERVERS);
  }
}


sub output {
  # Do command $command if we are at right verboseness level

  local($level,$command)=@_;
  
  eval $command if ($level<=$verbose);
}


sub parseargs {
  # Flag + server parsing

  # Have usage here where it's parsed :-)
  $usage = "Usage: fspcli [-q|-d|-v n|-nt|-t|-b|-nb|-p|-help] [server|full-server-name <port>]";

  while ($#_ >= 0) {
    $_ = shift;
    if (/^-q$/) { $verbose = 0; next; }
    if (/^-d$/) { $verbose = 2; next; }
    if (/^-v$/) { $verbose = shift; next; }
    if (/^-nt$/) { $trace = 0; next; }
    if (/^-t$/) { $trace = 1; next; }
    if (/^-b$/) { $beep = 1; next; }
    if (/^-nb$/) { $beep = 0; next; }
    if (/^-p$/) { $prompt = shift; $prompt =~ s/_/ /g; next; }
    if (/^-help$/) { &arghelp; } # arghelp terninates
    if (/^-.*/) { print "$usage\n"; exit 1;}
    &getservername(join(" ",($_,@_)));
    shift;
  }
}


sub rehash {
  # (Re)Initiate readline for command/alias completion
  if ($readline) {
    &readline'rl_basic_commands((keys %commands,keys %aliases));
  }
}


sub signals {
  local($action)=@_;

  foreach (@ignoresigs) { $SIG{$_} = $action; }
}


sub traceoff {
  # Set tracing off
  delete $ENV{'FSP_TRACE'};
}


sub traceon {
  # Set tracing off
  $ENV{'FSP_TRACE'}="1";
}


sub settrace {
  # Set traceing to value according to $trace
  if ($trace) { &traceon; } else { &traceoff;  }
}


sub mysys {
  # My own system call, with error checking
  local($cmd)=@_;
  local($err);

#  print "system($cmd);\n";
  &signals('IGNORE');
  $err=system($cmd);
  &signals('DEFAULT');

  print "Error $err doing system(\"$cmd\")\n" if (($err) && ($err!=768));
}


sub arghelp {
  # Help for arguments

  print "$usage

  -q		Quiet, almost no output (verboseness=0)
  -d		Debug output (verboseness=2)
  -v n		Verboseness=n, default is 1, normal
  -nt		No trace. When tracing the put/get cmd's print how many
		  Kb have been xfered until now
  -t		Trace on, this is default
  -b		Beep when file x-fers are finnished.
  -nb		Don't beep.
  -p prompt	Set the fspcli prompt. See notes in \"help prompt\".
  -help		Display this help

You can give default options with the environment varaible FSPCLI, the
flags supplied on command line takes precedence over the environment
varaible.

fspcli has builtin help; use the \"help\" command.
First time you try fspcli do \"help fspcli\".
";
  exit 0;
}


sub beep {
  print "\007" if (($beep) && (!$batching));
}


sub bsys {
  # Submit "batchable" system(3) calls here, they will be written
  # to batch or executed

  if ($batching) {
    @cmds=split(/;/,$_[0]);
    print "*Added to batch:\n* ",join("\n* ",@cmds),"\n" if ($verbose);
    print BATCH join("\n",@cmds),"\n";
  } else {
    &mysys(@_);
  }
}


sub closebatch {
  # Close the batch file
  if ($batching=1) {
    print BATCH "echo fspcli generated batchget finished.\n";
    print BATCH "echo 'Don\'t forget to do rm -f $bf'\n";
    close(BATCH);
    sleep(2); # We've had some problems with "disapeared" files...
  }
}

sub submitbatch {

  if (!$batching) {
    print "But I have no script to subimt! Try 'help batch'\n";
    return;
  }

  $when=&lowercase($_[0]);

  $when="now + 1 minutes" if ($when eq "now");

  &closebatch;

  &signals('IGNORE');

  # Prime the environment for at(1)/batch(1)
  &traceoff; # No tracing, pointless for unattended, bothersome if e-mail too
  $ENV{'SHELL'}=$sh;	 # The shell we want it to use

  if ($when eq "manual") {
    close(BATCH);
    print "The batchfile name is $bf\n";
    $batching=0;
    $?=0;
  } elsif ($when eq "later") {
    open(PIPE,"| $batch");
    print PIPE "$nice $bf\n";
    close(PIPE);
  } else {
    open(PIPE,"| $at $when");
    print PIPE "$nice $bf\n";
    close(PIPE);
  }

  &signals('DEFAULT');

  if ($?) {
    print "Error submitting batch. Try again.\n";
    $batching=2;
  } else {
    $batching=0;
  }
}

sub forgetbatch {
  if (!$batching) {
    &output(1,'print "But I have no script to forget! Try \'help batch\'\n";');
    return;
  }
  close(BATCH) if ($batching==1);
  unlink $bf;
  $batching=0;
  &output(1,'print "Batch forgotten!\n";');
}


sub timeout {
  # Set or unset timeout
  if ($_[0] eq '') {
    print "timeout is ";
    if ($ENV{'FSP_TIMEOUT'}) {
      print "$ENV{'FSP_TIMEOUT'} seconds\n";
    } else {
      print "off\n";
    }
  } elsif (($_[0]=~/^off$/i)||($_[0] eq "0")) {
    delete $ENV{'FSP_TIMEOUT'}
  } elsif ($_[0]=~/^\d+$/) {
    $ENV{'FSP_TIMEOUT'}=$_[0];
  } else {
    print "I expected 'off' or a number of seconds for the timeout to occur after\n";
  }
  &hinfobatch;
}


sub password {
  # Set or unset password
  if ($_[0] eq '') {
    delete $ENV{'FSP_PASSWORD'};
  } else {
    $ENV{'FSP_PASSWORD'}=$_[0];
  }
  &hinfobatch;
}


sub lowercase {
  $_[0] =~ tr/[A-Z]/[a-z]/;
  return $_[0];
}


sub readcmds {
  # Process input from
  local($verb)=$verbose;
  if (!open(CMDFILE,$_[0])) {
    print "Could not open $_[0]\n";
    return;
  }
  $interactive=0;
  $verbose=0;
  while (1) {
    $input = &getinput;
    last unless (defined($input));
    &parse($input);
  }
  $interactive=1;
  $verbose=$verb;
  close(CMDFILE);
}


sub getinput {
  # Read from the selected input source, stripping comments
  if ($interactive) {
    if ($readline) {
      $inp = &readline'readline($_[0]);
      return undef unless (defined($inp));
    } else {
      print $_[0];
      return undef unless ($inp=<STDIN>);
      chop($inp);
    }
  } else {
    return undef unless ($inp = <CMDFILE>);
    chop($inp);
  }
  $inp =~ s/#.*//;  # Get rid of comments
  return $inp;
}

__END__
cmds -Local commands:
cmds -	!		alias		beep		brief
cmds -	echo		help		lcd		lservers
cmds -	local		normal		prompt		quit
cmds -	verbose
cmds -
cmds -File transfer/remote commands:
cmds -	cat		cd		del		edit
cmds -	get		hinfo		ls		message
cmds -	mkdir		more		msg		password
cmds -	ping		pro		put		pwd
cmds -	rmdir		server		timeout		trace
cmds -	ver		zcat		zget		zmore
cmds -	zput
cmds -  
cmds -Other topics:
cmds -	fspcli		files
cmds -
cmds -Use "help <command>" to get help for a specific command.
cmds -You can interupt cd/ls/get/put/cat/rm/mkdir/rmdir/pro/msg by pressing
cmds -^C twice. Like if they use more time than you like.
timeout -timeout <verdi>
timeout -  Sets the command timeout. By default it is 7 retries.
timeout -  Timeouts only occur when doing 'cd' or 'ls' and so on, i.e.
timeout -  not _during_ filetransfers (so it's quite safe ;)
alias -alias
alias -  Lists all defined aliases.
alias -alias <alias>
alias -  Shows definition of the alias
alias -alias <alias> <definition>
alias -  Defines a simple one command alias. Example: 'alias ll ls -l'
alias -alias <alias> {
alias -  Define a compound alias. The user is prompted for the definition
alias -  of the alias, one command on each line. The alias def. is ended by
alias -  inputing a "}" alone on a line. Fspcli expands references to the
alias -  arguments given. $* is all the arguments, $0 is the first
alias -  argument, $1 the second and so on until $9.
alias -See also 'help unalias'.
alias -NOTE: fspcli makes no attempt to detect endless recursion
unalias -unalias <alias> [<alias> ...]
unalias -  Undefines (a) previously defined alias(es)
message -message <filename>
message -  Used to make a file containing something (like a message) and
message -  send it to the server: Your favourite editor is invoked to edit
message -  the given file, it is sent to the server, and then deleted from
message -  your machine. Any spaces in the filename are replaced by
message -  underline "_".
edit -edit <filename>
edit -  Used to edit a file on the remote machine. It gets the file, starts
edit -  your favourite editor, and then sends the file back. This requires
edit -  that you have delete permission in the remote directorty.
lservers -lservers
lservers -  Print a list of known servers.
verbose -verbose
verbose -  A lot of messages will be printed.
brief -brief
brief -  Almost no messages will be printed.
normal -normal
normal -  Some messages will be printed.
ping -ping
ping -  ping the fsp host. Even if the fsp host is alive the fsp server
ping -  there might be down.
! -!<shell command(s)>
! -  Shell escape.
lcd -lcd [localdir]
lcd -  Local cd, sets directory where files are gotten to or put from.
lcd -  lcd with no argument changes local dir to $HOME.
local -local <portno>
local -  Useless command that sets the port number to use localy, set it to
local -  0 and unix will find a port you can use. fspcli sets it to 0 all
local -  by itself :-) The local port number is reset to 0 by the server/open
local -  command.
help -help [command]
help -  8->
quit -quit
quit -  Quit fspcli
server -server <server>
server -  Change/set server according to nickname.
server -server <full server name> <portno>
server -  This form allows you to give full server name and port
server -  so the server don't have to be in the server lists.
server -See also 'help lservers'
cd -cd [remotedir]
cd -  Change directory on server. If no argument is given "/" is assumed.
ls -ls [flags] [filespecs]
ls -  Do ls on server. You can do stuff like 'ls -lR >ls-lR' if you like
ls -  (but getting a ready made index is a lot better).
get -get <filespec>
get -  Get files from server. Wildcards ('*' and family) is expanded on
get -  the server.
put -put <filespec>
put -  Put files on server. Wildcards ('*' and family) is expanded localy.
cat -cat <filespec>
cat -  cat a file from server onto screen or onto file "cat foo >bar".
zcat -zcat <file>
zcat -  Like cat except that the file is uncompressed before it's shown.
zcat -  Should only be used for one file at a time.
more -more <file>
more -  View file from server with help from your favourite pager.
more -  If no PAGER environment variable is set 'more' is used.
more -  A <filespec> can be used in place of <file>, but all files
more -  will be considered as the same file by more.
zmore -zmore <file>
zmore -  Like more except that the file is uncompressed before it's shown.
zmore -  Should only be used for one file at a time.
rmdir -rmdir <filespec>
rmdir -  Remove directory(s) (from server).
mkdir -mkdir <filespec>
mkdir -  Make directory on server, globbing is done localy since it's
mkdir -  meaningless to do it remotely and local globing allows easy(er)
mkdir -  directory tree duplication. Set permissions for the directory
mkdir -  with "pro" command afterwards.
pro -pro [+c|-c|+d|-d] <dir> 
pro -  get or set permissions for directories.
pro -    +c Give others permission to create new items.
pro -    -c Deny others permission to create new items.
pro -    +d Give others permission to delete old items.
pro -    -d Deny others permission to delete old items.
msg -msg <message>
msg -  Make a empty file with given name on server. This is much better
msg -  than doing mkdir to send messages. Any spaces in the message are
msg -  replaced by underlines "_". Se 'message' if you want to send a
msg -  long message.
del -del <filespec>
del -  Delete a file from server
trace -trace [on|off]
trace -  When trace is on fsp prints how many Kb of the file being
trace -  transfered that has actually been transfered. When trace is
trace -  off it dosn't say anything. With no argument current trace
trace -  status is printed.
files -Server list
files -  In you home directory you can have a file named ~/.fsphosts. In it
files -  you can store to details on the fsp servers you know. This is the
files -  format:
files -     "full-server-name port-no nickname directory description"
files -  All fields are required. Wuarchive can be described thusly:
files -     "wuarchive.wustl.edu     21      wu       / Washington univ."
files -  Comments are started with "#" and can appear alone on lines or on
files -  ends of lines.
files -
files -Startup commands
files -  Fspcli reads startup commands from ~/.fsprc. The commands should
files -  appear exactly as you type them inside fspcli. All lines can be
files -  ended with a comment, coments start with a '#' character.
fspcli -Invoking fspcli
fspcli -  fspcli may be given commandline arguments. For a list of those
fspcli -  do 'fspcli -help' at a unix prompt
fspcli -
fspcli -Environment variable
fspcli -  The commandline arguments may also be put in a environment variable
fspcli -  named FSPCLI. Example: "setenv FSPCLI -q". Since this variable
fspcli -  is parsed just like if it was commandline arguments you can supply
fspcli -  a default server with it too: "setenv FSPCLI -q wu".
fspcli -  Commandline options overrule whatever you have in FSPCLI
fspcli -
fspcli -Fsp client program arguments.
fspcli -  The arguments to cat,cd,del,get,ls,mkdir,pro,put and rmdir are passed
fspcli -  (as good as) untouched. This means that you can give options to the
fspcli -  corresponding fsp clients. So you can do 'get -f fsphosts'.
fspcli -
fspcli -Fspcli reads two files on startup, see 'help files' for more
fspcli -information
ver -ver
ver -  Print version of fsp software here and on server
beep -beep [on|off]
beep -  Set on if fspcli should beep when file xfers are finnished. Handy
beep -  if you use screen, or have loads of windows, or are looking the
beep -  other way while xfering. If this is handy then the 'batch' command
beep -  might be even more handy.
pwd -pwd
pwd -  Echos your current working directory on the server. Handy if you don't
pwd -  have it in the fspcli prompt (see 'help prompt')
echo -echo <stuff>
echo -  Prints <stuff>. Might be handy for aliases (see 'help alias') and
echo -  .fsprc (see 'help files').
hinfo -hinfo
hinfo -  Print all known information about the current server
prompt - prompt <prompt>
prompt -   Configure the fspcli prompt. You can put any text and these
prompt -   variables into the prompt:
prompt -     $dir    pwd on server
prompt -     $server full name of server
prompt -   The default prompt is "$server:$dir>".
prompt -   The prompt can also be set from the commandline/environment,
prompt -   when set thus the whole prompt string needs to be in one piece,
prompt -   this can be done by replacing any spaces " " with "_", fspcli
prompt -   replaces all underscores "_" are with space(es) " ".
batch -batch
batch -  Start making a batchfile. This can be used so you dont have to
batch -  watch fsp get the files, they can be gotten automaticaly later.
batch -  After a batch command is given all gets and puts (and their derivates)
batch -  are captured to a file. The effects of the cd, lcd and server commands
batch -  are also saved. This means that you can connect to servers, browse
batch -  around and issue cd/lcd/get/put commands and the actual transfers
batch -  will be done later.
batch -  
batch -  Se also 'help submit' and 'help forget'.
submit -submit <later|manual>
submit -submit <time> [date]
submit -  This tells fspcli when the acumulated transfer commands should be
submit -  done. 'submit later' gives the script to batch(1) for later persual.
submit -  (see the batch(1) man page). 'submit manual' just closes the batch
submit -  file and reminds you what it's name is so you can do whatever you
submit -  want with it.
submit -
submit -  The last form sends the job to at(1) and you can specify any
submit -  arguments at(1) accepts (not only the ones mentioned above).
submit -  "submit 05:00" will do the transfer next time 05:00 comes around.
submit -  "submit 02:30 friday", "submit 00:00 jan 24". In addition you can
submit -  do "submit now" (which is translated to "now + 1 minutes" due to
submit -  a at(1) quirk)
forget -forget
forget -  Closes and deletes the batchfile.
password -password [password]
password -  Sets your password. The password is not obscured. But you can
password -  always clear the screen afterwards. If you enter passwords
password -  in your .fsprc file then the file should not be world readable.
