Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: utzoo!utgpu!water!watnot!watmath!clyde!rutgers!lll-lcc!well!hoptoad!gnu
From: g...@hoptoad.UUCP
Newsgroups: net.sources
Subject: uuslave (public domain uucp), version hoptoad-1.11, part 1 of 2
Message-ID: <1925@hoptoad.uucp>
Date: Tue, 24-Mar-87 03:57:14 EST
Article-I.D.: hoptoad.1925
Posted: Tue Mar 24 03:57:14 1987
Date-Received: Thu, 26-Mar-87 01:19:08 EST
Organization: Nebula Consultants in San Francisco
Lines: 2257

Here is part 1 of the latest uuslave distribution.  Have fun...

	John "Use the Source, Luke" Gilmore

: To unbundle, sh this file
echo README
cat >README <<'@@@ Fin de README'
	Sketchy documentation for uuslave

	John Gilmore
	23 March 1987

This directory contains source for uuslave, a freeware program that
implements the Unix uucp file transfer protocol.

Uuslave is not derived from any AT&T or Unix code.  See the file
ATT.CERTIFICATION in this directory.  Uuslave can be freely given to
anyone, whether they have a Unix license or not.

Currently uuslave consists of a single source file, uuslave.c.  There
are two more files for MSDOS systems, comport.h and comport.asm, which
contain an interrupt-driven routine for handling the comm port.  The
system-dependent parts of other uuslave ports will be broken out into
separate files in the future, to make porting easier.


Compiling uuslave

An utterly trivial Makefile is included.  You basically just compile
uuslave.c and produce a runnable program.  For msdos, you have to
assemble comport.asm and link with it too.  You can define preprocessor
variables to get it to compile for these environments:

	BSD	Berkeley Unix
	SYSV	Unix System V
	CPM	CP/M-80
	MSDOS	Mess-Dos
	ST	Atari ST

It also has some other options:

	COMPORT	Use Tim Pozar's interrupt driven I/O routines under MSDOS
	DEBUG	Print out reams of debugging info
	LOG	Log each file transferred, like Unix uucp does, in LOGFILE
	SUBDIR	Keep received files in subdirectories like 4.2BSD uucp does


Running uuslave

Uuslave can be run in two modes.  One way is designed for small micros
and such; uuslave will sit and wait for someone to call on the modem,
and will then prompt them with "login: " and "Password:" and check the
answers.  When another machine calls in and supplies the right login
and password, it will run the uucp protocol to talk to that machine.
Then it hangs up the phone and goes back to listening for another call.
You get this mode by giving uuslave an argument, which is the device
name of the serial port that it should listen on.

The other mode is for larger machines where there already exist programs
to listen for callers on the phones.  When such a program decides that
the caller is a uucp machine, it can run uuslave with no arguments.  
Uuslave will immediately begin the uucp protocol negotiations on its
standard input.  When it is done transferring files, it will hang up
the phone and exit.  In this role, it acts much like the Unix "uucico"
program which implements uucp, and which is called when another machine
logs in to your machine with a login like "uucp" or "Uhoptoad".

When running with no arguments, uuslave will change directories to
a standard place (/usr/spool/uucp on Unix systems) and will leave its
debugging logs in "uuslave.log" in that directory.


For hints on debugging and porting uuslave, see the file PORTING.

Copyright 1987 John Gilmore; you can redistribute only if your recipients can.
{sun,ptsfa,lll-crg,ihnp4,ucbvax}!hoptoad!gnu	       g...@ingres.berkeley.edu
@@@ Fin de README
echo ATT.CERTIFICATION
cat >ATT.CERTIFICATION <<'@@@ Fin de ATT.CERTIFICATION'
Return-Path: <ihnp4!attunix!gcss20!gcdwf>
Date: Mon, 23 Mar 87 23:49:43 PST
From: ihnp4!attunix!gcss20!gcdwf
Message-Id: <8703240749.AA08...@hoptoad.uucp>
Apparently-To: hoptoad!gnu

John Gilmore,

Listed below is the source code for uuslave that you electronically
mailed to me on February 20, 1987. Our product management personnel
have reviewed this code and have determined that it was not derived
from source code from versions of our UNIX(r) operating system. If
you have any further questions, please feel free to call me at
1-800-828-UNIX.


David W. Frasure
AT&T UNIX Software Licensing
@@@ Fin de ATT.CERTIFICATION
echo PORTING
cat >PORTING <<'@@@ Fin de PORTING'
	Porting hints for uuslave

	John Gilmore
	23 March 1987

Read the uucp protocol documentation.  It's vital for understanding
what this code is doing down in the guts.  The best part is Greg
Chesson's paper explaining the packet protocol (packet.driver.ms),
which you can format with "nroff -ms packet.driver.ms" or "troff -ms
packet.driver.ms".  You can also just read it as-is, though you
have to ignore a lot of formatting commands.

Before doing much work with uuslave, I found it best to print it out
and actually read it; it has formfeeds and block comments at strategic
points to make it more readable.  It currently runs about 45 pages.
Mark up your listing where you have questions or comments, and later,
go through it and send them to me.  Or edit in more comments and
questions about unclear places as you port it.  As I merge your diffs
into the master source, I'll either leave the questions, or answer them
with more comments there.

Porting the code to a Unix system should be little effort.  Porting
it to another operating system should be doable, but more work.
Most of uuslave is invariant code, but you'll need to hack on the
parts that do serial I/O to the modem, and the parts that talk
to the file system (typically file names need to be munged to fit
in the obscene limitations of the average non-Unix operating system.)

If you want to port uuslave, download it to whatever system you are doing the
port on, and get it to compile.  Don't even make it run.  Then upload
the sources to a Unix system, if necessary, and mail me the diffs that
make it compile OK.  That way I will get a bunch of these diffs and
even if you get hung up in the next step and never send me anything
else, other people trying to port it won't have to straighten out the
include files and such.  Best way to make these diffs is to save a copy
of the uuslave that came with this message (as e.g. uuslave.hop), and use 

	diff -c uuslave.hop uuslave.c > diffs

(or just "diff" if -c doesn't work on your Unix) to generate the diffs.  I
can then use the "patch" program to merge them into my main sources.
If you don't have a Unix diff program, just send me back the FULL
SOURCE; it will be easier and less error prone than me trying to merge
your stuff in by hand.

Once you've sent me back your diffs (or a note saying it compiled
without any changes), go ahead and debug it and hack it and slash it
and make it write bad checks.  Try to resist the temptation to reformat
the whole source into your favorite form -- it makes it very tough to
figure out what it is you changed.  (Suggestions on how to change the
global formatting are welcome, though.)  Also resist putting a fancy
user interface on it (e.g. windows and stuff) -- first, make it run,
then later, make it pretty.

By the way, compiling the code with -DDEBUG or #define DEBUG produces
voluminous, useful, output showing everything that goes over the serial
line and what a lot of it means to uuslave.  I've been compiling with
-DDEBUG, -DBSD (for BSD Unix), -DLOG (for code that logs calls like
Unix uucp does, to the file LOGFILE), and -DSUBDIR (for a file name
munging hack that makes it compatible with 4.2BSD and Sun Unix uuxqt,
when both are running on the same machine).  You'll probably want to
add #ifdef's for your machine dependent changes.  You'll see where I've
put blocks of code that are different for each type of machine -- you
can just add a block for your machine next to all of mine, remove my 
-DBSD, and compile with -DFOOBAR if your machine or OS is called FOOBAR.

Once you have it compiled on the target system, run it with a 1200 baud
terminal hooked up to the serial port it is listening on.  Try typing
the "uucp" login and such to see that that actually works.  (Look at
the strings msgo0-5 and msgi0-4.)  You won't be able to test the packet
protocol with a terminal, but you can test the serial I/O code and the
timeouts, which will probably be the hardest part of this port.  (It
should time out after 60 seconds of no-data-received and go back to
waiting for someone to log in.)  Make sure you have this working before
you do the next step.

When you can "log in" to uuslave by typing on a terminal on uuslave's
serial port, STOP THERE!  Upload that ol' source to Unix again (NOT via
uuslave!) and send me more diffs.  RESIST the temptation to have Unix
call it up.  If you get Unix to call, it will fail and then you'll want
to debug it.  I've been through this, I know what it's like!  Send in
your diffs first.

Debugging it with Unix requires a lot more setup.  You'll need a
terminal logged into Unix, so you can watch what Unix is doing, and a
link between your micro and the Unix system (either a hardwired serial
line, or a dialout modem on the Unix machine and a dialin modem on the
micro).  Also, having access to the console of the micro so you can see
the debug output, recompile, etc is essential.  Having all this stuff
in front of you, rather than in different rooms, is very useful.

Once you've set this up, you can get it to actually work with a Unix
system.  Leave uuslave running on the micro, and get the Unix machine
to call it up -- talk to the Unix system administrator about how to
run uucp on your Unix machine.  The administrator will need to add a
line to your L.sys or Systems file to tell uucp how to call your micro.
Here is an L.sys line that works to talk to it:

uuslave Any ACU 1200 5551212 in: uucp word: s8000

Insert your micro's modem phone # in the place of 5551212.  If you are
using a hardwired line, you'll have to fix this line, and also change
L.devices or a similar file.

On the Unix machine, you can then do

	/usr/lib/uucp/uucico -r1 -suuslave -x9 &

to cause it to call the micro and log what it sees at your terminal.
(-r1 says uucico should be the master; -suuslave says which system to
call; -x9 sets the debug level to 9, meaning "tell me everything".)
Get it to where Unix can "log in" to the micro and they can
send INITA, INITB, INITC, and then a few data packets negotiating
about hanging up the phone (sending "H" and "HY" packets), then work
on being able to move files.  See the "uucp(1)" man page of your Unix
system, and talk to the system administrator about how uucp works.

Once it can call in, talk, and hang up, you should queue up a file to
be sent from your Unix system, and have it call your program again.
See if the file gets there, etc.  Do checksums on the file before and
after.  Transfer the file back to Unix (using kermit or xmodem or
something) and compare the two.  Make sure that the file is exactly the
same on both ends.  Then queue up a file to be received from your micro
to your Unix system and call up uuslave to try that.  When both work,
great!  SEND ME MORE DIFFS and buy champagne.  You have a uuslave that
actually can move files in the presence of no errors on the phone
line.

When you've done that and sent the diffs, try plugging in another phone
on the line it's using, while it moves a file, and pushing the
touchtone buttons or drumming your fingers on the microphone, or
otherwise making noise on the line so it will have to retry a few
times.  Make sure it doesn't get stuck anywhere when you do this; it
should pause a bit, then the Unix side should send another packet and
they should get back into sync.  You can see this happening in the
modem lights, or in the debug output of uuslave.  Do this a few times,
then leave the poor programs alone and let them finish moving that
file.  Then make sure that the file you moved over through all that
noise came out just the same as it started.  I had a bunch of bugs in
this area, you might find some more.  When you have that working (which
might work on the first try, but be sure you injected a lot of noise at
random points, and really saw it retry), then send in more diffs!  You
have successfully ported it.

Once you've sent us those diffs, hey, take a break.  If you feel like
enhancing it, go ahead, but don't feel obliged.  You will have done the
hard part and others can carry on from there if necessary.  Now would be
the time to make a window interface -- but realize that this program
will probably end up running without human intervention most of the
time.  It's supposed to be quiet and do its job, leaving the interaction
with the user to other programs.  Better to make it dial out, or run
full duplex, or something.  Look in the file BUGS for suggested things
to work on.

You can search for the strings FIXME in the sources.  I've marked various
places with it, which I noticed will need further work.  Feel free
to work on these places and send me back the changes, or to add more
FIXME comments about bad or fragile code that you notice.

I have marked my sources as copyright by me, distribution via the GNU
General Public License.  If you give sources out, with no conditions,
you have satisfied the license.  If you never give out sources or
binaries to anybody else, you have also satisfied the license.  If you
do something else, get a copy of the license, by sending mail to

	info-gnu-requ...@prep.ai.mit.edu
or	hoptoad!prep.ai.mit.edu!info-gnu-request

asking for one.  (It comes as file dist/COPYING in a GNU Emacs release,
if you have one of those lying around).  If anyone has problems with
this, let me know, or talk about it on the mailing list.  Up to now my
stuff has been PD, but having the ownership of the Usenet netnews
software snatched out of the public domain has made me more cautious
(see news.software.b).  I expect to sign over my copyright to the Free
Software Foundation if&when they start distributing uuslave (which by then
will probably be called gnuucp or something).

This code has been running production between hoptoad and lll-crg for
several weeks, and has also been successfully run in test mode on MSDOS.
It still needs a lot of work on portability and readability.  Do what
you can in these areas as you read it and hack on it.

Good luck...

	John Gilmore

Copyright 1987 John Gilmore; you can redistribute only if your recipients can.
{sun,ptsfa,lll-crg,ihnp4,ucbvax}!hoptoad!gnu	       g...@ingres.berkeley.edu
@@@ Fin de PORTING
echo BUGS
cat >BUGS <<'@@@ Fin de BUGS'
Sun uucp sends a "dc" long packet after receiving a file but before
sending the "CY" or "CN" packet.  Why?  It's not documented...
(we cope)

Make it terminate under fewer conditions -- go around a few more times.

Make uuslave use a short timeout and re-transmit its last packet,
rather than using the long "give up" timeout only.  Currently we are
depending on uucp on the other end to time out and retransmit to
joggle our elbow.

Logging doesn't log the other guy's hostname.  How do we find it out?
(the line we match with "L*" actually is "Lhostname" containing the
other guy's hostname.  Grab it!)

Logging stuffs whole command packet out there.

Logging needs work for failed xfers, see what uucp does.

Logging needs work for dropped connections, ditto....

Uuslave should exec uuxqt when it's done a single connection.

Merge in MSDOS file munging.

Break out system dependent modules to separate files.

-----

Feature requests:

Ability to look in the queue of outgoing things for a host, and act as
master to transfer those things in the queue.  To become the master,
you answer "No" to the "do you want to hang up?" question, then start
sending commands.

Once the above is done, add the ability to dial out to a particular
system and transfer any files with it that are in the queue.

Once the above is done, add the ability to figure out which systems
need to be called, and call them, one by one.  This is probably best
done by a separate program or shell script that calls uuslave.

Find or write public domain programs that implement:
	uuxqt -- execute the commands in received X. files, e.g. rmail, rnews
	uuclean -- clean out the queues of old stuff
	uucp -- queue up a file copying operation
	uux -- queue up a "remote command execution" operation
	uuls -- list the queue (this one is PD)
	uusnap -- snapshot the queue (is this PD?  Who wrote it?)

Implement a window size greater than 1, for effective use of the phone
bandwidth.  This requires being able to receive while you are sending,
which may require odd stuff on many micros.

Improve the logging, so a human can actually see what uuslave is doing.
Currently it's set up to match what uucp does, which is horrible.
But doing it this way doesn't break old awk scripts that parse the
uucp logs.

Instrument uuslave so it records in the logs how many packet retransmissions
it saw, how long each file transfer took and how many bytes were moved,
how much total time from start to finish, and total number of bytes
moved start to finish, and other interesting numbers.  This kind of data
is vital for improving the speed of uuslave.

Profile uuslave and see where it is spending most of its time.  Fix up
those parts to make it run faster and take less system resources.
I know the first place to look -- xgetc().
@@@ Fin de BUGS
echo Makefile
cat >Makefile <<'@@@ Fin de Makefile'
# Fix this sometime soon!

DEFS =	-DBSD -DDEBUG -DSUBDIR -DLOG
SHAR1 =	README ATT.CERTIFICATION PORTING BUGS Makefile uuslave.c
SHAR2 =	packet.driver.ms comport.h comport.asm

uuslave: uuslave.c
	cc -o uuslave -g uuslave.c $(DEFS)

lint:
	lint -hbxn $(DEFS) uuslave.c

shar:
	shar $(SHAR1) >uuslave.shar.1
	shar $(SHAR2) >uuslave.shar.2
@@@ Fin de Makefile
echo uuslave.c
cat >uuslave.c <<'@@@ Fin de uuslave.c'
/*
 * @(#)uuslave.c	Version hoptoad-1.11	87/03/23
 *
 * (C) Copyright 1987 by John Gilmore.
 * Copying and use of this program are controlled by the terms of the Free
 * Software Foundation's GNU Emacs General Public License.
 *
 * Derived from:
 * i[$]uuslave.c	1.7 08/12/85 14:04:20
 * which came from the ACGNJ BBS system at +1 201 753 9758.  Original
 * author unknown.
 */

char version[] = "Version hoptoad-1.11";

/*
 * Modified to compile on BSD systems by John Gilmore (hoptoad!gnu), Jan 1987.
 * 
 * Separated into layers and acknowledgement (middle) layer added, Feb 1987,
 * by John Gilmore.
 *
 * Incorporates a few of the "very extensive and brutal hacks by Marcus J.
 * Ranum",  posted to the net 6 Feb 1987 by mjra...@osiris.uucp.
 *
 * Modified to compile on CP/M by Larry Marek (ihnp4!strg8!larry), March '87.
 *
 * Modified to compile with Microsoft C 4.0 by Tim Pozar (hoptoad!pozar),
 * February and March of 1987:
 *
 *    ver 0.1     11.Mar.1987
 *    Since there isn't any bps rate detection at this point, the machine will 
 *    only work with whatever it was last set with.  Also, PC-DOS and IBM's
 *    serial port seem to have a slight problem with buffering and PC-DOS's
 *    stupid way of handling "read file" errors with the com ports.  PC-DOS
 *    isn't graceful in it's handling and will expect an operator to be at
 *    the physical system console to tell it to "retry".  BPS rate detection
 *    and buffered I/O will be the next code to be plugged into this beast. 
 *    
 *    Basically this guy shouldn't be released yet...
 *
 *    ver 0.2     13.Mar.1987
 *    Hacked to support the COMPORT.ASM/OBJ/H to speed up handshaking and 
 *    char interchange without DOS problems.
 *  
 * Changes from trwrb!sansom (Richard Sansom), 24 Feb '87, to get it to
 * compile on the Atari ST.
 *
 * Above changes merged back into master sources by John Gilmore, 23Mar87.
 *
 */

/*

This program implements the uucp (Unix-to-Unix CoPy) protocol as a
slave (the recipient of a phone call from another Unix system).  This
protocol is used to transfer mail, files, and Usenet news from Unix
machine to Unix machine.  UUCP comes with Unix (unless you get a
sleazeoid version like Xenix, where they charge you extra for it).  You
can buy a commercial program for MSDOS, called UULINK, which also
implements this protocol.  UULINK costs $300 and you don't get sources,
though.

The protocol requires a full 8-bit data path with no characters inserted
or deleted (e.g. ^S and ^Q are used as DATA characters).  Simple serial
ports and modems do this; most complicated networks do not, at least without
setting up odd modes and such.  Telenet's PC Pursuit works fine though.

The basic flow of the protocol is that the calling machine will send down
a line of text saying what it wants to do (send a file, receive a file,
or hang up).  (The lines of text are encapsulated into packets; see below.)
The called machine responds with a "yes" or "no" answer, and if the answer
was yes, it sends or receives the file.  Files are terminated with a
packet containing 0 bytes of data.  Then the system that received the file
sends a "copy succeeded" or "copy failed" line to the other end, and 
they go back to "what do we do now".  A request to hang up should be
answered "no" if the called machine has some mail or files it wants to
send to the calling machine; the two machines reverse roles and the calling
machine goes into "what do we do now".  If a hangup request is answered "yes",
the call is terminated.

The data flow described above is actually sent in packets containing
checksums and acknowledgements.  Each packet can either hold a short
control message, e.g. an ack, or a data block.  The data blocks are
numbered with a 3-bit counter, and sent with a checksum.  If the sender
has not received an acknowledgement for a data block within a certain
time, it retransmits the block.  The size of a data block is negotiated
at the start of a call.  To send a block with fewer bytes, a "short
data" block is sent, which is just as big as a "long data" block, but
contains a 1- or 2-byte count of "how many bytes in this block are just
padding".  This is a cute trick since it always works (e.g. if you want
to send 1023 out of 1024 bytes, you only need one byte for the count;
while if you want to send 1 byte out of 1024 then you have enough space
for the count to be 2 bytes).

The short control messages are used to start the call and negotiate the
packet size and the "window size", to acknowledge or reject packets,
and to terminate the packet protocol at the end of a call.  The window
size is how many packets one side can send before it will stop and wait
for an acknowledgement from the other side.  A window size of 1 makes
for a half-duplex protocol (which is what uuslave currently
implements), but also makes it easy to implement on micros that don't
handle serial lines with interrupts.  In window 1, you just keep
sending the same packet until the other side acknowledges it.  Unix
always uses a window size of 3, which is the max that can be dealt with
given the 3-bit packet numbers (for reasons that would take more space
than I want to spend here).  This gives much better throughput, but
requires full duplex serial port handling and more complicated
acknowledgement strategies.

At the low level, full 8-bit bytes are sent out and received on an async serial
port.  The received data is scanned for a DLE (hex 10) which indicates the
start of a packet, and the next 5 bytes are read and checked.  If they
pass, this is a good packet and it is acted upon.  (If it's a data
packet, we have to read in and check the data part too.)  If the checks
fail, all the bytes read so far should be scanned for another DLE.

Include files for various supported systems:
Note that NAMESIZE should be the max length of a file name, including
all its directories, drive specifiers, extensions, and the like.
E.g. on a Unix with 14-char file names, NAMESIZE is several hundred
characters, since the 14-char names can be nested.
*/

#ifdef BSD
/* Unix Berserkeley systems */
#include <stdio.h>
#include <ctype.h>
#include <sgtty.h>
#include <sys/param.h>
#include <sys/file.h>
#include <sys/time.h>
#include <signal.h>
#include <setjmp.h>

#define	UNIX
#define	NAMESIZE	MAXPATHLEN
#endif

#ifdef SYSV
/* Unix System V */
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <termio.h>
#include <signal.h>
#include <setjmp.h>

#define	UNIX
#endif

#ifdef UNIX
/* Stuff common to all Unix systems */
#define	MULTITASK
#define	STDIN		0
#define	SPOOLDIR	"/usr/spool/uucp"
#define	PUBDIR		"/usr/spool/uucppublic"
#define	LOGFILE		"LOGFILE"
#endif

#ifdef CPM
/* CP/M-80 */
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>

#define	NAMESIZE	50		/* No directories... */
#endif

#ifdef MSDOS
/* Microsoft DOS */
/* Turn on support for the interrupt driven comm port routines */
#define	COMPORT

#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
#include <setjmp.h>
#include <dos.h>
#include <conio.h>
#ifdef COMPORT
#include <comport.h>
#endif

typedef struct timetype {
	unsigned hour;
	unsigned minute;
	unsigned sec;
	unsigned hsec;
} TIME, *TIME_PTR;

#define	NAMESIZE	512		/* Pulled out of a hat */
#endif

#ifdef ST
/* Atari ST */
#include <stdio.h>
#include <ctype.h>
#include <osbind.h>
#include <signal.h>
#include <setjmp.h>

#define O_RDONLY	0	/* for read only open() */
#define AUX		1	/* rs232 port */
#define CON		2	/* console */
#define NAMESIZE	13	/* filename size */
#define CTRL(X)	(X & 037)

#endif

#define	MAGIC	0125252		/* checksum is subtracted from this */

/*
 * What is sent from one machine to the other is a control byte and
 * sometimes a data block following.
 * A packet is a just this, with a frame around the control byte and the
 * data, if any, after the frame.  The frame is 6 bytes long, and looks like:
 *	DLE K C0 C1 C X
 * where:
 *	DLE is a literal ASCII DLE (Data Link Escape) character
 *	K is binary, 9 for a control packet, or the packet size log 2, minus 4
 *		e.g. K = 2 means 64 byte packet since  (K+4) is  6 or 64.
 *						      2         2
 *	C0 and C1 are low, high order checksums for the entire data section, or
 *		are low, high order of (MAGIC minus the control byte).
 *	C is the control byte for the message.
 *	X is the xor of K, C0, C1, and C.
 * If a packet does not satisfy all of the above checks, it is invalid.
 */
#define	DLE	0x10		/* Start of packet indicator */

#define	LENBYTE	1		/* Byte offset from DLE to length */
#define	CBYTE	4		/* Byte offset from DLE to control */
#define	FRAMEBYTE 5		/* Byte offset from DLE to end of frame */

#define	KCONTROL 9		/* K value for control packets */

/*
 * A control byte is split into three bit fields: type, x, and y.
 * 	TT XXX YYY
 * Here are the types:
 */
#define	CONTROL	0		/* Control message */
#define	ALTCHN	1		/* Alternate channel message, unused in UUCP */
#define	LONGDATA	2	/* Long data block -- full size spec'd by K */
#define	SHORTDATA	3	/* Short data block -- first byte or two is
				   count.  Full K-size packet is sent, even
				   though the useful data is shorter. */

char *tt_pr[] = {"CONTROL", "ALTCHN", "LONGDATA", "SHORTDATA"};

/* If TT == CONTROL (also K will == KCONTROL) then
   the x field is: 			and the Y field means: */
#define	CLOSE	1	/* End of communication */
#define	RJ	2	/* Reject packet	last good packet # seen */
#define	SRJ	3	/* Selective reject	seq # of bad packet, resend
			   SRJ is not used by UUCP. */
#define	RR	4	/* Receiver Ready	last good packet # seen */
#define	INITC	5	/* Init phase C		window size to hand sender */
#define	INITB	6	/* Init phase B		max data segment size (K val)
						to hand sender */
#define	INITA	7	/* Init phase A		window size to hand sender */

char *ctrl_pr[] = {"*ZERO*",
	"CLOSE", "RJ", "SRJ", "RR", "INITC", "INITB", "INITA"};

/*
 * If TT == LONGDATA or SHORTDATA then x field is the sequence # of this packet
 * and y field is the last good packet # seen.
 *
 * In both data and RJ/RR packets, the "last good packet # seen" starts off
 * as zero.
 */


/*
 * Timeout for raw characters -- if we don't hear a char within BYTE_TIMEOUT
 * seconds, we assume the other side has gone away.  Has nothing to do with
 * retransmission timeouts (if any!).
 */
#define	BYTE_TIMEOUT	60

#define	MAX_PACKET	4096
#define	SLOP		10		/* Frame header, ctrl, slop */
#define	MAX_FLAGS	40

#ifndef LOG
#define	logit(one, two)	/* Nothing */
#endif

extern int errno;

unsigned char	msgi[MAX_PACKET+SLOP],	/* Incoming packet */
		msgo[MAX_PACKET+SLOP];	/* Outgoing packet */

char	ttynam[NAMESIZE],		/* Name of tty we use as serial port */
	cmnd[8],			/* Trashplace to put command letter */
	srcnam[NAMESIZE],		/* Source file name */
	dstnam[NAMESIZE],		/* Dest file name */
	dskbuf[MAX_PACKET],		/* Disk I/O buffer */
	who[NAMESIZE] = "-",		/* Who sent the file */
	flags[MAX_FLAGS],		/* Flags from file xfer cmd */
	temp[NAMESIZE],			/* Temp file name */
	msgbld[(NAMESIZE*4)+SLOP];	/* Top level message storage */
int	fdtty,				/* Terminal line (to unix uucp) file
					   descriptor */
	logfd,				/* file desc of uucp logfile */
	ourpid = 0,			/* Our process ID */
	firstslave,			/* First packet of slave's session */
	mode,				/* File mode from file xfer cmd */
	msgsize,			/* Size of data part of msg */
	tt, xxx, yyy,			/* Fields from control byte */
	rseq,				/* Last good packet # we received */
	his_rseq,			/* Last good packet # HE received */
	wseq;				/* Next packet # we will send */

int	last_op;			/* Last data op: OP_READ or OP_WRITE */
#define	OP_READ		0
#define	OP_WRITE	1

int	reject;				/* Packet # to reject or NOREJECT */
#define	NOREJECT	-1

#ifndef CPM
jmp_buf alarming;			/* For read timeouts */
#endif

#ifdef BSD
	struct sgttyb atermio, btermio;
#endif
#ifdef SYSV
	struct termio atermio, btermio;
#endif

/* Segments are encoded as (log2 length) - 3, except in INITB packet */
int wndsiz = 1;			/* Ask for window of 1 messages flying */
int segsiz = 2;			/* Ask for 64 byte messages */
int sendseg = 2;		/* Size segments other guy wants to see */
int sendwin = 1;		/* Size window other guy wants to see */

int sendbytes;			/* sendseg, in bytes */

int	segbytes[10] = {	/* K value (encoded segment size) */
		-1,		/* 0 */
		32,		/* 1 */
		64,		/* 2 */
		128,		/* 3 */
		256,		/* 4 */
		512,		/* 5 */
		1024,		/* 6 */
		2048,		/* 7 */
		4096,		/* 8 */
		0,		/* 9 = KCONTROL */
};

#ifdef MSDOS
char hayesinit[] = "\r\r\rATS0=1\r";
#endif

/* We print these prompts */
char msgo0[] = "login: ";
char msgo1[] = "Password:";
char msgo2[] = "\20Shere\0";
char msgo3[] = "\20ROK\0";
char msgo3a[]= "\20Pg\0";
char msgo4[] = "\20OOOOOOO\0";

/* We expect to receive these strings */
char msgi0[] = "uucp\n";
char msgi1[] = "s8000\n";
char msgi2[] = "\20S*\0";
char msgi3[] = "\20Ug\0";
char msgi4[] = "OOOOOO";

/*
 * Basement level I/O routines
 *
 * xwrite() writes a character string to the serial port
 * xgetc() returns a character from the serial port, or an EOF for timeout.
 * sigint() restores the state of the serial port on exit.
 */

#ifdef CPM
#define	abort()	exit(1)
extern xgetc(), xwrite(), sioinit();
#endif

sigint()
{
	/* Restore terminal settings on dialout line */
#ifdef BSD
	ioctl(fdtty, TIOCSETN, &atermio);
        close(fdtty);
#endif

#ifdef SYSV
	ioctl(fdtty,TCSETA,&atermio);
        close(fdtty);
#endif

#ifdef MSDOS
#ifndef COMPORT
        close(fdtty);
#else
        uninit_comm();
        reset_tty();
#endif
#endif
#ifdef ST
	/* No need to do anything here? */
#endif

	exit(0);
}

#ifdef MSDOS
xwrite(fd,buf,ctr)
int fd;
char *buf;
int ctr;
{
#ifndef COMPORT
	return write(fd,buf,ctr);
#else
	int i;

	for (i=0;i<=ctr;i++) {
		outp_char(buf[i]);
	}
	return ctr;
#endif
}
#endif

#ifdef ST
/*
 * xwrite(dummy, buf, n) - write "n" bytes from buffer "buf" to rs232 port.
 */
xwrite(dummy, buf, n)
	int dummy, n;
	char *buf;
{
	register char *c = buf;

	while (n) {
		/* wait for rs232 to be ready */
		while (Bcostat(AUX) == 0)
			;

		/* write next character in buffer */
		Bconout(AUX, c++);
		n--;
	}
}
#endif

#ifdef UNIX
#define	xwrite	write		/* Just use write system call */
#endif

/*
 * Serial port reading routines 
 */

#ifdef UNIX
sigalrm()
{
	longjmp(alarming, 1);
}

/*
 * FIXME:  This is really slow; it does 4 system calls per byte!
 */
xgetc()
{
	char data;
	int status;

	signal(SIGALRM,sigalrm);
	alarm(BYTE_TIMEOUT);
	if (setjmp(alarming) == 0) 
	{
		status = read(fdtty,&data,1);
		alarm(0);
		if (status == 1)	/* the read worked, returning 1 char */
			return(data & 0xFF);
	}
	/* Error on serial port, or timeout. */
	return(EOF);
}
#endif

#ifdef MSDOS
xgetc()
{
	char data;

#ifndef COMPORT
	/* Warning: No timeouts... */
	read(fdtty,&data,1);
	return(data & 0xFF);
#else
	int i;
	unsigned s;
	TIME n;

	i = 0;
	get_time(&n);
	s = n.sec;

	/*
	 * Implement timeouts by staring at the clock while we wait.
	 * When the second hand moves, bump our counter.  This is a lot
	 * easier than figuring out the time when we'd time out (in hours,
	 * minutes, and seconds!) and comparing against that, which is
	 * what people tend to do in Unix where the time is just an integer
	 * number of seconds.
	 */
	while (i < BYTE_TIMEOUT) {
		while (s == n.sec) {
			if(inp_cnt() != 0) {
				data = inp_char();
				return (data & 0xFF);
			}
			get_time (&n);
		}
		s = n.sec;
		++i;
	}
	return(EOF);
#endif
}
#endif /* MSDOS */

#ifdef ST
/*
 * Atari ST routines for reading the comm port.
 *
 * The following routines come by way of J. R. Bammi
 */

long prtime = 0L;			/* present time			*/
long alrmtime = 0L;			/* alarm time			*/
long *hz200 = (long *)0x0004ba;		/* address of system 200 hz clk	*/
jmp_buf abortenv;			/* used to catch user aborts	*/

/*
 * read200hz() - read the system 200 hz clock.
 */
void read200hz()
{
	prtime = *hz200;
}

/*
 * alarm(n) - set the alarm time to n seconds.
 */
void alarm(n)
	unsigned int n;
{
	/* if n != 0, then set the alarm time */
	if (n) {
		Supexec(read200hz);
		alrmtime = prtime + (long)(200 * n);

	/* n == 0, reset alarm time */
	} else
		alrmtime = 0L;
}

/*
 * xgetc() - get a character from the rs232 port, catch timeouts & aborts.
 */
xgetc()
{
	if (setjmp(alarming))
		return(EOF);
	alarm(BYTE_TIMEOUT);
	while (1) {
		/* catch ^C (user abort) */
		if (Bconstat(CON))
			if ((int)Bconin(CON) == CTRL('C'))
				longjmp(abortenv, 1);

		/* check for a char at rs232 port */
		if (Bconstat(AUX)) {
			alarm(0);
			return((int)Bconin(AUX));

		/* no char, check for timeout */
		} else if (alrmtime) {
			Supexec(read200hz);
			if (prtime >= alrmtime)
				longjmp(alarming, 1);
		}
	}
}
#endif

/*
 * Low level output routines.  These send packets without checking
 * whether they got properly received.
 *
 * writeframe():
 *
 * Finish off an outgoing frame in msgo and queue it up to be written
 * to the serial port.
 *
 * This routine is called to send each and every packet.
 */
int
writeframe(cksm)
	int cksm;
{
	
	msgo[0] = DLE;
	msgo[2] = cksm;
	msgo[3] = cksm >> 8;
	msgo[5] = msgo[1] ^ msgo[2] ^ msgo[3] ^ msgo[4];

#ifdef DEBUG
	{
		int tt, xxx, yyy, index, maxlen;

		printf("T ");
		maxlen = segbytes[msgo[LENBYTE]] + 6;
		for (index = 0; index < maxlen; index++)
			printf("%02x  ",msgo[index] & 0xFF);
		putchar('\n');
		tt = msgo[CBYTE] >> 6;
		xxx = (msgo[CBYTE] >> 3) & 7;
		yyy = msgo[CBYTE] & 7;
		if (tt == CONTROL)
			printf("Sent: CONTROL %s %d\n",
				ctrl_pr[xxx], yyy);
		else
			printf("Sent: %s %d %d\n",
				tt_pr[tt], xxx, yyy);
	}
#endif

	/*
	 * In our window=1 implementation, we just queue the packet
	 * up for transmission here (by leaving it in msgo[]).  It
	 * will be written next time we go through inpkt().
	 */
	last_op = OP_WRITE;	/* Remember to avoid overwriting the packet */
	return 0;		/* Never aborts */
}

/* Send an ack */
int
ackmsg()
{

	msgo[1] = KCONTROL;
	if (reject != NOREJECT)
		msgo[4] = (CONTROL << 6) | (RJ << 3) | reject;
	else
		msgo[4] = (CONTROL << 6) | (RR << 3) | rseq;
	reject = NOREJECT;
	return writeframe(MAGIC - msgo[4]);
}


/* Send a control message other than an ack */
int
ctlmsg(byte)
char byte;
{

	msgo[1] = KCONTROL;
	msgo[4] = (CONTROL << 6) | byte;
	return writeframe(MAGIC - msgo[4]);
}

/*
 * Medium level output routine.  This sends a short or long data packet
 * and figures out when to retransmit and/or insert acknowledgements as
 * needed.
 */
sendpacket(s, n, sorl)
	char *s;
	int n;
	int sorl;			/* SHORTDATA or LONGDATA */
{
	int cksm, offset, difflen;

	if (last_op == OP_WRITE) {
		/* Better get the first one sent and ack'd first */
		/* FIXME, this will change for window > 1 */
		if (inpkt()) return 1;
	}

	bzero(msgo+6, sendbytes);
	msgo[1] = sendseg;
	msgo[4] = (sorl << 6) + (wseq << 3) + rseq;

	switch(sorl) {
	case LONGDATA:
		if (n > sendbytes) abort();
		offset = 6;
		break;

	case SHORTDATA:
		difflen = sendbytes - n;
		if (difflen < 1) abort();
		offset = 7;
		if (difflen <= 127) {
			msgo[6] = difflen;	  /* One byte count */
		} else {
			msgo[6] = 128 | difflen;  /* low byte, with 0x80 on */
			msgo[7] = difflen >> 7;   /* High byte */
			offset = 8;
		}
	}

	bcopy(s, msgo+offset, n);		/* Move the data */

	cksm = MAGIC - (chksum(&msgo[6], sendbytes) ^ (0377 & msgo[4]));
	wseq = (wseq + 1) & 7;			/* Bump sent pkt sequence # */
	return writeframe(cksm);
}

/*
 * Medium level input routine.
 *
 * Look for an input string for the send-expect sequence.
 * Return 0 for matching string, 1 for timeout before we found it.
 * FIXME:  we only time out if the other end stops sending.  If it
 *	   keeps sending, we keep listening forever.
 */
instr(s,n)
register char *s;
register int n;
{
	int data,count,j;
	register int i;

	count = 0;
#ifdef DEBUG
	printf("Expecting ");
	for (i = 0; i < n; i++)
		printf("%02x%c ",s[i] & 0xFF, isprint(s[i])? s[i]: ' ');
	printf("\nR ");
#endif
	while ((data = xgetc()) != EOF)
	{
		msgi[count++] = data & 0x7F;
#ifdef DEBUG
		printf("%02x%c ",msgi[count-1],
			 isprint(msgi[count-1])? msgi[count-1]: ' ');
#endif
		if (count >= n)
		{
			for (i = n - 1, j = count - 1; i >= 0; i--, j--)
				if (*(s+i) == '*' || *(s+i) != msgi[j])
					break;
			if (i < 0 || *(s+i) == '*')
			{
#ifdef DEBUG
				putchar('\n');
#endif
				return(0);
			}
		}
	}
#ifdef DEBUG
	putchar('\n');
#endif
	msgi[count] = 0;
	return(1);
}

/*
 * Medium level input routine.
 *
 * Write a packet to the serial port, then read a packet from the serial port.
 * Return 1 if other side went away, 0 if good packet received.
 *
 * With window size of 1, we send a packet and then receive one.
 * FIXME, when we implement a larger window size, this routine will become
 * more complicated and will callers will not be able to depend on msgo[]
 * being sent and acknowledged when it returns.
 */
int
inpkt()
{
	int data,count,need;
	register int i;
	short pktsum, oursum;
	int status;
	/*
	 * Next vars are for re-queueing received chars to rescan them
	 * for a valid packet after an error.
	 */
	int queued = -1;  /* <0: off, 0: just finished, >0: # chars pending */
	unsigned char *qp;
	unsigned char qbuf[sizeof msgi];	/* This can be static if 4K
						   on the stack is too much */
#	define	bad(str) {printf str; goto oops; }

	if (firstslave) {
		firstslave = 0;
		goto again;
	}

xmit:
	i = segbytes[msgo[LENBYTE]] + 6;
	status = xwrite(fdtty,msgo,i);
	if (status != i) {
#ifdef DEBUG
		printf("xmit %d bytes failed, status %d, errno %d", 
			i, status, errno);
#endif
		return 1;		/* Write failed */
	}

again:
	count = 0;

#ifdef DEBUG
	printf("R ");
#endif

	while (1) {
		if (queued >= 0) {
			/*
			 * Process some stuff from a string.
			 * If we just finished the last char queued, and
			 * we are still scanning for a DLE, re-xmit our
			 * last packet before we continue reading.
			 * On the other hand, if we have a valid packet
			 * header accumulating, just keep reading the serial
			 * port.
			 */
			if (--queued < 0)
				if (count == 0) {
#ifdef DEBUG
					printf("End queue.  Re-xmit.\n");
#endif
					goto xmit; /* No packet comin' in */
				} else {
#ifdef DEBUG
					printf("End queue.  Keep reading\n");
#endif
					goto readser; /* Seems to be sumpin' */
				}
			data = *qp++;		/* Just grab from queue */
		} else {
readser:
			data = xgetc();
			if (data == EOF) break;
		}
#ifdef DEBUG
		printf("%02x%c ",data & 0xFF, isprint(data)? data: ' ');
#endif
		switch (count)
		{
		case 0:
			/* Look for DLE */
			if (data == DLE)
				msgi[count++] = DLE;
			break;

		case LENBYTE:
			/* Check length byte */
			if (data > KCONTROL || data == 0)
				bad(("packet size"));
			if (segbytes[data] > MAX_PACKET) {
				bad(("packet too long for buffer"));
		oops:
				printf(" bad in above packet\n");

				/* FIXME, decode packet header here,
				   if enough of it has come in. */

				/* See if any DLEs in the bad packet */
				/* Skip 0, we know that's a DLE */
				for (i = 1; i < count; i++) {
					if (msgi[i] == DLE) {
						/* Reprocess from the DLE.
						 * if queued, back up the q.
						 * if not, make one.
						 */
						if (queued) {
							queued += count - i;
							qp -= count - i;
						} else {
							bcopy(msgi+i, qbuf, 
								count - i);
							qp = qbuf;
							queued = count - i;
						}
#ifdef DEBUG
						printf("Rescan input\n");
#endif
						goto again;
					}
				}

				if (queued >= 0) {
#ifdef DEBUG
					printf("Continue scan\n");
#endif
					goto again;
				} else {
#ifdef DEBUG
					printf("Re-xmit previous packet\n");
#endif
					goto xmit;	/* Xmit then rcv */
				}
			}
			msgi[count++] = data;		/* Save it */
			msgsize = segbytes[data];	/* Save Packet size */
			need = 6 + msgsize;
			break;

		case CBYTE:
			/* Break up control byte as well as storing it */
			msgi[count++] = data;		/* Save it */
			tt = (data >> 6) & 3;
			xxx = (data >> 3) & 7;
			yyy = data & 7;

			/* Now check it a bit */
			switch (tt) {		/* Switch on msg type */
			case CONTROL:
				/* Control msg must have KCONTROL size */
				if (msgsize != 0) bad(("K versus Control"));
				/* We don't implement SRJ, nor does Unix */
				switch (xxx) {
				case SRJ:
					bad(("SRJ received"));
				case RJ:
				case RR:
					if (yyy != (7 & (wseq - 1)))
						bad(("didn't ack our pkt"));
				}
				break;
			
			case ALTCHN:
				bad(("ALTCHN received")); /* Unsupported */

			case SHORTDATA:
			case LONGDATA:
				if (msgsize == 0) bad (("KCONTROL with data"));
				if (((xxx - rseq) & 7) > wndsiz) {
					/* Atari ST cpp has problems with
					 * macro args broken across lines?
					 * That's why funny indent here
					 */
bad (("data out of window, xxx=%d rseq=%d", xxx, rseq));
				}
				/* FIXME, below enforces window size == 1 */
				/* Note that this is also how we guarantee
				   that msgo has been received OK by the time
				   we exit inpkt() too.  Don't change it unless
				   you know what you are doing. */
				if (yyy != (7 & (wseq - 1)))
					bad(("didn't ack our pkt"));
				break;
			}
			break;

		case FRAMEBYTE:
			/* See whole frame, check it a bit. */
			msgi[count++] = data;
			if (data != (msgi[1] ^ msgi[2] ^ msgi[3] ^ msgi[4]))
				bad(("frame checksum"));
			pktsum = msgi[2] + (msgi[3] << 8);

			if (tt == CONTROL) {
				/* Check checksums for control packets */
				oursum = MAGIC - msgi[4];
				if (pktsum != oursum)
					bad(("control checksum"));
				/*
				 * We have a full control packet.
				 * Update received seq number for the ones
				 * that carry one.
				 */
				switch (xxx) {
				case RJ: case RR:
					if (((wseq - yyy) & 7) > sendwin) {
bad (("RJ/RR out of window, yyy=%d wseq=%d", yyy, wseq));
					}
					his_rseq = yyy;
				}
				goto done;
			} else {
				/*
				 * Received frame of data packet.
				 *
				 * Now that the checksum has been verified,
				 * we can believe the acknowledgement (if
				 * any) in it.
				 */
				if (((wseq - yyy) & 7) > sendwin) {
bad (("data ack out of window, yyy=%d wseq=%d", yyy, wseq));
				}
				his_rseq = yyy;
			}
			break;

		default:
			msgi[count++] = data;
			if (count >= need) {
				/* We have received a full data packet */
				oursum = MAGIC - (chksum(&msgi[6], sendbytes)
						  ^ (0377 & msgi[4]));
				if (pktsum != oursum) {
					/* Send a reject on this pkt */
					reject = xxx - 1;
bad(("\ndata checksum in packet %x, ours=%x", pktsum, oursum));
				}
				/* FIXME, this may change for window>1 */
				if (xxx != (rseq+1)%8 ) {
bad(("Not next packet xxx=%d rseq=%d", xxx, rseq));
				}
				rseq = xxx;	/* We saw this pkt OK */
		done:
#ifdef DEBUG
				putchar('\n');
				if (tt == CONTROL)
					printf("Rcvd: CONTROL %s %d\n",
						ctrl_pr[xxx], yyy);
				else
					printf("Rcvd: %s %d %d\n",
						tt_pr[tt], xxx, yyy);
#endif
				last_op = OP_READ;
				return(0);
			}
			break;
		}
	}
#ifdef DEBUG
	printf(" EOF\n");
#endif
	return(1);
}

int
chksum(s,n)
register unsigned char *s;
register n;
{
	register short sum;
	register unsigned short t;
	register short x;

	sum = -1;
	x = 0;
	do {
		if (sum < 0)
		{
			sum <<= 1;
			sum++;
		}
		else
			sum <<= 1;
		t = sum;
		sum += *s++ & 0377;
		x += sum ^ n;
		if ((unsigned short)sum <= t)
			sum ^= x;
	} while (--n > 0);

	return(sum);
}

/*
 * Medium level packet driver input routine.
 *
 * Read a data packet from the other side.  If called twice in succession,
 * we send an ack of the previous packet.  Otherwise we tend to piggyback
 * the acks on data packets.
 *
 * Result is 0 if we got a data packet, 1 if we got some other kind, or
 * a hangup timeout.
 */
int
indata()
{

	while (1) {
		if (last_op == OP_READ) {
			ackmsg();		/* Send an ack */
		}
		if (inpkt()) return 1;

		switch (tt) {
		case ALTCHN:
			return 1;		/* Unsupported - yet */

		case LONGDATA:
		case SHORTDATA:
			/*
			 * We got a data packet.  That's what we want,
			 * so return.
			 */
			return 0;		/* We are done. */

		case CONTROL:	
			switch (xxx) {
			default:
				return 1;	/* Bad packet type */

			case RJ:		/* Reject prev pkt */
			case RR:		/* OK but no data */
				break;		/* Ack and try again */
			}
		}
	}
}

/*
 * Open a conversation in the g protocol.  Medium level routine.
 * Returns 0 for success, 1 for failure.
 */
int
gpro_open(mastermode)
	int mastermode;
{
	int tries = 0;
	int expect = 0;
	static int which[] = {INITA, INITB, INITC};

	/* initialize protocol globals, e.g. packet sequence numbers */

	rseq = 0;		/* Last good packet # we have seen from him */
	wseq = 1;		/* Next packet # we will send */
	his_rseq = 0;		/* Last good Packet # he has seen from us */
	reject = NOREJECT;	/* Don't reject first packet */
	firstslave = mastermode? 0: 1;	/* About to do first slave packet? */

	if (mastermode) goto master_start;

	while (++tries <= 10) {
		/* Receive an initialization packet and handle it */
		if (inpkt() == 0 && tt == CONTROL && xxx == which[expect]) {
			/* Remember we've seen it, grab value */
			switch (xxx) {
			case INITA:
			case INITC:
				sendwin = yyy;
				break;
			case INITB:	
				/*
				 * Get preferred packet size for other guy,
				 * but don't overrun our buffer space.
				 * The encoded segment size is off-by-1 from
				 * the one used in the K field in each packet.
				 */
				do {
					sendseg = yyy+1;
					sendbytes = segbytes[sendseg];
				} while (sendbytes > MAX_PACKET && --yyy);
				break;
			}
		} else {
			expect = -1;
		}

master_start:
		/*
		 * Transmit an initialization packet.
		 *
		 * Send whichever packet we expected, if we got it.
		 * If we didn't, send INITA.
		 */
		switch (expect) {
		case -1:
		case 0:
			ctlmsg((INITA << 3) | wndsiz);
			break;
		case 1:
			ctlmsg((INITB << 3) | (segsiz - 1));
			break;
		case 2:
			ctlmsg((INITC << 3) | wndsiz);
			break;
		}
		
		if (++expect > 2)
			return 0;		/* We are done */
	}
	return 1;				/* Failure */
}

/*
 * Close a conversation in the G protocol.  Medium level routine.
 */
int
gpro_close()
{

	/* In windowed protocol, we have to check if prev one's been ack'd */
	if (last_op == OP_WRITE) {
		if (inpkt()) return 1;
	}

	do {
		ctlmsg(CLOSE << 3);
		if (inpkt())
			return 1;
	} while (tt != CONTROL && xxx != CLOSE);

	return 0;
}

/*
 * MAIN ROUTINE.
 *
 * This is called at program startup.  It parses the arguments to the
 * program (if any) and sets up to receive a call on the modem.
 *
 * If there are no arguments, we assume the caller is already on standard
 * input, waiting to do uucp protocols (past the login prompt), and we
 * just handle one caller.
 *
 * If there is an argument, it is the name of the tty device where we
 * should listen for multiple callers and handle login and password.
 */
main(argc,argv)
int argc;
char *argv[];
{
	int ontheline = 1;

	/* If argument provided, use it as name of comm port */
	if (argc > 1) {
		ontheline = 0;
		strcpy(ttynam, argv[1]);
	}

#ifdef UNIX
	if (ontheline) {
		if (chdir(SPOOLDIR)) {
			perror("Can't chdir to Spool directory");
			exit(2);
		}
	}
#endif UNIX

#ifdef LOG
	if (0 > (logfd = open(LOGFILE, O_CREAT|O_WRONLY|O_APPEND, 0644))) {
		perror("Can't open LOGFILE");
		exit(2);
	}
	/* Log our presence so we humans reading the logs can find the
	   entries created by uuslave. */
	logit("UUSLAVE", ontheline? version: ttynam);
#endif

#ifdef DEBUG
	if (ontheline) {
		freopen("uuslave.log", "a", stdout);
	}
#endif

#ifdef SYSV
	setbuf(stdout, (char *)NULL);	/* Unbuffered debug output */
#endif

#ifdef BSD
	setbuf(stdout, (char *)NULL);	/* Unbuffered debug output */
#endif

#ifdef MSDOS
	setbuf(stdout, (char *)NULL);	/* Unbuffered debug output */
#endif

#ifdef DEBUG
	{
		long clock;

		time(&clock);
		printf("\014\nuuslave log starting %s", ctime(&clock));
	}
#endif

#ifdef COMPORT
	set_tty();      /* read old settings and set up port
			   so it is a full 8 bit channel at
			   1200 baud. */

	printf("\n Initializing modem.");
	xwrite(fdtty,hayesinit,sizeof(hayesinit)-1);
#endif

	do {
		/*
		 *  Set up serial channel, wait for incoming call. 
		 */

#ifdef DEBUG
		printf("\nrestarting\n");
#endif

#ifdef CPM
		/* FIXME, we should implement ontheline here */
		sioinit();
#endif

#ifdef MSDOS
		/* FIXME, we should implement ontheline here */
#ifndef COMPORT
		if ((fdtty = open(ttynam, O_RDWR)) < 0)
		{
			printf("Cannot open %s for read/write %d\n",
				ttynam, errno);
			exit(1);
		}
#endif
#ifdef COMPORT
		printf("\n Waiting for call.  Strike any key to abort.\n");
		while ((get_msr() & CD) == 0){
			if (kbhit() != 0){
				printf("\nAborting UUSLAVE at user's request.");
				exit(1);
			}
		}
		init_comm();
		inp_flush();
		sleep(3);
#endif
		signal(SIGINT,sigint);
#endif

#ifdef ST
		/* FIXME, we should implement ontheline here */
		/* set 1200 baud, no flow ctrl. */
		Rsconf(7, 0, -1, -1, -1, -1);
		/* FIXME, these numbers (at least the 7!) should be replaced
		 * with symbolic constants, preferably from a system header
		 * file */

		/* setup user abort */
		if (setjmp(abortenv))
			exit(0);
#endif

#ifdef BSD
		/* Berserkeley version */
		if (ontheline) {
			fdtty = STDIN;
		} else if ((fdtty = open(ttynam, O_RDWR)) < 0) {
			perror(ttynam);
			exit(1);
		}
		ioctl(fdtty, TIOCGETP, &atermio);
		btermio = atermio;
		btermio.sg_flags |= RAW;
		btermio.sg_flags &= ~(ECHO|XTABS);
		if (!ontheline)
			btermio.sg_ispeed = btermio.sg_ospeed = B1200;
		ioctl(fdtty, TIOCSETN, &btermio);
		signal(SIGINT,sigint);
#endif

#ifdef SYSV
		/* Missed'em Five version */
		if (ontheline) {
			fdtty = STDIN;
		} else if ((fdtty = open(ttynam, O_RDWR)) < 0) {
			perror(ttynam);
			exit(1);
		}
		ioctl(fdtty,TCGETA,&atermio);
		btermio = atermio;
		btermio.c_iflag = btermio.c_oflag = btermio.c_lflag = 0;
		btermio.c_cc[VMIN] = 1;
		btermio.c_cc[VTIME] = 0;
		if (!ontheline)
			btermio.c_cflag = (btermio.c_cflag & ~CBAUD) | B1200;
		ioctl(fdtty,TCSETA,&btermio);
		signal(SIGINT,sigint);
#endif

		do_session(ontheline);

#ifdef UNIX
		(void) close (fdtty);
#endif

#ifdef MSDOS
#ifndef COMPORT
		(void) close (fdtty);
#else
		uninit_comm();
#endif
#endif

	} while (!ontheline);

	sleep(3);		/* Let output drain? makes hangup work? */
}


/* Handle a single uucp login session */
do_session(ontheline)
	int ontheline;
{

	if (!ontheline) {
		/* output login request, verify uucp */
		xwrite(fdtty,msgo0,sizeof(msgo0)-1);
		if (instr(msgi0,sizeof(msgi0)-1))
			goto bort;

		/* output password request, verify s8000 */
		xwrite(fdtty,msgo1,sizeof(msgo1)-1);
		if (instr(msgi1,sizeof(msgi1)-1))
			goto bort;
	}

	/* output here message, wait for response */
	xwrite(fdtty,msgo2,sizeof(msgo2)-1);
	if (instr(msgi2,sizeof(msgi2)-1))
		goto bort;

	/* output ok message, output protocol request, wait for response */
	xwrite(fdtty,msgo3,sizeof(msgo3)-1);
	xwrite(fdtty,msgo3a,sizeof(msgo3a)-1);
	if (instr(msgi3,sizeof(msgi3)-1))
		goto bort;

	if (gpro_open(0)) goto bort;
	logit("OK", "startup");
	while (1)
		if (top_level()) goto bort;
	/* NOTREACHED */

bort:	
	printf("...aborting...\n");
	;
}

/*
 * Handle a transaction "at top level", as Unix uucp's debug log says.
 * This really means "receive a command from the other side and handle
 * it".  Return 1 to abort, 0 to continue.
 */
int
top_level()
{

	msgbld[0] = '\0';		/* No command yet */
	do {
		if (indata() || tt != LONGDATA)
			return 1;
		msgi[6+msgsize] = '\0';	/* Null terminate packet */
		strcat(msgbld,&msgi[6]);	/* Tack on to command */
	} while (strlen(msgi+6) == msgsize);	/* Loop if no null in pkt */

#ifdef DEBUG
	/* Print it for easy debugging */
	printf("\n\nCommand: %s\n\n", msgbld);
#endif

	switch (msgbld[0]) {
	case 'S' :
		if (send_file(msgbld)) return 1;
		break;
	case 'R' :
		if (receive_file(msgbld)) return 1;
		break;
	case 'H' :
		if (yesno('H', 1))
			return 1;
		if (indata() || tt != LONGDATA)
			return 1;
		if (!strcmp(&msgi[6],"HY"))
		{
			/* Shut down the packet protocol */
			gpro_close();

			/* Write the closing sequence */
			xwrite(fdtty,msgo4,sizeof(msgo4)-1);
			(void) instr(msgi4,sizeof(msgi4)-1);
			xwrite(fdtty,msgo4,sizeof(msgo4)-1);
			logit("OK", "conversation complete");
			return 1;	/* Go byebye */
		}
		break;
	default:
		/* Unrecognized packet from the other end */
		printf("\nBad control packet type %c refused.\n", msgbld[0]);
		if (yesno(msgbld[0], 0))
			return 1;
		break;
	}

	return 0;
}

/* Send a "yes or no" packet with character 'c'. */
int
yesno(c, true)
	char c;
	int true;
{
	char buf[20];

	buf[0] = c;
	buf[1] = 'Y';
	buf[2] = 0;
	/* FIXME, errno might not be the right return code here */
	if (!true) 
		sprintf(buf,"%cN%d", c, errno);

	return sendpacket(buf, strlen(buf), LONGDATA);
}

/*
 * Create a temporary file name for receiving a file into.
 * "name" is the name we will actually eventually want to use for the file.
 * We currently ignore it, but some OS's that can't move files around
 * easily might want to e.g. put the temp file into the same directory
 * that this file is going into.
 */
char *
temp_filename(name)
	register char *name;
{
	static char tname[NAMESIZE];

	if (ourpid == 0)
		ourpid = getpid();
	sprintf(tname, "TM.u%d", ourpid);
#ifdef DEBUG
	printf("Using temp file %s\n", tname);
#endif
	return tname;
}


/*
 * Transform a filename from a uucp packet (in Unix format) into a local
 * filename that will work in the local file system.
 */
char *
munge_filename(name)
	register char *name;
{
	register char *p;

#ifdef DEBUG
	printf("Munge_filename  input: %s\n", name);
#endif

#ifdef CPM
	for (p = name + strlen(name); p != name && *(p-1) != '/'; p--) ;
#endif

#ifdef UNIX
	{static char buffer[NAMESIZE+SLOP];

	/* FIXME: Security checking goes here! */

	if (name[0] == '~') {
		/* Handle user-relative names -- ~ or ~uucp turns to PUBDIR */
		if (name[1] == '/')
			p = &name[1];
		else if (!strncmp("~uucp/", name))
			p = &name[5];
		else {
			p = NULL;		/* Neither of the above */
			goto out;
		}
		strcpy(buffer, PUBDIR);
		strcat(buffer, p);
		p = buffer;
		goto out;
	}
#ifdef SUBDIR
	/* Berkeley Unix subdirectory hack.
	 * Full pathnames go through OK.
	 * D.myname*	-> D.myname/D.myname*
	 * D.*		-> D./D.*
	 * C.*		-> C./C.*
	 * otherwise left alone (e.g. X.*).
	 * FIXME: we punt D.myname since we don't know our own name yet.
	 * FIXME: I hear Honey Danber has a slightly different scheme.
	 */
	if (name[0] != '/') {
		if (name[1] == '.' &&
		    (name[0] == 'C' || name[0] == 'D')) {
			strncpy(buffer, name, 2);
			buffer[2] = '/';
			strcpy(buffer+3, name);
			p = buffer;
			goto out;
		}
	}
#endif

	p = name;		/* Let it through as-is. */
	}
#endif

#ifdef MSDOS
	p = name;		/* FIXME */
#endif

out:
#ifdef DEBUG
	printf("Munge_filename output: %s\n", p);
#endif
	return p;
}

/*
 * Master wishes to send a file to us -- we receive it.
 * Return 1 to abort the call, 0 to continue.
 */
int
send_file(msg)
	char *msg;
{
	char *p, *q;	/* File names */
	int offset;
	int fddsk;	/* Disk file descriptor */
	int status;
	int error = 0;	/* No errors so far */

	sscanf(msg,"%s %s %s %s %s %s %o",
		cmnd, srcnam, dstnam, who, flags, temp, &mode);
	logit("REQUESTED", msgbld);
	q = munge_filename(dstnam);	/* Translate to local customs */
	p = temp_filename(q);		/* Create a handy temp file */
	if (p && (fddsk = open(p, O_CREAT|O_EXCL|O_WRONLY, mode|0600)) >= 0) {
		/* FIXME: Are the above permissions right?? */
		/* FIXME: Should we create directories for the file? */
		if (yesno('S',1))		/* Say yes */
			return 1;
		do {
			/* Read a packet, handle the data in it */
			if (indata())
				return 1;

			switch (tt) {
			case LONGDATA:
				/* FIXME, check this write */
				offset = 6;
				goto writeit;
			case SHORTDATA:
				if (msgi[6] & 0x80) {
					msgsize -=
					  (msgi[7] << 7) | (127&msgi[6]);
					offset = 8;
				} else {
					msgsize -= msgi[6];
					offset = 7;
				}

			writeit:
				if (msgsize != 0) {
					status = 
					  write(fddsk, &msgi[offset], msgsize);
					if (status != msgsize) error++;
				}
				break;
			}
		} while (msgsize != 0);
		status = close(fddsk);
		if (status != 0) error++;

		/* Move the file from its temp location to its real loc */
		/* FIXME:  This needs to be able to copy the file, if 
		   a simple rename does not suffice. */
		status = rename(p, q);
		if (status != 0) {
#ifdef DEBUG	
		printf("Cannot rename file %s to %s, errno=%d\n",
			p, q, errno);
#endif DEBUG
			error++;
		}

		logit("COPY", error? "FAILED": "SUCCEEDED");
		if (yesno('C', error == 0))	/* Send yes or no */
			return 1;
	}
	else
	{
		/* Can't open file -- send error response */
#ifdef DEBUG
		printf("Cannot open file %s (%s) for writing, errno=%d\n",
			p, dstnam, errno);
#endif
		logit("REQUEST", "FAILED -- HMM");
		if (yesno('S', 0))
			return 1;
	}

	return 0;
}

/*
 * Master wants to Recieve a file from us -- we send it.
 * Return 1 to abort the call, 0 to continue.
 */
int
receive_file(msg)
	char *msg;
{
	char *p;
	int count;
	int fddsk;		/* Disk file descriptor */

	sscanf(msg,"%s %s %s",cmnd,srcnam,dstnam);
	logit("REQUESTED", msg);
	p = munge_filename(srcnam);
	if (p && (fddsk = open(p, O_RDONLY)) >= 0) {
		if (yesno('R',1))
			return 1;
		do {
			count = read(fddsk, dskbuf, sendbytes);
			if (sendpacket(dskbuf, count,
				(count == sendbytes)? LONGDATA: SHORTDATA))
					return 1;
		} while (count);
		close(fddsk);
		/* Await the "CY" or "CNddd" packet, and toss it. */
		while (1) {
			if (indata() || tt != LONGDATA)
				return 1;
			if (msgi[6] != 'C') {
#ifdef DEBUG
				printf("\nDidn't get 'CY' or 'CN', got %s\n",
					&msgi[6]);
#endif
			} else {
				logit("REQUESTED", &msgi[6]);
				break;
			}
		}
	} else {
		/* Can't open file for reading; return error packet */
#ifdef DEBUG
		printf("Cannot open file %s (%s) for reading, errno=%d\n",
			p, srcnam, errno);
#endif
		logit("DENIED", "CAN'T OPEN");
		if (yesno('R', 0))
			return 1;
	}

	return 0;
}

#ifdef LOG
/*
 * Log file writing subroutine.
 *
 * Makes incredibly ugly log entries that look *just like* Unix uucp's
 * incredibly ugly log entries.
 *
 * Once we don't care about compatability, we should do this much better.
 */
logit(one, two)
	char *one, *two;
{
	char logbuf[(NAMESIZE*4)+SLOP+50];	/* Temp buffer for logs */
	long clock;
	struct tm *tm;
	int len;

	(void) time(&clock);
	tm = localtime(&clock);
	if (ourpid == 0)
		ourpid = getpid();

	sprintf(logbuf, "%s uuslave (%d/%d-%d:%02d-%d) %s (%s)\n",
		who, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min,
		ourpid, one, two);

#ifdef DEBUG
	printf("%s", logbuf);
#endif

	len = strlen(logbuf);
	if (len != write(logfd, logbuf, len)) {
#ifdef DEBUG
		printf("Can't log to logfd, terminating");
		perror(LOGFILE);
#endif
		exit(39);		/* Terminate if we can't log */
	}

}
#endif

#ifndef UNIX
/* CP/M and MSDOS and ST need these routines.  Probably should use
 * the new names, but for now...
 */
bzero(s, cnt)
register char	*s;
register int	cnt;
{
	register int	i;
	for (i = 0; i < cnt; i++) {
		*s++ = '\0';
	}
}

bcopy(from, to, cnt)
register char	*from;
register char	*to;
register int	cnt;
{
	register int	i;
	for (i = 0; i < cnt; i++) {
		*to++ = *from++;
	}
}
#endif

#ifdef COMPORT
/*
 * MSDOS routines for handling the comm port.
 *
 * get_time(n)
 * TIME_PTR n;
 *
 * fills timetype structure n with current time using DOS interrupt 21
 *
 */

get_time(n)
TIME_PTR n;
{
  union REGS inregs;
  union REGS outregs;

  inregs.h.ah = 0x2c;		/* Please make a #define for this, Tim */

  int86(0x21, &inregs, &outregs);/* Please #define the 0x21 too */

  n->hour = outregs.h.ch;
  n->minute  = outregs.h.cl;
  n->sec  = outregs.h.dh;
  n->hsec = outregs.h.dl;

  return(0);
}

sleep(x)
int x;
{
  int i;
  unsigned s;
  TIME n;               /* current time record */

  i = 0;
  get_time(&n);
  s = n.sec;

  while (i < x){
    while (s == n.sec)
      get_time(&n);
    s = n.sec;
    ++i;
  }
}
#endif
@@@ Fin de uuslave.c
exit 0
-- 
Copyright 1987 John Gilmore; you can redistribute only if your recipients can.
(This is an effort to bend Stargate to work with Usenet, not against it.)
{sun,ptsfa,lll-crg,ihnp4,ucbvax}!hoptoad!gnu	       g...@ingres.berkeley.edu

Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: utzoo!utgpu!water!watnot!watmath!clyde!rutgers!lll-lcc!well!hoptoad!gnu
From: g...@hoptoad.UUCP
Newsgroups: net.sources
Subject: uuslave (free uucp), version hoptoad-1.11, part 2 of 2
Message-ID: <1926@hoptoad.uucp>
Date: Tue, 24-Mar-87 04:01:41 EST
Article-I.D.: hoptoad.1926
Posted: Tue Mar 24 04:01:41 1987
Date-Received: Thu, 26-Mar-87 01:17:46 EST
References: <1925@hoptoad.uucp>
Organization: Nebula Consultants in San Francisco
Lines: 1234

Here is part 2 of the latest uuslave distribution.  Have fun...
 
        John "Use the Source, Luke" Gilmore 
 
PS:  In the subject of part 1, I called uuslave public domain.  This
version is copyright by me and distributed under the GNU rules.
Thus it is free, but not public domain.  Sorry for the confusion.

: To unbundle, sh this file
echo packet.driver.ms
cat >packet.driver.ms <<'@@@ Fin de packet.driver.ms'
.\" @(#) packet.driver.ms	Version hoptoad-1.3	87/03/24
.\"
.\" format this with [nt]roff -ms.
.\"
.\" From: g...@sgi.uucp (Greg Chesson)
.\" Newsgroups: mod.std.unix
.\" Volume-Number: Volume 9, Number 55
.\" Subject: Packet Driver Protocol
.\" Message-ID: <7...@ut-sally.UUCP>
.\" Date: 11 Feb 87 23:44:09 GMT
.\"
.\" This message contains a copy of ``Packet Driver Protocol,''
.\" written by G. L. Chesson while he was at Bell Laboratories.
.\" He remarks that it was approved for public distribution, and that
.\" 
.\" 	The version of the note that you probably have omits the
.\" 	detail that the transmitted checksum is really 0125252
.\" 	- the block checksum function.
.\" 
.\" [Note that 0125252 is 0xAAAA, which is easier to remember.
.\" I have folded this update into the document. -- hoptoad!gnu]
.ce
.B
Packet Driver Protocol
.R
.sp 1
.ce
G. L. Chesson
.br
.ce
Bell Laboratories
.SH
Abstract
.in +.5i
.PP
These notes describe the packet driver link
protocol that was supplied
with the
Seventh Edition of
.UX
and is used by the UUCP program.
.in -.5i
.SH
General
.PP
Information flow between a pair of machines
may be regulated by
first
representing the data 
as sequence-numbered 
.I
packets
.R
of data 
and then establishing conventions that
govern the use of sequence numbers.
The
.I
PK,
.R
or
.I
packet driver,
.R
protocol
is a particular instance of this type of
flow-control discipline.
The technique depends on the notion of a transmission
.I
window
.R
to determine upper and lower bounds for valid
sequence numbers.
The transmitter is allowed to retransmit packets
having sequence numbers
within the window until the receiver indicates that
packets have been correctly received.
Positive acknowledgement from the receiver moves the
window;
negative acknowledgement or no acknowledgement
causes retransmission.
The receiver must ignore duplicate transmission, detect
the various errors that may occur,
and inform the transmitter when packets are 
correctly or incorrectly received.
.PP
The following paragraphs describe the packet formats,
message exchanges,
and framing
used by the protocol as coded
in the UUCP program and the
.UX
kernel.
Although no attempt will be made here to present
internal details of the algorithms that were used,
the checksum routine is supplied
for the benefit of other implementors.
.SH
Packet Formats
.PP
The protocol is defined in terms of message
transmissions of 8-bit bytes.
Each message includes one
.I
control
.R
byte plus a
.I
data segment
.R
of zero or more information bytes.
The allowed data segment sizes range
between 32 and 4096 as determined by the formula
32(2\uk\d) where
k is a 3-bit number.
The packet sequence numbers are likewise constrained
to 3-bits; i.e. counting proceeds modulo-8.
.PP
The control byte is partitioned into three fields as
depicted below.
.bp
.nf
.sp 
.in 1i
.ls 1
bit	7	6	5	4	3	2	1	0
	t	t	x	x	x	y	y	y
.ls 1
.in -1i
.fi
.sp
The
.I
t
.R
bits indicate a packet type and
determine the interpretation to be placed on
the
.I
xxx
.R
and
.I
yyy
.R
fields.
The various interpretations are as follows:
.in +1i
.sp
.nf
.ls 1
.I
tt	interpretation
.sp
.R
00	control packet
10	data packet
11	`short' data packet
01	alternate channel
.ls 1
.fi
.sp
.in -1i
A data segment accompanies all non-control packets.
Each transmitter is constrained to observe the maximum
data segment size
established during initial synchronization by the
receiver that it sends to.
Type 10 packets have maximal size data segments.
Type 11, or `short', packets have zero or more data
bytes but less than the maximum.
The first one or two bytes of the data segment of a
short packet are `count' bytes that
indicate the difference between the
maximum size and the number of bytes in the short
segment.
If the difference is less than 127, one count
byte is used.
If the difference exceeds 127,
then the low-order seven bits of the difference
are put in the first data byte and the high-order
bit is set as an indicator that the remaining
bits of the difference are in the second byte.
Type 01 packets are never used by UUCP
and need not be discussed in detail here.
.PP
The sequence number of a non-control packet is
given by the
.I
xxx
.R
field.
Control packets are not sequenced.
The newest sequence number,
excluding duplicate transmissions,
accepted by a receiver is placed in the
.I
yyy
.R
field of non-control packets sent to the
`other' receiver.
.PP
There are no data bytes associated with a control packet,
the
.I
xxx
.R
field is interpreted as a control message,
and the
.I
yyy
.R
field is a value accompanying the control message.
The control messages are listed below in decreasing priority.
That is, if several control messages are to be sent,
the lower-numbered ones are sent first.
.in +1i
.nf
.ls 1
.sp
.I
xxx	name		yyy
.R

1	CLOSE	n/a
2	RJ		last correctly received sequence number
3	SRJ		sequence number to retransmit
4	RR		last correctly received sequence number
5	INITC	window size
6	INITB	data segment size
7	INITA	window size
.in -i
.ls 1
.fi
.sp
.PP
The CLOSE message indicates that the communications channel
is to be shut down.
The RJ, or
.I
reject,
.R
message indicates that the receiver has detected an error
and the sender should retransmit after using the 
.I
yyy
.R
field to update the window.
This mode of retransmission is usually
referred to as a
`go-back-N' procedure.
The SRJ, or
.I
selective reject,
.R
message carries with it the sequence number of
a particular packet to be retransmitted.
The RR, or
.I
receiver ready,
.R
message indicates that the receiver has detected
no errors; the
.I
yyy
.R
field updates the sender's window.
The INITA/B/C messages are used
to set window and data segment sizes.
Segment sizes are calculated by the formula
32(2\uyyy\d)
as mentioned above,
and window sizes may range between 1 and 7.
.PP
Measurements of the protocol running on communication
links at rates up to 9600 baud showed that
a window size of 2 is optimal
given a packet size greater than 32 bytes.
This means that the link bandwidth can be fully utilized
by the software.
For this reason the SRJ message is not as important as it
might otherwise be.
Therefore the
.UX
implementations no longer generate or respond to SRJ
messages.
It is mentioned here for historical accuracy only,
and one may assume that SRJ is no longer part of the protocol.
.SH
Message Exchanges
.SH
	Initialization
.PP
Messages are exchanged between four cooperating
entities: two senders and two receivers.
This means that the communication channel is thought of
as two independent half-duplex data paths.
For example the window and segment sizes need not
be the same in each direction.
.PP
Initial synchronization is accomplished
with two 3-way handshakes: two each of
INITA/INITB/INITC.
Each sender transmits INITA messages repeatedly.
When an INITA message is received, INITB is
sent in return.
When an INITB message is received
.I
and
.R
an INITB message has been sent,
an INITC message is sent.
The INITA and INITB messages carry 
with them the packet and window size that
each receiver wants to use,
and the senders are supposed to comply.
When a receiver has seen all three
INIT messages, the channel is 
considered to be open.
.PP
It is possible to design a protocol that starts up using
fewer messages than the interlocked handshakes described above.
The advantage of the more complicated design lies in its use as
a research vehicle:
the initial handshake sequence is completely symmetric,
a handshake
can be initiated by one side of the link while the
connection is in use, and the software to do this can
utilize code that would ordinarily be used only once
at connection setup time.
These properties were used in experiments with dynamically
adjusted parameters.
That is attempts were made to adapt the window and segment
sizes to changes observed in traffic while a link was in use.
Other experiments used the initial
handshake  in a different way
for restarting the protocol without data loss
after machine crashes.
These experiments never worked well in the packet driver and
basically provided the impetus for other protocol designs.
The result 
as far as UUCP is concerned is that initial synchronization
uses the two 3-way handshakes, and the INIT
messages are ignored elsewhere.
.SH
	Data Transport
.PP
After initial synchronization each receiver
sets a modulo-8 incrementing counter R to 0;
each sender sets a similar counter S to 1.
The value of R is always the number of the most recent
correctly received packet.
The value of S is always the first sequence number in
the output window.
Let W denote window size.
Note that the value of W may be different for each sender.
.PP
A sender may transmit packets with sequence numbers
in the range S to (S+W-1)\ mod-8.
At any particular time a receiver expects
arriving packets to have numbers in the range
(R+1)\ mod-8 to (R+W)\ mod-8.
Packets must arrive in sequence number order
are are only acknowledged in order.
That is,
the `next' packet a receiver
will acknowledge must have
sequence number (R+1)\ mod-8.
.PP
A receiver acknowledges receipt of data packets
by arranging for the value of its R counter to be
sent across the channel
where it will be used to update an S counter.
This is done in two ways.
If data is flowing in both directions across a
channel then each receiver's current R value is
carried in the
.I
yyy
.R
field of non-control packets.
Otherwise when there is no bidirectional
data flow,
each receiver's R value is transmitted across the link
as the
.I
yyy
.R
field of an RR control packet.
.PP
Error handling is up to the discretion
of the receiver.
It can ignore all errors in which case
transmitter timeouts must provide for
retransmission.
The receiver may also generate RJ 
error control packets.
The
.I
yyy
.R
field of an incoming RJ message replaces
the S value of the local sender and
constitutes a request for retransmission to start
at that sequence number.
The
.I
yyy
.R
field of an incoming SRJ message selects a particular
packet for retransmission.
.PP
The resemblance between the flow control procedure in the
packet driver and that defined for X.25 is no accident.
The packet driver protocol began life as an attempt at
cleaning up X.25.
That is why, for example,
control information is uniform in length (one byte),
there is no RNR message (not needed),
and there is but one timeout defined
in the sender.
.SH
	Termination
.PP
The CLOSE message is used to terminate communications.
Software on either or both ends of the communication
channel may initiate termination.
In any case when one end wants to terminate it sends
CLOSE messages until one is received from the other end
or until a programmable limit on the number of CLOSE
messages is reached.
Receipt of a CLOSE message causes a CLOSE message to be sent.
In the 
.UX
environment
it also causes the SIGPIPE or
`broken pipe' signal to be sent to
the local process using the communication channel.
.SH
	Framing
.PP
The term
.I
framing
.R
is used to denote the technique by which the
beginning and end of a message is detected
in a byte stream;
.I
error control
.R
denotes the method by which transmission
errors are detected.
Strategies for framing and error control depend
upon
additional information being transmitted along
with the control byte and data segment,
and the choice of a particular strategy usually
depends on characteristics of input/output
devices and transmission media.
.PP
Several framing techniques are in used in support
of PK protocol implementations,
not all of which can be described in detail here.
The technique used on asynchronous serial lines
will be described.
.PP
A six byte
framing
.I
envelope
.R
is constructed using the control byte
C of a packet and five other bytes as
depicted below.
.in +1i
<DLE><k><c0><c1><C><x>
.in -1i
The <DLE> symbol denotes the ASCII ctrl/P character.
If the envelope is to be followed by a data segment,
<k> has the value
log\d2\u(size)-4;
i.e. 1 \(<= k \(<= 8.
If k is 9, then the envelope represents a control packet.
The <c0> and <c1> bytes are the low-order and high-order
bytes respectively of 0xAAAA minus a 16-bit checksum.
For control packets, this 16-bit checksum is the same
as the control byte C.
For data packets, the checksum is calculated by the 
program below.
The <x> byte is the exclusive-or of <k><c0><c1><C>.
Error control is accomplished by checking 
a received framing envelope for compliance with the definition,
and comparing a checksum function of the data segment
with <c0><c1>.
.PP
This particular framing strategy assumes data segments
are constant-sized:
the `unused' bytes in a short packet are actually
transmitted.
This creates a certain amount of overhead which
can be eliminated by a more complicated framing technique.
The advantage of this strategy is that i/o
devices can be programmed to take advantage of the
constant-sized framing envelopes and data segments.
.bp
.PP
The checksum calculation is displayed below as a C function.
Note that the code is not truly portable because
the definitions of
.I short
and
.I char
are not necessarily uniform across all machines
that might support this language.
This code assumes that
.I short
and
.I char
are 16 and 8-bits respectively.
.PP
.in +.5i
.nf
.ft CW
.ls 1
/* [Original document's version corrected to actual version] */
chksum(s,n)
register char *s;
register n;
{
	register short sum;
	register unsigned short t;
	register short x;

	sum = -1;
	x = 0;

	do {
		if (sum<0) {
			sum <<= 1;
			sum++;
		} else
			sum <<= 1;
		t = sum;
		sum += (unsigned)*s++ & 0377;
		x += sum^n;
		if ((unsigned short)sum <= t) {
			sum ^= x;
		}
	} while (--n > 0);

	return(sum);
}
.fi
.in -.5i
.ft R
@@@ Fin de packet.driver.ms
echo comport.h
cat >comport.h <<'@@@ Fin de comport.h'
/*
 * Comport.h
 *
 * defines the bit masking for the get_mcr()
 *
 * @(#) comport.h	Version hoptoad-1.3	87/03/24
 *
 * Copyright (C) Tim M. Pozar 1987
 * Anyone can use this code for anything, but it is copyright by Tim
 * and you must leave his copyright in the code.
 *
 */

/*
 * get_msr()
 *   Function to read (get) the byte located in the Modem Status 
 * Register (3FEh).  The table below describes the byte returned.
 *   bit  description
 *    0   Delta Clear to Send (DCTS)
 *        Indicates that the !CTS input to the chip has changed state
 *        since the last time it was read by the processor.
 *    1   Delta Data Set Ready (DDSR)
 *        Indicates that the !DRS input to the chip has changed since 
 *        last time it was read by the processor.
 *    2   Trailing Edge Ring Indicator (TERI)
 *        Indicates that the !RI input to the chip has changed from
 *        an on (logical 1) to an off (logical 0) condition.
 *    3   Delta Rx Line Signal detect (DRLSD)
 *        Indicates that the !RLSD input to the chip has changed state.
 * NOTE: Whenever bit 0, 1, 2, or 3 is set to a logical 1, a modem status
 *       interrupt is generated.
 *
 *    4   Clear to Send (CTS)
 *        This bit is the complement of the clear to send (!CTS) input.
 *        If bit 4 (LOOP) of the MCR is set to a logical 1, this is 
 *        equivalent to RTS in the MCR.
 *    5   Data Set Ready (DSR)
 *        This bit is the complement of the data set ready (!DSR) input.
 *        If bit 4 (LOOP) of the MCR is set to a logical 1, this is 
 *        equivalent to DTR in the MCR.
 *    6   Ring Indicator (RI)
 *        This bit is the complement of the ring indicator (!RI) input.
 *        If bit 4 (LOOP) of the MCR is set to a logical 1, this is 
 *        equivalent to OUT 1 in the MCR.
 *    7   Receive Line Signal Detect (RLSD) or Carrier Detect (CD).
 *        This bit is the complement of the received line signal detect
 *        (!RLSD) input. If bit 4 (LOOP) of the MCR is set to a logical 1,
 *        this is equivalent to OUT 2 in the MCR.
 */

#define  DCTS       1
#define  DDSR       2
#define  TERI       4
#define  DRLSD      8
#define  CTS       16
#define  DST       32
#define  RI        64
#define  RLSD     128   /* Also known as ... */
#define  CD       128   
@@@ Fin de comport.h
echo comport.asm
cat >comport.asm <<'@@@ Fin de comport.asm'
title IBM PC Communications I/O Routines 
;
; @(#) comport.asm	Version hoptoad-1.3	87/03/24
;
; Orginal code -- Curt Klinsing
;
; Changes and updates -- Copyright (c) 1987 Tim Pozar
; Anyone can use this code for anything, but it is copyright by Tim
; and you must leave his copyright in the code.
;
; ver: 0
; rev: 2
; March 13th 1987
; This code is in a very early stage and should not be let out.
; Several other extensive functions are planned as well as changes
; to the current code.
;
; 2/20/87 
;   Changed segment declarations and function names (eg. _function)
; to fit Microsoft C 4.0 and linker requirements.
;
; FUNCTIONS CHANGED/ADDED --
; set_tty(port_number)
;   Function to find current settings of the port and set up serial 
; port for 'baud' and 'lcbyte', and enable DTR.  This will set up the
; port number base addressed passed to it (eg. 3F8h) and all functions
; will use this port until the function is used again. (NOT READY FOR USE)
;
; reset_tty()
;   Function to put the port back into the state it was when it was 
; first found by set_tty().  If set_tty() was not called it will not 
; change the settings of the port. (NOT READY FOR USE)
;
; 3/13/87
; get_msr()
;   Function to read (get) the byte located in the Modem Status 
; Register (3FEh).  The table below describes the byte returned.
;   bit  description
;    0   Delta Clear to Send (DCTS)
;        Indicates that the !CTS input to the chip has changed state
;        since the last time it was read by the processor.
;    1   Delta Data Set Ready (DDSR)
;        Indicates that the !DRS input to the chip has changed since 
;        last time it was read by the processor.
;    2   Trailing Edge Ring Indicator (TERI)
;        Indicates that the !RI input to the chip has changed from
;        an on (logical 1) to an off (logical 0) condition.
;    3   Delta Rx Line Signal detect (DRLSD)
;        Indicates that the !RLSD input to the chip has changed state.
; NOTE: Whenever bit 0, 1, 2, or 3 is set to a logical 1, a modem status
;       interrupt is generated.
;
;    4   Clear to Send (CTS)
;        This bit is the complement of the clear to send (!CTS) input.
;        If bit 4 (LOOP) of the MCR is set to a logical 1, this is 
;        equivalent to RTS in the MCR.
;    5   Data Set Ready (DSR)
;        This bit is the complement of the data set ready (!DSR) input.
;        If bit 4 (LOOP) of the MCR is set to a logical 1, this is 
;        equivalent to DTR in the MCR.
;    6   Ring Indicator (RI)
;        This bit is the complement of the ring indicator (!RI) input.
;        If bit 4 (LOOP) of the MCR is set to a logical 1, this is 
;        equivalent to OUT 1 in the MCR.
;    7   Receive Line Signal Detect (RLSD).
;        This bit is the complement of the received line signal detect
;        (!RLSD) input. If bit 4 (LOOP) of the MCR is set to a logical 1,
;        this is equivalent to OUT 2 in the MCR.
;
;   Currently this driver is set up for COM1 (3f8h).
;   If you are using the interupt driven buffer, take out the code 
; that enables the DTR so that it doesn't get raised until the vectors
; are initilized. 
;

_TEXT   SEGMENT BYTE PUBLIC 'CODE'
_TEXT   ENDS
_DATA   SEGMENT BYTE PUBLIC 'DATA'
_DATA   ENDS
CONST   SEGMENT BYTE PUBLIC 'CONST'
CONST   ENDS
_BBS    SEGMENT BYTE PUBLIC 'BBS'
_BBS    ENDS

DGROUP  GROUP   CONST, _BBS, _DATA
      ASSUME    CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP

_TEXT      SEGMENT
;
;A set of Lattice C and MSC callable functions to support
;interrupt driven character I/O on the  IBM PC. Input
;is buffered, output is polled.
;
;added functions (TMP) --
public  _set_tty        ;find current settings, and initialize 
                        ;comm port to 8 bits and set DTR
public  _reset_tty      ;reset to settings that set_tty() found
public  _get_msr        ;get MSR byte from port.
;
;original functions --
public  _init_comm      ;initialize the comm port interupts,
public  _uninit_comm    ;remove initialization,
public  _set_xoff       ;enable/disable XON/XOFF,
public  _get_xoff       ;read XON/XOFF state,
public  _rcvd_xoff      ;returns true if XOFF rcvd,
public  _sent_xoff      ;true if XOFF sent,
public  _inp_cnt        ;returns count of rcv chars,
public  _inp_char       ;get one char from buffer,
public  _inp_flush      ;flush input buffer,
public  _outp_char      ;output a character,
;
;A better description can be found in the comment
;block  in each function.
;
;       assume  cs:pgroup
;
FALSE   EQU     0
TRUE    EQU     NOT FALSE
;
BASE    EQU     03F8H   ;BASE FOR SERIAL BOARD
;
LCR    	equ     BASE+3  ; Line control register
IER     equ     BASE+1  ; Interrup Enable Register
MCR     EQU     BASE+4  ;modem control register
MDMSTA  EQU     BASE+5  ;line status register
MDMMSR  EQU     BASE+6  ;modem status register
MDMBAD  EQU     BASE    ;lsb baud resgister
EnblDRdy equ    01H     ; enable 'data-ready' interrupt bit
IntCtlr  EQU    21H     ;OCW 1 FOR 8259 CONTROLLER
EnblIRQ4 EQU    0EFH    ;Enable COMMUNICATIONS (IRQ4)
dataport EQU    BASE    ;transmit/receive data port
MaskIRQ4 EQU    10H     ;BIT TO DISABLE COMM INTERRUPT (IRQ4)

MDMCD   EQU     80H     ;mask for carrier dectect
SETBAU  EQU     80H     ;code for Divisor Latch Access Bit
MDMTBE  EQU     20H     ;8250 tbe flag
MDMBRK  EQU     40H     ;command code for 8250 break
LINMOD  EQU     03H     ;line mode=8 bit, no parity
MDMMOD  EQU     0BH     ;modem mode = DTR and RTS HIGH
STOP2   EQU     04H     ;BIT FOR TWO STOP BITS IF BAUD<300
RS8259  EQU     20H     ;OCW 3 FOR 8259
RSTINT  EQU     64H     ;SPECIFIC EOI FOR COMM INTERRUPT 
XOFF    EQU     13H     ;XOFF character
XON     EQU     11H     ;XON character
;
;       MISCELLANEOUS EQUATES
;
CR      EQU     13
LF      EQU     10
DosCall EQU     33      ;INTERRUPT NUMBER FOR DOS CALL  
CNSTAT  EQU     11      ;FUNCTION NUMBER FOR CONSOLE STATUS
CNIN    EQU     1       ;FUNCTION NUMBER FOR CONSOLE INPUT
BUFSIZ  EQU     512     ;Max NUMBER OF CHARS
SetIntVect  EQU 25H     ;SET INTERRUPT VECTOR FUNCTION NUMBER

;
; Communication parameters --
;
baud    equ     96      ; 1047 =  110 (are you kidding?)
                        ;  384 =  300
                        ;   96 = 1200
                        ;   48 = 2400
                        ;   24 = 4800
                        ;   12 = 9600
parity  equ     00000b  ;00000 = none
                        ;01000 = odd
                        ;11000 = even
stopbit equ     000b    ;  000 = 1 bit
                        ;  100 = 2 bits
wordlth equ     11b     ;   10 = 7 bits
                        ;   11 = 8 bits
lcbyte  equ     parity+stopbit+wordlth      ;line control byte
div_on  equ     80h     ;divisor latch access bit (DLAB)

;
;       DUMP BUFFER, COUNT AND POINTER.  
;
CIRC_BUF DB     BUFSIZ DUP(?)   ;ALLOW 512 MaxIMUM BUFFERED CHARACTERS
BUF_TOP EQU     $ - 1           ;KEEP TRACK OF THE TOP OF THE BUFFER
CIRC_TOP DW     BUF_TOP         ;
;
CIRC_IN DW      OFFSET CIRC_BUF ;POINTER TO LAST CHAR. PLACED IN BUFFER
CIRC_CUR DW     OFFSET CIRC_BUF ;POINTER TO NEXT CHAR. TO BE RETRIEVED FROM
                                ; BUFFER
CIRC_CT DW      0               ;COUNT OF CHARACTERS USED IN BUFFER
SNT_XOFF DB     FALSE           ;FLAG TO CHECK IF AN XOFF HAS BEEN SEND
GOT_XOFF  DB    FALSE           ;FLAG TO CHECK IF AN XOFF HAS BEEN RECEIVED
SEE_XOFF  DB    FALSE           ;FLAT TO SEE IF WE ARE INTERESTED IN XON/XOFF
;
;
; set_tty()
;
_set_tty proc near
        push    bp
;        mov     dx,mcr
;        in      al,dx           ; get modem parameters
;        mov     MCR_BYTE,al     ; save them
        mov     dx,lcr
;        in      al,dx           ; get line parameters
;        mov     LCR_BYTE,al     ; save them
        mov     al,div_on
        out     dx,al           ; set 8250 for baud rate selection
        ; can the baud rate divisor be read to save the settings?
        ; if so, stick the code here.
        mov     ax,baud
        mov     dx,mdmbad
        out     dx,al           ; low byte divisor
        mov     al,ah
        inc     dx
        out     dx,al           ; high byte divisor
        mov     dx,lcr
        mov     al,lcbyte
        out     dx,al           ; set line control reg.
        mov     dx,mcr
        in      al,dx
        or      al,mdmmod
        out     dx,al           ; set DTR high
flsh:   mov     dx,dataport
        in      al,dx
        mov     dx,mdmsta
        in      al,dx
        and     al,1
        jnz     flsh
      
        pop     bp
        ret

_set_tty endp


_reset_tty proc near
        push    bp

        pop     bp
        ret

_reset_tty endp

_get_msr proc near
        push    bp
        push    ds              ; save data segment
        push    cs
        pop     ds

        xor     ax,ax
        mov     dx,MDMMSR
        in      al,dx

        pop     ds
        pop     bp
        ret

_get_msr endp

;
; set_xoff(flag)         Enable (flag != 0) or disable
;int flag;              (flag == 0) XON/ XOFF protocol
;                       for the character input stream.
;If enabled, an XOFF will be sent when  the buffer
;reaches 3/4 full. NOTE: an XON will not be sent auto-
;matically. Your program must do it when it sees
;the _rcvd_xoff() flag,  and ready for more chars.
;
_set_xoff proc near
        push    bp
        PUSH    DS              ;SAVE DATA SEGMENT
        mov     bx,[bp+6]       
        push    cs
        pop     ds              ; move code seg addr to data seg reg.
        cmp     bx,0
        jnz     to_on
        mov     see_xoff,FALSE
        jmp     done1
to_on:  mov     see_xoff,TRUE
done1:  pop     ds
        pop     bp
        ret
_set_xoff endp
;
;flag = get_xoff()      Returns the current setting
;                       of the XON/ XOFF flag set
;by set_xoff(), above.
;
_get_xoff proc near
        push    bp
        push    ds              ; save data reg
        push    cs
        pop     ds              ; move code seg addr to data seg reg.
        xor     ax,ax
        mov     al,see_xoff
        pop     ds
        pop     bp
        ret
_get_xoff endp
;
;flag = sent_xoff();    Returns true if an XOFF
;                       character was sent, indicating
;the receive buffer is  3/4 full.
;
_sent_xoff proc  near
        push    bp
        push    ds              ; save data reg
        push    cs
        pop     ds              ; move code seg addr to data seg reg.
        xor     ax,ax
        mov     al,snt_xoff
        pop     ds
        pop     bp
        ret
_sent_xoff endp
;
; rcvd_xoff()            Returns true if an XOFF was
;                       received; will return false as
;soon as an XON is received. Does not effect data output,
;only indicates the above. (Obviously useless for binary
;data.)
;
_rcvd_xoff proc  near
        push    bp
        push    ds              ; save data reg
        push    cs
        pop     ds              ; move code seg addr to data seg reg.
        xor     ax,ax
        mov     al,got_xoff
        pop     ds              ; restore data reg
        pop     bp
        ret
_rcvd_xoff endp
;
;count = inp_cnt()       Returns the number of characters
;                       available in the input buffer.
;

_inp_cnt proc near       
        push    bp
        push    ds              ; save data segment
        push    cs
        pop     ds              ; move code seg addr to data seg reg
        mov     ax,circ_ct
        pop     ds
        pop     bp
        ret
_inp_cnt endp
;
; inp_flush()    Flush the input buffer.
;
_inp_flush proc  near    
        push    bp
        push    ds              ; save data reg
        push    cs
        pop     ds              ; move code seg addr to data seg reg.
        mov     bx,offset circ_buf
        mov     circ_in,bx      
        mov     circ_cur,bx
        xor     ax,ax
        mov     circ_ct,ax
        pop     ds
        pop     bp
        ret
_inp_flush endp

; --------- Init -----------------------------------
; Program initialization:
;   --  Set up vector for RS232 interrupt (0CH)
;   --  Enbl IRQ4
;   --  Enbl RS232 interrupt on data ready
;
; ---------------------------------------------------

_init_comm proc  near
        push    bp
        cli

;  ---- Set up  INT x'0C' for IRQ4

        push    ds
        push    cs
        pop     ds              ;cs to ds
        mov     dx,offset IntHdlr ;relative adddres of interrupt handler
        mov     al,0cH          ;interrupt number for comm.
        mov     ah,SetIntVect   ;function number for setting int vector
        int     DosCall         ;set interrupt in 8086 table
        pop     ds              ;restore DS

;  ---- Enbl IRQ4 on 8259 interrupt controller

        cli

        in      al,IntCtlr      ; get current masks 
        and     al,EnblIRQ4     ; Reset IRQ4 mask
        out     IntCtlr,al      ; And restore to IMR

;  ---   Enbl 8250 data ready interrupt

        mov     dx,LCR          ; DX ==> LCR
        in      al,dx           ; Reset DLAB for IER access
        and     al,7FH
        out     dx,al
        mov     dx,IER          ; Interrupt Enbl Register
        mov     al,EnblDRdy     ; Enable 'data-ready' interrupt
        out     dx,al

;  ---   Enbl OUT2 on 8250

        mov     dx,MCR          ; modem control register        
        in      al,dx           ; Enable OUT2
        or      al,08h            ; find out what is in there and
        out     dx,al            ; enable the DTR

        sti

        pop     bp
        ret
_init_comm endp
;
; uninit_comm()          Removes the interrupt structure
;                       installed by _init_comm(). Must be
;done before passing control to the DOS, else chars received
;will be stored into the next program loaded!
;
_uninit_comm proc near
        push    bp
; ---   Disable IRQ4 on 8259

        cli
        in      al,IntCtlr      ;GET OCW1 FROM 8259
        or      al,MaskIRQ4     ;DISABLE COMMUNICATIONS INTERRUPT
        out     IntCtlr,al

; ---   Disable 8250 data ready interrupt
        
        mov     dx,LCR          ; DX ==> LCR
        in      al,dx           ; Reset DLAB for IER access
        and     al,7FH
        out     dx,al
        mov     dx,IER          ; Interrupt Enbl Register
        mov     al,0            ; Disable all 8250 interrupts
        out     dx,al

;  ---   Disable OUT2 on 8250

        mov     dx,MCR          ; modem control register        
        mov     al,0            ; Disable OUT2
        out     dx,al

        sti
        pop     bp
        ret
_uninit_comm endp
;
;char  inp_char()      Return a character from the input
;                      buffer. Assumes you have called
;inp_cnt() to see if theres any characters to get.
;
_inp_char proc near      
        push    bp
        push    ds              ; save data reg
        push    cs
        pop     ds              ; move code seg addr to data seg reg.
        mov     bx,circ_cur
        xor     ax,ax
        mov     al,[bx]         ;get next char from circ_buf
        DEC     circ_ct         ;decrement circ_buf COUNT
        CMP     bx,circ_top     ;ARE WE AT THE TOP OF THE circ_buf?
        JZ      reset_cur       ;JUMP IF SO
        INC     bx              ;ELSE, BUMP PTR
        JMP SHORT upd_cur
reset_cur:
        mov     bx,OFFSET circ_buf      ;RESET circ_in TO BOTTOM OF BUF.
upd_cur:
        mov     circ_cur,bx             ;SAVE NEW PTR
        xor     cx,cx
        mov     cl,see_xoff     ;check if interested in xon/xoff
        cmp     cl,TRUE
        jnz     clnup2          ;not interested, so goto return
        cmp     snt_xoff,TRUE   ;have we sent an xoff?
        jnz     clnup2          ;no, so return
        cmp     circ_ct,80h     ;yes, so see in buf is now emptying
        jg      clnup2          ;not empty enuf to send xon, jump to ret
        mov     snt_xoff,FALSE
        mov     cl,XON
        push    ax              ; save char
        call    comout
        pop     ax
clnup2: pop     DS              ;GET BACK ENTERING DS
        pop     bp
        ret
_inp_char endp
;
; outp_char(c)           Output the character to the
;char c;                serial port. This is not buffered
;                       or interrupt driven.
;
_outp_char proc  near
        push    bp
        mov     bp,sp
        mov     cl,[bp+4]
        sti
        call    comout
        pop     bp
        ret
_outp_char endp
;
;Local  subroutine: output CL to the port.
;
comout: mov     dx,MDMSTA       
        in      al,dx           ; get 8250 status
        and     al,MDMTBE       ; check for transmitter ready
        jz      comout          ; jump if not to wait
        mov     al,cl           ; get char to al
        mov     dx,dataport     
        out     dx,al           ; output char to 8251
        ret
;
;       RECEIVE INTERRUPT HANDLER (CHANGED TO PLACE CHARACTERS IN A
;        CIRCULAR circ_buf AND TO SEND AN XOFF IF THE circ_buf IS MORE THAN
;        3/4 FULL - S.G.)
;
IntHdlr:
        CLI
        push    cx
        push    dx
        push    bx
        push    ax
        push    ds
        mov     ax,cs           ;get cur code segment
        mov     ds,ax           ;and set it as data segment
        mov     bx,circ_in      ;GET circ_buf IN PTR
        mov     DX,dataport     ;GET DATA PORT NUMBER
        IN      AL,DX           ;GET RECEIVED CHARACTER
;       push    ax
;       push    dx   
;       xor     ax,ax
;       xor     dx,dx
;       mov     dl,al
;       mov     ah,2
;       int     DosCall
;       pop     dx
;       pop     ax
        xor     cx,cx
        mov     cl,see_xoff     ;check if interested in xon/xoff
        cmp     cl,TRUE
        jnz     ck_full         ;not interested goto ck if buf full
        mov     cl,al           ;put char in cl for testing
        and     cl,7fh          ;turn off any parity bits 
        cmp     cl,XOFF         ;see if we got an xoff
        jnz     ck_xon
        mov     got_Xoff,TRUE   ; code for handling xon/xoff from remote
        jmp     clnup
ck_xon: cmp     cl,XON
        jnz     reg_ch
        mov     got_Xoff,FALSE
        jmp     clnup
;
;Normal character; not  XON/XOFF, or XON/XOFF disabled.
;
reg_ch: test    snt_Xoff,TRUE   ;SEE IF sentXoff IS SET
        jnz     ck_full         ;IF SO, DON'T SEND ANOTHER XOFF
        CMP     circ_ct,(BUFSIZ * 3)/4  ;ALLOW BUF TO BECOME 3/4 FULL BEFORE
                                        ; SENDING XOFF
        jb      savch           ;IF IT'S OK, CONTINUE
        push    ax              ;SAVE CHARACTER
        mov     CL,XOFF         ;GET XOFF CHARACTER
        mov     snt_Xoff,TRUE  ;RESET sentXoff
        call    comout          ; AND SEND IT
        pop     ax              ;RETRIEVE CHARACTER
        JMP SHORT savch         ;IF WE'RE HERE, THE circ_buf HAS BUFSIZ-80H
                                ;  CHARACTERS
ck_full:
        CMP     circ_ct,BUFSIZ  ;SEE IF circ_buf ALREADY FULL
        JZ      clnup           ; JUMP IF SO, DO NOT PLACE CHARACTER IN BFR
savch:                          
        mov     [bx],AL         ;SAVE NEW CHARACTER IN circ_buf
        inc     circ_ct         ;BUMP circ_buf COUNT
        CMP     bx,circ_top     ;ARE WE AT THE TOP OF THE circ_buf?
        JZ      reset_in        ;JUMP IF SO
        inc     bx              ;ELSE, BUMP PTR
        JMP SHORT into_buf
reset_in:
        mov     bx,OFFSET circ_buf      ;RESET circ_in TO BOTTOM OF BUF.
into_buf:
        mov     circ_in,bx              ;SAVE NEW PTR
clnup:
        mov     AL,RSTINT
        OUT     RS8259,AL       ;ISSUE SPECIFIC EOI FOR 8259
        pop     ds              ;GET BACK ENTERING DS
        pop     ax
        pop     bx
        pop     dx
        pop     cx
        sti
        iret

_TEXT   ENDS

end
@@@ Fin de comport.asm
exit 0
-- 
Copyright 1987 John Gilmore; you can redistribute only if your recipients can.
(This is an effort to bend Stargate to work with Usenet, not against it.)
{sun,ptsfa,lll-crg,ihnp4,ucbvax}!hoptoad!gnu	       g...@ingres.berkeley.edu

			  SCO's Case Against IBM

November 12, 2003 - Jed Boal from Eyewitness News KSL 5 TV provides an
overview on SCO's case against IBM. Darl McBride, SCO's president and CEO,
talks about the lawsuit's impact and attacks. Jason Holt, student and 
Linux user, talks about the benefits of code availability and the merits 
of the SCO vs IBM lawsuit. See SCO vs IBM.

Note: The materials and information included in these Web pages are not to
be used for any other purpose other than private study, research, review
or criticism.