[comp.lang.perl] A FTP client

clipper@csd.uwo.ca (Khun Yee Fung) (05/01/91)

There is the whole ftp client I wrote last year. It works for SUNs.
You might have to change the domain name server address. You don't
have to worry about that if you have the resolver library builtin. I
also include all the .h .pl and .ph files it uses. Be warned they are
for Suns.

If it is useful, good. If not, oh well. Randal can obviously write a
much better one. If only I had chat2 at that time...

Here it goes,

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	nftp.dir
# This archive created: Tue Apr 30 19:32:38 1991
export PATH; PATH=/bin:$PATH
if test ! -d 'nftp.dir'
then
	mkdir 'nftp.dir'
fi
cd 'nftp.dir'
if test -f 'nftp'
then
	echo shar: will not over-write existing file "'nftp'"
else
cat << \SHAR_EOF > 'nftp'
#!/usr2/new/bin/perl 
# -*-perl-*-
# A simple ftp client in perl.
# Copyright 1990 Khun Yee Fung (clipper@csd.uwo.ca)
# You can modify, distribute, duplicate or do whatever to this file
# provided you do not delete the copyright notice and do not sell this
# program for profit. No warranty whatsoever.
# $Id: nftp,v 1.21 90/07/26 16:39:19 clipper Exp $
# $Source: /u3/thesis/clipper/pl/RCS/nftp,v $
eval "exec perl -S $0 $*"
	if $running_under_some_shell;

# Return code value for the reply from the server
$DATA = 1;
$SUCCESS = 2;
$CONTINUE = 3;
$ERROR = 4;
$FAIL = 5;

# the smaller verbose is, the less printing this program will do.
# verbose == 4 : everything is shown
# verbose == 3 : hash marks are not shown
# verbose == 2 : commands are not shown
# verbose == 1 : reply codes are not shown
# verbose == 0 : the prompt is not printed.
# verbose == -1: not even the error messages are shown.
if ($ENV{'TERM'} eq 'emacs') {
    $verbose = 3;
}
else {
    $verbose = 4;
}

# flag to indicate whether \r should be rid of
$NOCR = 1;

# the anonymous ftp list compiled by odin@pilot.njin.net
$ftplist = "/usr2/new/lib/ftp.list.Z";

# subroutine associate array for different commands of ftp
%func = ('lmkdir', 'domkdir', 'lcd', 'dochdir', 'quit', 'doquit',
	 'open', 'connect', 'back', 'background', 'rm', 'rm',
	 'mput', 'mput', 'ls', 'list', 'cd', 'cd', 'more', 'more',
	 'get', 'get', 'put', 'put', 'verb', 'verbose',
	 'reget', 'restoffile', 'mget', 'mget', 'login', 'login',
	 'cdup', 'simple', 'close', 'close', 'rename', 'rename', 
	 'del', 'simple1', 'rmdir', 'simple1', 'mkdir', 'simple1',
	 'pwd', 'simple', 'syst', 'simple', 'status', 'simple',
	 'help', 'dohelp', 'noop', 'simple', 'binary', 'type', 'ascii', 'type',
	 'tenex', 'type', 'rhelp', 'dorhelp',
	 'history', 'dohistory', '!', 'doshell', '%', 'dohis',
#	 'set', 'doset', 'unset', 'dounset',
#	 'alias', 'doalias', 'unalias', 'dounalias',
	 );

# The actual code word understood by ftpd
%words = ('nlst', 'NLST', 'ls', 'LIST', 'cd', 'CWD',
  'get', 'RETR', 'put', 'STOR', 'cdup', 'CDUP',
  'close', 'QUIT', 'del', 'DELE', 'rmdir', 'RMD', 'type', 'TYPE',
  'mkdir', 'MKD', 'pwd', 'PWD', 'syst', 'SYST', 'status', 'STAT',
  'help', 'HELP', 'noop', 'NOOP', 'binary', 'I', 'ascii', 'A',
  'tenex', 'L');

require 'sys/socket.h';
require 'sys/ioctl.pl';
require 'telnet.h';
require 'pathname.pl';
require 'getopts.pl';
require 'cbreak2.pl';
require 'resolver.pl';
require 'sys/time.h';
require 'sys/errno.ph';

# -h hostname   -- specify a hostname on the command line
# -a              -- do anonymous login automatically
do Getopts('h:a');

# flag indicating whether this is a background job (started by the back
# command)
$background = 0;

# other than options, no filenames, must be interactive.
if ($#ARGV < $[) {
  $interactive = 1;
}
$SIG{'PIPE'} = 'Sigpipe';

# get the file name of this ftp program
@prog = split("/", $0);

$sockaddr = 'S n a4 C8';
$connecting = 0;

# initialize nice little prompt with working directory name
if ($interactive) {
  $pwd = $ENV{'PWD'};
  @t = localtime(time);
  $prompt = $pwd;
  $prompt =~ s|$ENV{'HOME'}|~|;
  $prompt .= sprintf ("[%02d:%02d]", $t[2], $t[1]);
  print "nftp Version 1.0 February 21, 1991\n";
  print "Comments to clipper@csd.uwo.ca\n\n";
}

if (defined($opt_h)) {
  do connect($opt_h);  ## connect the host specified on the command line
}
if (defined($opt_v) && ($opt_v =~ /^[01234]$/ || $opt_v == -1)) {
  $verbose = $opt_v;
  print "Verbose level is: $verbose\n";
}

# Help messges
%help = (
"help", 
"  Nftp is a ftp client. To get help messages for a command, type
  help command. Commands available in nftp are:

  !         %         ascii     back      binary    cd        cdup
  close     del       get       help      history   lcd       lmkdir
  login     ls        mget      mkdir     more      mput      noop
  open      put       pwd       quit      reget     rename    rhelp
  rm        rmdir     status    syst      tenex     verb

  Newer commands are: [nothing yet]
",

'!', "
! command

Execute a UNIX command. '! ls' lists the current directory. Notice the 
space after the character '!'. This is different from '!' in csh, for 
example.
",
'%', "
% history-entry-number

Run the history entry number again. % 2 executes the entry number 2.
",
'ascii', "
ascii

Set the type of file transfer to be ASCII
",
'back', "
back [log-file]

This command asks for commands to be executed in the background. Then nftp 
will disconnect its own terminal and runs in the background. The
optional log file will contain the output of nftp in the backgorund
session. The default file name for the log file is /tmp/nftp$$.back
where $$ is the process number of the current nftp process.
",
'binary', "
binary

Set the file type to be binary
",
'cd', "
cd [directory]

Change the current directory on the remote machine.
",
'cdup', "
cdup

Goes up a level in a tree-structured file system.
",
'close', "
close

Close ftp connection to the remote machine.
",
'del', "
del file

Delete the file from the remote machine.
",
'get', "
get remote-file [local-file]

Get the remote file and optionally rename it to the local file name
provided.
",
'history', "
history

Returns the list of commands executed in the history array.
",
'lcd', "
lcd [dir]

Change the local directory.
",
'lmkdir', "
lmkdir dir

Make a local directory.
",
'login', "
login

Login as a user.
",
'ls', "
ls [options] [file]

List the current directory of the remote machine. The syntax depends
on the machine type of the remote machine.
",
'mget', "
mget regexp

Get multiple files from remote machine using regular expression.
Notice the regular expression syntax is that of Unix.
",
'mkdir', "
mkdir dir

Create a new directory on the remote machine.
",
'more', "
more file

Print the content of a file on the current directory of the remote
mahcine on the screen.
",
'mput', "
mput regexp

Putting multiple files to remote machine using Unix regular expression
syntax.
",
'noop', "
noop

No-op.
",
'open', "
open machine

Connect to a new remote machine.
",
'put', "
put localfile [remotefile]

Putting localfile to remote machine using a optional remote file name.
",
'pwd', "
pwd

Print the current directory of the remote machine.
",
'quit', "
quit

Close the connection and exit nftp.
",
'reget', "
reget filename

Starting from the last byte of the file in the current local
directory, get the rest of the file from the remote machine.
",
'rename', "
rename original new

Rename a file on the remote machine.
",
'rhelp', "
rhelp [text]

Ask for help from the remote machine.
",
'rm', "
rm file

Remove a local file.
",
'rmdir', "
rmdir dir

Remove a directory from the remote machine.
",
'status', "
status

Return the status of the remote machine.
",
'syst', "
syst

Print the remote machine type.
",
'tenex', "
tenex

Set file type to tenex, whatever that is.
",
'verb', "
verb level

Set the verbose level of nftp to level. the verbose levels of nftp
are:

the smaller the value of verbose is, the less printing nftp will do.
 verbose == 4 : everything is shown
 verbose == 3 : hash marks are not shown
 verbose == 2 : commands are not shown
 verbose == 1 : reply codes are not shown
 verbose == 0 : the prompt is not printed.
 verbose == -1: not even the error messages are shown.
");

# number of command lines remembered
$histnum = 20;

if ($interactive) {
    $SIG{'INT'} = 'again';
}
while ($in = &getinput(":$prompt> ")) {
  chop($in);
  push(@history, $in);
  if ($#history > $histnum) {
      shift(@history);
  }
  $in =~ s/\s+/ /g; ## no useless spaces
  @command = split(' ', $in);
  next if ($#command < $[); ## no command, next line

  $command_name = $command[0];
  $command_name =~ y/A-Z/a-z/;
  shift(@command);
  if ($func{$command_name} ne "") {
    $func = $func{$command_name};
    do $func(@command);
  }
  elsif ($interactive && $verbose >= 0) {
    print "Unknown command: Passed directly to the server (y/n)? ";
    $answer = <STDIN>;
    if ($answer =~ /^[yY]/) {
      do literal(@command);
    }
  }
  elsif ($verbose >= 0) {
    print "Ignored command: $command_name @command\n";
  }
}
continue {
  if ($interactive){
    @t = localtime(time);
    $prompt = $pwd;
    $prompt =~ s|$ENV{'HOME'}|~|;
    $prompt .= sprintf ("[%02d:%02d]", $t[2], $t[1]);
  }
}
do doquit();

## End of main program

# get the reply, parse it and return a nice code
sub getreply {
  local($code);
  local($answer);

  $answer = <SERVER> || ((&printn("Lost connection, getreply\n")), close SERVER,
    $connecting = 0, return $FAIL);
  $answer =~ s/\r//g;
  local($reply) = $answer;
  while ($reply ne "") {
    if ($reply =~ /^\d\d\d\-/) { ## Oh no, multi-line reply
      &print1 ($reply);
      while ($reply = <SERVER>) {
        $reply =~ s/\r//g;
        $answer = $reply;
        if ($reply =~ /^\d\d\d[^-]/) { last; }
        &print1($reply);
      }
    }

    ($reply =~ /^1/) && ($code = $DATA, last);
    ($reply =~ /^2/) && ($code = $SUCCESS, last);
    ($reply =~ /^3/) && ($code = $CONTINUE, last);
    ($reply =~ /^4/) && ($code = $ERROR, last);
    ($reply =~ /^5/) && ($code = $FAIL, last);
    ($reply =~ /^&IAC&WILL(.)/) && ((print SERVER "&IAC&WONT$1"), $reply = $');
    ($reply =~ /^&IAC&DO(.)/) && ((print SERVER "&IAC&DONT$1"), $reply = $');
    $answer = "ERROR: $answer"; $code = $ERROR;
  }
  &print1 ($answer);
  $code;
}

# The simplest commands, just the command name
sub simple {
  if (!$connecting) {
    &printn ("Not connected. Connect first\n");
    return;
  }
  &print2 ("    $words{$command_name}\n");
  print SERVER $words{$command_name}, "\r\n";
  $code = &getreply();
}

# The next simplest commands, with one and only one command
sub simple1 {
  if (!$connecting) {
    &printn ("Not connected. Connect first\n");
    return;
  }
  if ($#_ > $[) {
    &printn ("$prog: trailing garbage ignored\n");
  }
  elsif ($#_ < $[) {
    &printn ("Need one argument\n");
    return;
  }
  &print2 ("    $words{$command_name} $_[0]\n");
  print SERVER $words{$command_name}, " $_[0]\r\n";
  $code = &getreply();
}

# for local help.
sub dohelp {
    if ($#_ < $[ || $#_ > $[) {
	print $help{'help'};
    }
    else {
	print $help{$_[0]};
    }
    print "\n";
}

# for remote help command, just pass everything to the remote site
sub dorhelp {
  local($command) = $words{$command_name};
  if (!$connecting) {
    &printn ("Not connected. Connect first\n");
    return;
  }
  for ($index = 0; $index <= $#_; $index++){
    $command .= " " . $_[$index];
  }
  &print2 ("    $command\n");
  print SERVER $command, "\r\n";
  $code = &getreply();
}

# pass everything as typed to the remote site
sub literal {
  local($command) = $command_name;
  if (!$connecting) {
    &printn ("Not connected. Connect first\n");
    return;
  }
  for ($index = 0; $index <= $#_; $index++){
    $command .= " " . $_[$index];
  }
  &print2 ("    $command\n");
  print SERVER $command, "\r\n";
  $code = &getreply();
}

# change working directory. just as Unix cd
sub cd {
  local($command) = $words{$command_name};
  if (!$connecting) {
    &printn ("Not connected. Connect first\n");
    return;
  }
  if ($#_ > $[) {
    &printn ("trailing garbage ignored\n");
    $#_ = $[;
  }
  for ($index = 0; $index <= $#_; $index++){
    $command .= " " . $_[$index];
  }
  &print2 ("    $command\n");
  print SERVER $command, "\r\n";
  $code = &getreply();
}

# Get a file from remote site. The first argument is the remote filename
# If the second argument is absent, it is assumed to be the same as the
# first argument. The second argument is the local filename
sub get {
  if (!$connecting) {
    &printn ("Not connected. Connect first\n");
    return;
  }
  if ($#_ < $[) {
    &printn ("Need at least one ardument\n");
    return;
  }
  local($rfilename) = $_[0];
  local($lfilename);
  local($code);
  local($buf);

  if ($#_ == $[) {
    $lfilename = ">$rfilename";
  }
  else {
    $lfilename = ">$_[1]";
  }
  open(FILE, $lfilename) || ((&printn ("open:$!\n")), return);
  $code = &setup();
  if ($code != 2) { return; } # &setup reply code is not 2, something is wrong
  &print2 ("    RETR \'$rfilename\'\n");
  print SERVER "RETR $rfilename\r\n";

  $code = &getreply();
  if ($code != 1) { return; }

  ($addr = accept(DATA1, DATA)) || ((&printn ("accept:$!\n")), return);
  select(FILE); $| = 1; select(STDOUT); $| = 1;

  if ($interactive) {
      do init_hash();
  }
  $buf = '';
  while (!$inter && (($len = read(DATA1, $buf, 1000)) > 0 || $! == &EINTR)) {
    $buf =~ s/\r//g if ($NOCR);
    $bytes += $len;
    print FILE $buf;
    $buf = '';
  }
  $SIG{'INT'} = 'again';
  if ($interactive) {
      do end_hash();
  }
  close FILE;
  close DATA1;
  $code = &getreply();
}

sub more {
  if (!$connecting) {
    &printn ("Not connected. Connect first\n");
    return;
  }
  if ($#_ < $[) {
    &printn ("Need at least one ardument\n");
    return;
  }
  local($rfilename) = $_[0];
  local($code);
  local($buf);

  local($code) = &setup();
  if ($code != 2) { return; }
  &print2 ("    RETR \'$rfilename\'\n");
  print SERVER "RETR $rfilename\r\n";

  $code = &getreply();
  if ($code != 1) { return; }
  ($addr = accept(DATA1, DATA)) || ((&printn ("accept:$!\n")), return);
  if ($rfilename =~ /\.Z$/) {
      open(RFILE, "| zmore");
  }
  else {
      open(RFILE, "| more") || ((&printn ("open:$!\n")), return);
  }
  $SIG{'INT'} = 'abort';
  while (($len = read(DATA1, $buf, 1000)) > 0 || $! == &EINTR) {
    $buf =~ s/\r//g if ($NOCR);
    print RFILE $buf;
  }
  $SIG{'INT'} = 'again';
  close DATA1;
  close RFILE;
  $code = &getreply();
}

# List the current directory
# Has the same syntax and options as Unix ls command
sub list {
  if (!$connecting) {
    &printn ("Not connected. Connect first\n");
    return;
  }
  local($buf, $command, $index);
  local($code) = &setup();
  if ($code != 2) { return; }
  $command = "LIST";
  for ($index = 0; $index <= $#_; $index++) {
    $command .= " " . $_[$index];
  }
  &print2 ("    $command\n");
  print SERVER $command, "\r\n";
  $code = &getreply();
  if ($code != 1) { return; }
  ($addr = accept(DATA1, DATA)) || ((&printn ("accept:$!\n")), return);
  $SIG{'INT'} = 'abort';
  $less = 0;
  if ($interactive & $ENV{'TERM'} ne 'emacs' & $ENV{'TERM'} ne 'unknown') {
      $less = 1;
      open(LESS, "|less");
      $SIG{'PIPE'} = 'SIGPIPE';
      $nooutput = 0;
  }
  while (sysread(DATA1, $buf, 1000) > 0) {
      $buf =~ s/\r//g if ($NOCR);
      if ($less) {
	  $ret = eval 'print LESS $buf';
      }
      else {
	  print $buf;
      }
  }
  if ($less) {
      close(LESS);
  }
  $SIG{'INT'} = 'again';
  close DATA1;
  $code = &getreply();
}

# Return a file list of the current remote working directory in a tmp file
sub nlst {
  if (!$connecting) {
    &printn ("Not connected. Connect first\n");
    return;
  }
  local($buf, $command, $index);
  local($code) = &setup();
  if ($code != 2) { return; }
  $command = "NLST";
  for ($index = 0; $index <= $#_; $index++) {
    $command .= " " . $_[$index];
  }
  &print2 ("    $command\n");
  print SERVER $command, "\r\n";
  $code = &getreply();
  if ($code != 1) { return; }
  ($addr = accept(DATA1, DATA)) || ((&printn ("accept:$!\n")), return);
  $SIG{'INT'} = 'abort';
  open(FILE, ">/tmp/nftp$$.nlst")||((&printn ("open:$!\n")), close(DATA1), return);
  while (sysread(DATA1, $buf, 1000) > 0) {
    $buf =~ s/\r//g if ($NOCR);
    print FILE $buf;
  }
  $SIG{'INT'} = 'again';
  close(FILE);
#  unlink("/tmp/nftp$$.nlst");
  close DATA1;
  $code = &getreply();
}

# Put a local file to the remote site. The first argument is the local file
# name. The seocnd argument is the remote filename, assumed to be the same
# as the first argument when it is missing
sub put {
  if (!$connecting) {
    &printn ("Not connected. Connect first\n");
    return;
  }
  if ($#_ < $[) {
    &nprint ("Need at least one argument\n");
    return;
  }
  local($rfilename);
  local($lfilename) = $_[0];
  open(FILE, $lfilename) || ((&printn ("open:$!\n")), return);
  local($code) = &setup();
  local($buf);
  if ($code != 2) { return; }
  if ($#_ == $[) {
    $rfilename = $lfilename;
  }
  else {
    $rfilename = $_[1];
  }
  &print2 ("    STOR $rfilename\n");
  print SERVER "STOR $rfilename\r\n";
  $code = &getreply();
  if ($code != 1) { return; }
  ($addr = accept(DATA1, DATA)) || ((&printn ("accept:$!\n")), return);
  select(FILE); $| = 1; select(STDOUT); $| = 1;
  $bytes = 0;
  $SIG{'INT'} = 'abort';
  if ($interactive) {
      do init_hash();
  }
  while (($len = read(FILE, $buf, 1000)) > 0) {
      $bytes += $len;
      print DATA1 $buf;
  }
  if ($interactive){
      do end_hash();
  }
  $SIG{'INT'} = 'again';
  close DATA1;
  $code = &getreply();
}

# Rename a remote file
sub rename {
  if (!$connecting) {
    &printn ("Not connected. Connect first\n");
    return;
  }
  local($from, $to) = @_;
  local($code);

  &print2 ("    RNFR $from\n");
  print SERVER "RNFR $from\r\n";
  $code = &getreply();
  if ($code != 3) { return; }
  &print2 ("    RNTO $to\n");
  print SERVER "RNTO $to\r\n";
  $code = &getreply();
}

# Get the rest of the file. No different filenames for the remote
# and local files, sorry.
sub restoffile {
  if (!$connecting) {
    &printn ("Not connected. Connect first\n");
    return;
  }
  if ($#_ < $[) {
    &nprint ("Need at least one argument\n");
    return;
  }
  local($code);
  local(@size) = stat($_[0]);

  &print2 ("    REST $size[7]\n");
  print SERVER "REST ", $size[7], "\r\n";
  $code = &getreply();
  if ($code != 3) { return; }
  do get($_[0], ">$_[0]");
}

sub init_hash {
    $SIG{'ALRM'} = 'sig_alarm';
    $ten = 0;
    $#ten = -1;
    $tentimes = 0;
    $lastbytes = 0;
    alarm(1);
    $bytes = 0;
    $totaltime = 0;
    $inter = 0;
    $SIG{'INT'} = 'abort';
}

sub end_hash {
    print "\n";
    $SIG{'ALRM'} = 'IGNORE';
    alarm(0);
}

sub sig_alarm {
    $totaltime++;
    if ($totaltime > 0) {
	$usetime = $bytes / $totaltime / 1000;
    }
    if ($totaltime < 10) {
	$increment = $bytes - $lastbytes;
	$tentimes += $increment;
	$tentime = $tentimes / $totaltime / 1000;
	$ten[$ten] = $increment;
	$ten++;
    }
    else {
	$increment = $bytes - $lastbytes;
	$tentimes += $increment - $ten[$ten];
	$tentime = $tentimes / 10000;
	$ten[$ten] = $increment;
	$ten = ($ten + 1) % 10;
    }
    $lastbytes = $bytes;
    if ($verbose > 3) {
        printf "\r%5d:%8d:%7.2f:%7.2f", $totaltime, $bytes, $usetime, $tentime;
    }
    $SIG{'ALRM'} = 'sig_alarm';
    alarm(1);
}

# login if -a is not specified,
sub ilogin {
  if (defined($opt_a)) {
    &printn ("Ah, but you specified -a option.\n");
  }
  else {
    &login (@_);
  }
}

# login the remote site
sub login {
  local($user, $pass, $acct) = @_;
  local($code);

  if (! defined($_[0])) {
    print "login:";
    $user = <>;
    chop($user);
  }
  &print2 ("    USER $user\n");
  print SERVER "USER ", $user, "\r\n";
  $code = &getreply();
  if ($code == 2) { $connecting = 1; $NOCR = 1; return; }
  if ($code != 3) { return; }
  $NOCR = 1;
  if (! defined($_[1])) {
    do set_cbreak(1);
    print "Password:";
    $pass = <>;
    do set_cbreak(0);
    print "\n";
    chop($pass);
  }
  &print2 ("    PASS\n");
  print SERVER "PASS ", $pass, "\r\n";
  $code = &getreply();
  if ($code == 2) { $connecting = 1; return; }
  if ($code != 3) { return; }

  if (! defined($_[2])) {
    do set_cbreak(1);
    print "Account:";
    $acct = <>;
    do set_cbreak(0);
    print "\n";
    chop($acct);
  }
  &print2 ("    ACCT\n");
  print SERVER "ACCT ", $acct, "\r\n";
  $code = &getreply();
  if ($code == 2) { $connecting = 1; return; }
}

# Set up the data connection
sub setup {
  socket(DATA, &PF_INET, &SOCK_STREAM, 0) || ((&printn ("socket:$!\n")), return 0);
  $sockname = getsockname(SERVER) || ((&printn ("getsockname:$!\n")), return 0);
  ($family, $port, $addr) =  unpack($sockaddr, $sockname);
  @port = unpack("CC", $port);
  $sout = pack($sockaddr, &AF_INET, 0, $addr);
  bind(DATA, $sout) || ((&printn ("bind:$!\n")), return $FAIL);
  listen(DATA, 1) || ((&printn ("listen:$!\n")), return $FAIL);
  $sockname = getsockname(DATA) || ((&printn ("getsockname:$!\n")), return $FAIL);
  ($family, $port, $addr) =  unpack($sockaddr, $sockname);
  $port[0] = int($port / 256);
  $port[1] = $port % 256;
  &print2 ("    PORT $addr[0],$addr[1],$addr[2],$addr[3],$port[0],$port[1]\n");
  print SERVER "PORT $addr[0],$addr[1],$addr[2],$addr[3],$port[0],$port[1]\r\n";
  &getreply();
}

# Connect to a remote site
sub connect {
  if ($connecting) {
    &printn ("Connected. Disconnect first\n");
    return;
  }
  $port = 21;
  if ($_[0] =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
      print "IP address is given...";
      $saddr = pack("CCCC", $1, $2, $3, $4);
  }
  else {
      print "Trying the host table /etc/hosts...";
      if (!(($name, $aliases, $type, $len, $saddr) = gethostbyname($_[0]))) {
	  print "\nTrying the domain name server for IP address...";
	  if (!($saddr = &resolver($_[0]))) {
	      print "\nTrying the FTP file for IP address...";
	      if (!($saddr = &ftpl($_[0]))) {
		  &printn ("\nNo such host\n");
		  return;
	      }
	  }
      }
  }
  print join('.', unpack('C4', $saddr)), "\n";
  $sin = pack($sockaddr, 2, $port, $saddr);

  socket(SERVER, 2, 1, 0) || ((&printn ("socket:$!\n")), return $FAIL);
  connect(SERVER, $sin) || ((&printn ("connect:$!\n")), return $FAIL);
  $sockname = getsockname(SERVER) || ((&printn ("getsockname:$!\n")), return $FAIL);
  ($family, $port, $addr) =  unpack($sockaddr, $sockname);
  @addr = unpack("CCCC", $addr);

  select(SERVER); $| = 1; select(STDOUT); $| = 1;
  $connecting = 1;
  &getreply();
  if ($opt_a) {
    do login("ftp", "guest");
  }
}

# make a local directory
sub domkdir {
  mkdir($_[0], 0700) || ((&printn ("lmkdir: $!\n")), return);
  &print1 ("created: $_[0]\n");
}

# delete a local file
sub rm {
  unlink($_[0]);
}

# change the current local working directory
sub dochdir {
  local($path2) = $_[0];
  if ($path2 eq "~") {
    $path2 = $ENV{'HOME'};
  }
  if ($path2 =~ m|^~([^/]*)|) {
    if ($1 eq "") {
      $path2 = $ENV{'HOME'} . $';
    }
    else {
      @ent = getpwnam($1);
      $path2 = $ent[7] . $';
    }
  } 
  if ($path2 eq "") {
    $path2 = $ENV{'HOME'};
  }
  chdir $path2 || ((&printn ("lcd: $!\n")), next);
  $pwd = &pathname($pwd, $path2);
}

# Quit the current session
sub doquit {
  if ($connecting) {
    $command_name = "QUIT";
    do literal();
    unlink ("/tmp/nftp$$");
    close SERVER;
  }
  &print1 ("<End of nftp session>\n");
  close STDOUT;
  exit 0;
}

# send abort command. Unfortunately, this is optional in some sites. Oh well
sub abort {
    &end_hash();
    printf SERVER pack("CC", &IAC, &IP);
    send(SERVER, pack("C", &IAC), 1, &MSG_OOB);
    printf SERVER pack("C", &DM);
    $command_name = "ABOR";
    do literal();
    $inter = 1;
    $code = &getreply();
    $code = &getreply();
#    close DATA1;
    $SIG{'INT'} = 'again';
    print "Tried my best\n";
}

# dummy interrupt routine  
sub again {
  $in = "";
  print "\n";
}

# multiple get command
sub mget {
  do nlst($_[0]);
  open(NLIST, "/tmp/nftp$$.nlst");
  $inter = 0;
  $reg = $_[0];

  # convert Unix regular expression to perl regular expression
  $reg =~ s/\?/./g;
  $reg =~ s/([^\\]*)([\*\+])/\1.\2/g;
  $reg =~ s/^\*/\.\*/g;
  $reg =~ s/^\?/\.\?/g;

  while ($name = <NLIST>) {
    chop($name);
    $name =~ s/(.*)\r/\1/;
    $name =~ /$reg/ && do get($name, "$name");
    if ($inter && $interactive) {
      print "Continue with mget? ";
      $c = <STDIN>;
      if ($c =~ /^[yY]/) {
        $inter = 0;
        next;
      }
      else {
         last;
      }
    }
  }
  unlink("/tmp/nftp$$.nlst");
}

# multiple put command
sub mput {
  local($names) = $_[0];
  local($_, @file);
  while (<${names}>) {
    if ($inter && $interactive) {
      print "Continue with mput? ";
      $c = <STDIN>;
      if ($c =~ /^[yY]/) {
        $inter = 0;
        next;
      }
      else {
         last;
      }
    }
    @file = split('/', $_);
    do put($file[$#file], $_);
  }
}

# get a line of command
sub getinput {
    if (defined($lastcommand)) {
	$_ = $lastcommand;
	undef($lastcommand);
	return;
    }
    $prompt_sig = $_[0];
    if ($interactive) {
	&print0 ($_[0]);
	if ($_ = <>) {
	    $_;
	}
	else {
	    "quit\n";
	}
    }
    elsif ($background) {
	if ($_ = <STDIN>) {
	    $_;
	}
	else {
	    "quit\n";
	}
    }
    elsif ($_ = <>) {
	$_;
    }
    else {
	"quit\n";
    }
}

# set background ftp session
sub background {
  $background = 1;
  $interactive = 0;
  $noread = 0;
  $SIG{'INT'} = 'backint';
  open (COMM, ">/tmp/nftpback$$");
  &print0 ("Input commands. Finish by a single dot on the line.\n");
  &print0 ("> ");
  $comm = <STDIN>;
  while ($comm !~ /^\.$/) {
    print COMM $comm;
  }
  continue {
    &print0 ("> ");
    $comm = <STDIN>;
  }
  close(COMM);
  $SIG{'INT'} = 'again';
  open(STDIN, "/tmp/nftpback$$");
  if (defined($_[0])) {
    $file = "$_[0]";
  }
  else {
    $file = "/tmp/nftpback$$.stdout";
  }
  if ($child = fork()) {
    &print1 ("[nftp log file: $file  process: $child]\n");
    exit(0);
  }
  $SIG{'HUP'} = 'IGNORE';
  open(STDOUT, ">$file");
  open(TTY, '/dev/tty');
  ioctl(TTY, $TIOCNOTTY, 0);
  close(TTY);
backexit: return;
}

# for changing the type of file expected
sub type {
  if (!$connecting) {
    &printn ("Not connected. Connect first\n");
    return;
  }
  local($command) = $command_name;
  $command_name = "TYPE";
  $command =~ 'binary' && (do literal('I'), $NOCR = 0, return);
  $command =~ 'ascii' && (do literal('A'), $NOCR = 1, return);
  $command =~ 'tenex' && (do literal('L', 8), $NOCR = 1);
}

# close remote connection
sub close {
  if (!$connecting) {
    &printn ("Not connected. Connect first\n");
    return;
  }
  $command_name = "close";
  &simple();
  $connecting = 0;
  $NOCR = 1;
}

# set verbose levels
sub verbose {
  return if ($#_ < $[);
  local($level) = $_[0];
  return if ($level < -1 || $level > 4);
  $verbose = $level;
  if ($verbose > 1) {
    print "Verbose level is now: $verbose\n";
  }
}

sub printn {
  &nprint (-1, @_);
}

sub print1 {
  &nprint (1, @_);
}

sub print0 {
  &nprint (0, @_);
}

sub print2 {
  &nprint (2, @_);
}

sub print3 {
  &nprint (3, @_);
}

sub nprint {
  local($level, @str) = @_;
  print @str if ($verbose > $level);
}

sub Sigpipe {
#  print "Can't handle sigpipe signal\n";
#  print "Leaving...\n";
#  exit(1);
}

sub backint {
    $SIG{'INT'} = 'DEFAULT';
    goto backexit;
}

sub ftpl {
    local($ftpl) = @_;
    open(FTPL, "uncompress -c $ftplist |");
    while (<FTPL>) {
	last if ($_ =~ /^=/);
    }
    while (<FTPL>) {
	if ($_ =~ /^$ftpl /) {
	    ($name, $addr) = split(' ', $_);
	    close(FTPL);
	    return $addr;
	}
    }
    close(FTPL);
    0;
}

sub dohistory {
    local($i, $entry);
    $i = 0;
    foreach $entry (@history) {
	print "$i: $entry\n";
	$i++;
    }
}

sub doshell {
    system("sh -c @_");
}

sub dohis {
    if ($#_ < 0 || $#_ > 0 || $_[0] < 0 || $_[0] > $#history) {
	print "Invalid history entry\n";
	return;
    }
    $lastcommand = "$history[$_[0]]\n";
    print "$lastcommand";
}

sub SIGPIPE {
    close(LESS);
    $nooutput = 1;
}

sub SIGTSTP {
    print "\n";
    $SIG{'TSTP'} = 'DEFAULT';
    kill('TSTP', 0);
    print $prompt_sig;
}
SHAR_EOF
chmod +x 'nftp'
fi # end of overwriting check
if test -f 'nftp.1'
then
	echo shar: will not over-write existing file "'nftp.1'"
else
cat << \SHAR_EOF > 'nftp.1'
.TH NFTP 1 "Release 44" "Perl Version 3"
.SH NAME
nftp - a ftp client written in Perl
.SH SYNOPSIS
.B nftp
[-a] [-h hostname/IP-address] [command-file]
.SH DESCRIPTION
The \fInftp\fP program is a ftp client written in Perl. Originally
written as an exercise for Perl, it now contains almost all the
features of the BSD ftp client. More features are being added to it to
make it more versatile.
.SH OPTIONS
.PP
.IP "\fB-h\fP hostname
Specify a host to connect to by hostname, or IP address. \fInftp\fP
connects to a domain name server if it does not know the IP address of
the hostname you specify. In addition, it will also check the set of
anonymous FTP list posted in the newsgroup comp.misc. This file is in
/usr2/new/lib/ftp.list.Z and will be updated from time to time.
.IP "\fB-a\fP"
Tell \fInftp\fP to login as user ftp and password guest. This is
useful if you are connecting as a anonymous user.
.SH "EXAMPLES"
.TP8
.B "nftp"
This will run nftp and wait for input.
.TP 8
.B "nftp command"
This will run nftp and execute the commands in the file command.
.TP8
.B "nftp -a -h no9sun.csd.uwo.ca"
This will connect to the host no9sun.csd.uwo.ca as an anonymous user.
.TP 8
.SH "COMMANDS"
Since features are being added to nftp, please run nftp and then type
"help" to see the list of commands available.
.SH "SEE ALSO"
ftp(1)
.SH AUTHOR
Khun Yee Fung, Department of Computer Science, University of Western
Ontario.

The author is not responsible for the use of this program. Use at your
own risk. If you have any problems, you can mail to
clipper@csd.uwo.ca. The author will not promise to respond to them,
however.

.SH BUGS
Control C is not handled nicely.

.SH DATE
1991 February 21
SHAR_EOF
fi # end of overwriting check
if test -f 'resolver.pl'
then
	echo shar: will not over-write existing file "'resolver.pl'"
else
cat << \SHAR_EOF > 'resolver.pl'
#!/usr2/new/bin/perl 
# a subroutine to resolve a Internet host name to IP address
# Written by Khun Yee Fung (clipper@csd.uwo.ca)
sub resolver {
  local($sockaddr) = 'S n a4 x8';
  local($hostname) = $_[0];
  local($server) = 'ria.ccs.uwo.ca';
  local($port) = 53;

  (local($name, $aliases, $type, $len, $saddr) = gethostbyname($server))
    || ((print "hostname\n"), return 0);
  local($sin) = pack($sockaddr, 2, $port, $saddr);

  socket(NSERVER, 2, 1, 0) || ((print "socket: $!\n"), return 0);
  connect(NSERVER, $sin) || ((print "connect: $!\n"), return 0);

  select(NSERVER); $| = 1; select(STDOUT); $| = 1;

  local($len) = 18 + length($hostname);
  local(@names) = split('\.', $hostname);
  local($head) = pack('S6', 319, 256, 1, 0, 0, 0);
  print NSERVER pack('S', $len), $head;
  local($arg, $response);
  foreach $arg (@names) {
    print NSERVER pack('C', length($arg)), $arg;
  }
  print NSERVER pack('CS2', 0, 1, 1);

  read(NSERVER, $len, 2);
  read(NSERVER, $response, unpack('S', $len));
  close NSERVER;

  local(@RCODE) = ("No error", "Format error", "Server Failure", "Name error",
    "Not implemented", "Refused");
  local(@shead) = unpack('S6', $response);
  ($shead[1] & 0x0F) == 0 || return 0;

  local($in) = 12;
  local($ans) = $shead[2];
  local($c);
  while ($ans > 0) {
    while (($c = ord(substr($response, $in++, 1))) != 0) {
      $in += $c;
    }
    $in += 4;
    $ans--;
  }

  $ans = $shead[3];
  local($type, $rdlength, $rdata);
  while ($ans > 0) {
    while (($c = ord(substr($response, $in++, 1))) != 0) {
      ($c & 0xc0) != 0xc0 || $in++, last;
      $in += $c;
    }
    $type = substr($response, $in, 2);
    $in += 8;
    $rdlength = unpack('n', substr($response, $in, 2));
    $in += 2;
    $rdata = substr($response, $in, $rdlength);
    if (unpack('S', $type) == 1) {
      return $rdata;
    }
    $in += $rdlength;
    $ans--;
  }
  return 0;
}
1;
SHAR_EOF
chmod +x 'resolver.pl'
fi # end of overwriting check
if test -f 'pathname.pl'
then
	echo shar: will not over-write existing file "'pathname.pl'"
else
cat << \SHAR_EOF > 'pathname.pl'
#!/usr2/new/bin/perl 
# A simple subroutine to get the absolute path when two paths are
# merged. Written by Khun Yee Fung (clipper@csd.uwo.ca)

sub pathname {
  local($path1) = $_[0];
  local($path2) = $_[1];
  local($arg, @path);
  local($num) = 0;

  if ($path2 eq /^~/) {
    $path2 = $ENV{'HOME'} . $path2;
  }
  if ($path2 =~ m|^~([^/]*)|) {
    if ($1 == "") {
      $path2 = $ENV{'HOME'} . $path2;
    }
    else {
      @ent = getpwnam($1) || (@ent = (""));
      $path2 = $ent[7] . $path2;
    }
  } 
  if ($path2 !~ m|^/|) {
    $path2 = $path1 . "/" . $path2;
  }
  local(@paths) = split(m|/|, $path2);
  foreach $arg (@paths) {
    ($arg =~ /^\.\.$/) && ($num--, next);
    ($arg =~ /^\.$/) && next;
    ($arg eq "") && next;
    $path[$num] = $arg;
    $num++;
  }
  $#path = $num - 1;
  $path1 = "";
  foreach $arg (@path) {
    $path1 = $path1 . $arg . "/";
  }
  chop($path1);
  $path1 = "/" . $path1 if ($path1 !~ "^/");
  $path1;
}
1;
SHAR_EOF
chmod +x 'pathname.pl'
fi # end of overwriting check
if test -f 'socket.h'
then
	echo shar: will not over-write existing file "'socket.h'"
else
cat << \SHAR_EOF > 'socket.h'
if (!defined &_SOCKET_) {
    eval 'sub _SOCKET_ {1;}';
    eval 'sub SOCK_STREAM {1;}';
    eval 'sub SOCK_DGRAM {2;}';
    eval 'sub SOCK_RAW {3;}';
    eval 'sub SOCK_RDM {4;}';
    eval 'sub SOCK_SEQPACKET {5;}';
    eval 'sub SO_DEBUG {0x0001;}';
    eval 'sub SO_ACCEPTCONN {0x0002;}';
    eval 'sub SO_REUSEADDR {0x0004;}';
    eval 'sub SO_KEEPALIVE {0x0008;}';
    eval 'sub SO_DONTROUTE {0x0010;}';
    eval 'sub SO_BROADCAST {0x0020;}';
    eval 'sub SO_USELOOPBACK {0x0040;}';
    eval 'sub SO_LINGER {0x0080;}';
    eval 'sub SO_OOBINLINE {0x0100;}';
    eval 'sub SO_DONTLINGER {(~&SO_LINGER);}';
    eval 'sub SO_SNDBUF {0x1001;}';
    eval 'sub SO_RCVBUF {0x1002;}';
    eval 'sub SO_SNDLOWAT {0x1003;}';
    eval 'sub SO_RCVLOWAT {0x1004;}';
    eval 'sub SO_SNDTIMEO {0x1005;}';
    eval 'sub SO_RCVTIMEO {0x1006;}';
    eval 'sub SO_ERROR {0x1007;}';
    eval 'sub SO_TYPE {0x1008;}';
    eval 'sub SOL_SOCKET {0xffff;}';
    eval 'sub AF_UNSPEC {0;}';
    eval 'sub AF_UNIX {1;}';
    eval 'sub AF_INET {2;}';
    eval 'sub AF_IMPLINK {3;}';
    eval 'sub AF_PUP {4;}';
    eval 'sub AF_CHAOS {5;}';
    eval 'sub AF_NS {6;}';
    eval 'sub AF_NBS {7;}';
    eval 'sub AF_ECMA {8;}';
    eval 'sub AF_DATAKIT {9;}';
    eval 'sub AF_CCITT {10;}';
    eval 'sub AF_SNA {11;}';
    eval 'sub AF_DECnet {12;}';
    eval 'sub AF_DLI {13;}';
    eval 'sub AF_LAT {14;}';
    eval 'sub AF_HYLINK {15;}';
    eval 'sub AF_APPLETALK {16;}';
    eval 'sub AF_NIT {17;}';
    eval 'sub AF_802 {18;}';
    eval 'sub AF_OSI {19;}';
    eval 'sub AF_X25 {20;}';
    eval 'sub AF_OSINET {21;}';
    eval 'sub AF_GOSIP {22;}';
    eval 'sub AF_MAX {21;}';
    eval 'sub PF_UNSPEC {&AF_UNSPEC;}';
    eval 'sub PF_UNIX {&AF_UNIX;}';
    eval 'sub PF_INET {&AF_INET;}';
    eval 'sub PF_IMPLINK {&AF_IMPLINK;}';
    eval 'sub PF_PUP {&AF_PUP;}';
    eval 'sub PF_CHAOS {&AF_CHAOS;}';
    eval 'sub PF_NS {&AF_NS;}';
    eval 'sub PF_NBS {&AF_NBS;}';
    eval 'sub PF_ECMA {&AF_ECMA;}';
    eval 'sub PF_DATAKIT {&AF_DATAKIT;}';
    eval 'sub PF_CCITT {&AF_CCITT;}';
    eval 'sub PF_SNA {&AF_SNA;}';
    eval 'sub PF_DECnet {&AF_DECnet;}';
    eval 'sub PF_DLI {&AF_DLI;}';
    eval 'sub PF_LAT {&AF_LAT;}';
    eval 'sub PF_HYLINK {&AF_HYLINK;}';
    eval 'sub PF_APPLETALK {&AF_APPLETALK;}';
    eval 'sub PF_NIT {&AF_NIT;}';
    eval 'sub PF_802 {&AF_802;}';
    eval 'sub PF_OSI {&AF_OSI;}';
    eval 'sub PF_X25 {&AF_X25;}';
    eval 'sub PF_OSINET {&AF_OSINET;}';
    eval 'sub PF_GOSIP {&AF_GOSIP;}';
    eval 'sub PF_MAX {&AF_MAX;}';
    eval 'sub SOMAXCONN {5;}';
    eval 'sub MSG_OOB {0x1;}';
    eval 'sub MSG_PEEK {0x2;}';
    eval 'sub MSG_DONTROUTE {0x4;}';
    eval 'sub MSG_MAXIOVLEN {16;}';
}
1;
SHAR_EOF
chmod +x 'socket.h'
fi # end of overwriting check
if test -f 'ioctl.pl'
then
	echo shar: will not over-write existing file "'ioctl.pl'"
else
cat << \SHAR_EOF > 'ioctl.pl'
$TIOCGSIZE = 0x40087468;
$TIOCSSIZE = 0x80087467;
$IOCPARM_MASK = 0x1fff;
$IOCPARM_MAX = 0x200;
$IOC_VOID = 0x20000000;
$IOC_OUT = 0x40000000;
$IOC_IN = 0x80000000;
$IOC_INOUT = 0xC0000000;
$IOC_DIRMASK = 0xe0000000;
$TIOCGETD = 0x40047400;
$TIOCSETD = 0x80047401;
$TIOCHPCL = 0x20007402;
$TIOCMODG = 0x40047403;
$TIOCMODS = 0x80047404;
$TIOCM_LE = 0001;
$TIOCM_DTR = 0002;
$TIOCM_RTS = 0004;
$TIOCM_ST = 0010;
$TIOCM_SR = 0020;
$TIOCM_CTS = 0040;
$TIOCM_CAR = 0100;
$TIOCM_CD = 0x40;
$TIOCM_RNG = 0200;
$TIOCM_RI = 0x80;
$TIOCM_DSR = 0400;
$TIOCGETP = 0x40067408;
$TIOCSETP = 0x80067409;
$TIOCSETN = 0x8006740A;
$TIOCEXCL = 0x2000740D;
$TIOCNXCL = 0x2000740E;
$TIOCFLUSH = 0x80047410;
$TIOCSETC = 0x80067411;
$TIOCGETC = 0x40067412;
$TANDEM = 0x00000001;
$CBREAK = 0x00000002;
$LCASE = 0x00000004;
$ECHO = 0x00000008;
$CRMOD = 0x00000010;
$RAW = 0x00000020;
$ODDP = 0x00000040;
$EVENP = 0x00000080;
$ANYP = 0x000000c0;
$NLDELAY = 0x00000300;
$NL0 = 0x00000000;
$NL1 = 0x00000100;
$NL2 = 0x00000200;
$NL3 = 0x00000300;
$TBDELAY = 0x00000c00;
$TAB0 = 0x00000000;
$TAB1 = 0x00000400;
$TAB2 = 0x00000800;
$XTABS = 0x00000c00;
$CRDELAY = 0x00003000;
$CR0 = 0x00000000;
$CR1 = 0x00001000;
$CR2 = 0x00002000;
$CR3 = 0x00003000;
$VTDELAY = 0x00004000;
$FF0 = 0x00000000;
$FF1 = 0x00004000;
$BSDELAY = 0x00008000;
$BS0 = 0x00000000;
$BS1 = 0x00008000;
$ALLDELAY = 0xFF00;
$CRTBS = 0x00010000;
$PRTERA = 0x00020000;
$CRTERA = 0x00040000;
$TILDE = 0x00080000;
$MDMBUF = 0x00100000;
$LITOUT = 0x00200000;
$TOSTOP = 0x00400000;
$FLUSHO = 0x00800000;
$NOHANG = 0x01000000;
$L001000 = 0x02000000;
$CRTKIL = 0x04000000;
$PASS8 = 0x08000000;
$CTLECH = 0x10000000;
$PENDIN = 0x20000000;
$DECCTQ = 0x40000000;
$NOFLSH = 0x80000000;
$TIOCLBIS = 0x8004747F;
$TIOCLBIC = 0x8004747E;
$TIOCLSET = 0x8004747D;
$TIOCLGET = 0x4004747C;
$LCRTBS = 0x1;
$LPRTERA = 0x2;
$LCRTERA = 0x4;
$LTILDE = 0x8;
$LMDMBUF = 0x10;
$LLITOUT = 0x20;
$LTOSTOP = 0x40;
$LFLUSHO = 0x80;
$LNOHANG = 0x100;
$LCRTKIL = 0x400;
$LPASS8 = 0x800;
$LCTLECH = 0x1000;
$LPENDIN = 0x2000;
$LDECCTQ = 0x4000;
$LNOFLSH = 0xFFFF8000;
$TIOCSBRK = 0x2000747B;
$TIOCCBRK = 0x2000747A;
$TIOCSDTR = 0x20007479;
$TIOCCDTR = 0x20007478;
$TIOCGPGRP = 0x40047477;
$TIOCSPGRP = 0x80047476;
$TIOCSLTC = 0x80067475;
$TIOCGLTC = 0x40067474;
$TIOCOUTQ = 0x40047473;
$TIOCSTI = 0x80017472;
$TIOCNOTTY = 0x20007471;
$TIOCPKT = 0x80047470;
$TIOCPKT_DATA = 0x00;
$TIOCPKT_FLUSHREAD = 0x01;
$TIOCPKT_FLUSHWRITE = 0x02;
$TIOCPKT_STOP = 0x04;
$TIOCPKT_START = 0x08;
$TIOCPKT_NOSTOP = 0x10;
$TIOCPKT_DOSTOP = 0x20;
$TIOCSTOP = 0x2000746F;
$TIOCSTART = 0x2000746E;
$TIOCMSET = 0x8004746D;
$TIOCMBIS = 0x8004746C;
$TIOCMBIC = 0x8004746B;
$TIOCMGET = 0x4004746A;
$TIOCREMOTE = 0x80047469;
$TIOCGWINSZ = 0x40087468;
$TIOCSWINSZ = 0x80087467;
$TIOCUCNTL = 0x80047466;
$TIOCSSOFTC = 0x80047465;
$TIOCGSOFTC = 0x40047464;
$TIOCSCARR = 0x80047463;
$TIOCWCARR = 0x20007462;
$OTTYDISC = 0;
$NETLDISC = 1;
$NTTYDISC = 2;
$TABLDISC = 3;
$SLIPDISC = 4;
$FIOCLEX = 0x20006601;
$FIONCLEX = 0x20006602;
$FIONREAD = 0x4004667F;
$FIONBIO = 0x8004667E;
$FIOASYNC = 0x8004667D;
$FIOSETOWN = 0x8004667C;
$FIOGETOWN = 0x4004667B;
$SIOCSHIWAT = 0x80047300;
$SIOCGHIWAT = 0x40047301;
$SIOCSLOWAT = 0x80047302;
$SIOCGLOWAT = 0x40047303;
$SIOCATMARK = 0x40047307;
$SIOCSPGRP = 0x80047308;
$SIOCGPGRP = 0x40047309;
$SIOCADDRT = 0x8030720A;
$SIOCDELRT = 0x8030720B;
$SIOCSIFADDR = 0x8020690C;
$SIOCGIFADDR = 0xC020690D;
$SIOCSIFDSTADDR = 0x8020690E;
$SIOCGIFDSTADDR = 0xC020690F;
$SIOCSIFFLAGS = 0x80206910;
$SIOCGIFFLAGS = 0xC0206911;
$SIOCGIFBRDADDR = 0xC0206912;
$SIOCSIFBRDADDR = 0x80206913;
$SIOCGIFCONF = 0xC0086914;
$SIOCGIFNETMASK = 0xC0206915;
$SIOCSIFNETMASK = 0x80206916;
$SIOCGIFMETRIC = 0xC0206917;
$SIOCSIFMETRIC = 0x80206918;
$SIOCSARP = 0x8024691E;
$SIOCGARP = 0xC024691F;
$SIOCDARP = 0x80246920;
SHAR_EOF
chmod +x 'ioctl.pl'
fi # end of overwriting check
if test -f 'telnet.h'
then
	echo shar: will not over-write existing file "'telnet.h'"
else
cat << \SHAR_EOF > 'telnet.h'
sub IAC {255;}
sub DONT {254;}
sub DO {253;}
sub WONT {252;}
sub WILL {251;}
sub SB {250;}
sub GA {249;}
sub EL {248;}
sub EC {247;}
sub AYT {246;}
sub AO {245;}
sub IP {244;}
sub BREAK {243;}
sub DM {242;}
sub NOP {241;}
sub SE {240;}
sub EOR {239;}
sub SYNCH {242;}
if (defined &TELCMDS) {
}
sub TELOPT_BINARY {0;}
sub TELOPT_ECHO {1;}
sub TELOPT_RCP {2;}
sub TELOPT_SGA {3;}
sub TELOPT_NAMS {4;}
sub TELOPT_STATUS {5;}
sub TELOPT_TM {6;}
sub TELOPT_RCTE {7;}
sub TELOPT_NAOL {8;}
sub TELOPT_NAOP {9;}
sub TELOPT_NAOCRD {10;}
sub TELOPT_NAOHTS {11;}
sub TELOPT_NAOHTD {12;}
sub TELOPT_NAOFFD {13;}
sub TELOPT_NAOVTS {14;}
sub TELOPT_NAOVTD {15;}
sub TELOPT_NAOLFD {16;}
sub TELOPT_XASCII {17;}
sub TELOPT_LOGOUT {18;}
sub TELOPT_BM {19;}
sub TELOPT_DET {20;}
sub TELOPT_SUPDUP {21;}
sub TELOPT_SUPDUPOUTPUT {22;}
sub TELOPT_SNDLOC {23;}
sub TELOPT_TTYPE {24;}
sub TELOPT_EOR {25;}
sub TELOPT_TUID {26;}
sub TELOPT_OUTMRK {27;}
sub TELOPT_TTYLOC {28;}
sub TELOPT_3270REGIME {29;}
sub TELOPT_X3PAD {30;}
sub TELOPT_NAWS {31;}
sub TELOPT_TSPEED {32;}
sub TELOPT_LFLOW {33;}
sub TELOPT_EXOPL {255;}
if (defined &TELOPTS) {
    eval 'sub NTELOPTS {(1+&TELOPT_LFLOW);}';
}
sub TELQUAL_IS {0;}
sub TELQUAL_SEND {1;}
1;
SHAR_EOF
chmod +x 'telnet.h'
fi # end of overwriting check
if test -f 'getopts.pl'
then
	echo shar: will not over-write existing file "'getopts.pl'"
else
cat << \SHAR_EOF > 'getopts.pl'
;# getopts.pl - a better getopt.pl

;# Usage:
;#      do Getopts('a:bc');  # -a takes arg. -b & -c not. Sets opt_* as a
;#                           #  side effect.

sub Getopts {
    local($argumentative) = @_;
    local(@args,$_,$first,$rest,$errs);
    local($[) = 0;

    @args = split( / */, $argumentative );
    while(($_ = $ARGV[0]) =~ /^-(.)(.*)/) {
	($first,$rest) = ($1,$2);
	$pos = index($argumentative,$first);
	if($pos >= $[) {
	    if($args[$pos+1] eq ':') {
		shift(@ARGV);
		if($rest eq '') {
		    $rest = shift(@ARGV);
		}
		eval "\$opt_$first = \$rest;";
	    }
	    else {
		eval "\$opt_$first = 1";
		if($rest eq '') {
		    shift(@ARGV);
		}
		else {
		    $ARGV[0] = "-$rest";
		}
	    }
	}
	else {
	    print STDERR "Unknown option: $first\n";
	    ++$errs;
	    if($rest ne '') {
		$ARGV[0] = "-$rest";
	    }
	    else {
		shift(@ARGV);
	    }
	}
    }
    $errs == 0;
}

1;
SHAR_EOF
chmod +x 'getopts.pl'
fi # end of overwriting check
if test -f 'cbreak2.pl'
then
	echo shar: will not over-write existing file "'cbreak2.pl'"
else
cat << \SHAR_EOF > 'cbreak2.pl'
$sgttyb_t   = 'C4 S';

sub cbreak {
    &set_cbreak(1);
}

sub cooked {
    &set_cbreak(0);
}

sub set_cbreak {
    local($on) = @_;
    do 'sys/ioctl.pl' || die "Can't do sys/ioctl.pl. Stopped";

    ioctl(STDIN,$TIOCGETP,$sgttyb)
        || die "Can't ioctl TIOCGETP: $!";

    @ary = unpack($sgttyb_t,$sgttyb);
    if ($on) {
        $ary[4] |= $CBREAK;
        $ary[4] &= ~$ECHO;
    } else {
        $ary[4] &= ~$CBREAK;
        $ary[4] |= $ECHO;
    }
    $sgttyb = pack($sgttyb_t,@ary);
    ioctl(STDIN,$TIOCSETP,$sgttyb)
            || die "Can't ioctl TIOCSETP: $!";
}

1;
SHAR_EOF
chmod +x 'cbreak2.pl'
fi # end of overwriting check
if test -f 'time.h'
then
	echo shar: will not over-write existing file "'time.h'"
else
cat << \SHAR_EOF > 'time.h'
if (!defined &_TIME_) {
    eval 'sub _TIME_ {1;}';
    eval 'sub DST_NONE {0;}';
    eval 'sub DST_USA {1;}';
    eval 'sub DST_AUST {2;}';
    eval 'sub DST_WET {3;}';
    eval 'sub DST_MET {4;}';
    eval 'sub DST_EET {5;}';
    eval 'sub DST_CAN {6;}';
    eval 'sub DST_GB {7;}';
    eval 'sub DST_RUM {8;}';
    eval 'sub DST_TUR {9;}';
    eval 'sub DST_AUSTALT {10;}';
    eval 'sub timerisset {
        local($tvp) = @_;
        eval "(($tvp)->&tv_sec || ($tvp)->&tv_usec)";
    }';
    eval 'sub timercmp {
        local($tvp, $uvp, $cmp) = @_;
        eval "(($tvp)->&tv_sec $cmp ($uvp)->&tv_sec || ($tvp)->&tv_sec == ($uvp)->&tv_sec && ($tvp)->&tv_usec $cmp ($uvp)->&tv_usec)";
    }';
    eval 'sub timerclear {
        local($tvp) = @_;
        eval "($tvp)->&tv_sec = ($tvp)->&tv_usec = 0";
    }';
    eval 'sub ITIMER_REAL {0;}';
    eval 'sub ITIMER_VIRTUAL {1;}';
    eval 'sub ITIMER_PROF {2;}';
    if (!defined &KERNEL) {
	do 'time.h' || die "Can't include time.h: $!";
    }
}
1;
SHAR_EOF
fi # end of overwriting check
if test -f 'errno.ph'
then
	echo shar: will not over-write existing file "'errno.ph'"
else
cat << \SHAR_EOF > 'errno.ph'
sub EPERM {1;}
sub ENOENT {2;}
sub ESRCH {3;}
sub EINTR {4;}
sub EIO {5;}
sub ENXIO {6;}
sub E2BIG {7;}
sub ENOEXEC {8;}
sub EBADF {9;}
sub ECHILD {10;}
sub EAGAIN {11;}
sub ENOMEM {12;}
sub EACCES {13;}
sub EFAULT {14;}
sub ENOTBLK {15;}
sub EBUSY {16;}
sub EEXIST {17;}
sub EXDEV {18;}
sub ENODEV {19;}
sub ENOTDIR {20;}
sub EISDIR {21;}
sub EINVAL {22;}
sub ENFILE {23;}
sub EMFILE {24;}
sub ENOTTY {25;}
sub ETXTBSY {26;}
sub EFBIG {27;}
sub ENOSPC {28;}
sub ESPIPE {29;}
sub EROFS {30;}
sub EMLINK {31;}
sub EPIPE {32;}
sub EDOM {33;}
sub ERANGE {34;}
sub EWOULDBLOCK {35;}
sub EINPROGRESS {36;}
sub EALREADY {37;}
sub ENOTSOCK {38;}
sub EDESTADDRREQ {39;}
sub EMSGSIZE {40;}
sub EPROTOTYPE {41;}
sub ENOPROTOOPT {42;}
sub EPROTONOSUPPORT {43;}
sub ESOCKTNOSUPPORT {44;}
sub EOPNOTSUPP {45;}
sub EPFNOSUPPORT {46;}
sub EAFNOSUPPORT {47;}
sub EADDRINUSE {48;}
sub EADDRNOTAVAIL {49;}
sub ENETDOWN {50;}
sub ENETUNREACH {51;}
sub ENETRESET {52;}
sub ECONNABORTED {53;}
sub ECONNRESET {54;}
sub ENOBUFS {55;}
sub EISCONN {56;}
sub ENOTCONN {57;}
sub ESHUTDOWN {58;}
sub ETOOMANYREFS {59;}
sub ETIMEDOUT {60;}
sub ECONNREFUSED {61;}
sub ELOOP {62;}
sub ENAMETOOLONG {63;}
sub EHOSTDOWN {64;}
sub EHOSTUNREACH {65;}
sub ENOTEMPTY {66;}
sub EPROCLIM {67;}
sub EUSERS {68;}
sub EDQUOT {69;}
sub ESTALE {70;}
sub EREMOTE {71;}
sub ENOSTR {72;}
sub ETIME {73;}
sub ENOSR {74;}
sub ENOMSG {75;}
sub EBADMSG {76;}
sub EIDRM {77;}
sub EDEADLK {78;}
sub ENOLCK {79;}
sub ENONET {80;}
sub ERREMOTE {81;}
sub ENOLINK {82;}
sub EADV {83;}
sub ESRMNT {84;}
sub ECOMM {85;}
sub EPROTO {86;}
sub EMULTIHOP {87;}
sub EDOTDOT {88;}
sub EREMCHG {89;}
1;
SHAR_EOF
fi # end of overwriting check
cd ..
#	End of shell archive
exit 0