MODULE KERMSG (IDENT = '1.0.001'
		) =
BEGIN

SWITCHES LANGUAGE (COMMON);

!++
! FACILITY:
!   KERMIT-32/36
!
! ABSTRACT:
!	KERMSG is the message processing routines for KERMIT-32/36.
!	This module is written in common BLISS, so that it can be
!	transported for the DECsystem-10 and VAX/VMS systems.
!
! ENVIRONMENT:
!   User mode
!
! AUTHOR: Robert C. McQueen, CREATION DATE: 24-January-1983
!
! MODIFIED BY:
!
!--

%SBTTL 'Table of Contents'
%SBTTL 'Revision History'

!++
! Start of version 1.
!
! 1.0.000	Create this program.
!--

%SBTTL 'Interface requirements'

!++
!		Interface requirements
!
! The following routines and data locations are rquired for a correct
! implementation of KERMIT.
!
! File routines:
!
!	FILE_OPEN (Function)
!		This routine will open a file for reading or writting.  It
!		will assume that FILE_SIZE contains the number of bytes
!		and FILE_NAME contains the file name of length FILE_SIZE.
!		The function that is passed is either FNC_READ or FNC_WRITE.
!
!	FILE_CLOSE ()
!		This routine will close the currently open file.  This
!		routine will return the status of the operation.
!
!	FILE_DUMP ()
!		This routine will cause the current record to be written to
!		the file, this routine will only handle binary files if the
!		low level I/O routines are record oriented.  Otherwise it is
!		not needed.
!
!	GET_FILE (Character)
!		This routine will get a character from the currently open file
!		and store it in the location specified by "Character".  There
!		will be a true/false value returned by the routine to determine
!		if there was an error.
!
!	PUT_FILE (Character)
!		This routine will output a character to the currently open
!		file.  It will return a true/false value to determine if the
!		routine was successful.
!
!	NEXT_FILE ()
!		This routine will advance to the next file.  This routine
!		will return false if there are no more files to process.
!
! Communications line routines:
!
!	RECEIVE
!		This routine will receive a message from the remote Kermit.
!
!	SEND
!		This routine will send a message to the remote Kermit.
!
! Operating system routines:
!
!	DISMISS (Seconds)
!		This routine will cause Kermit to sleep for the specified
!		number of seconds.  It is used to handle the DELAY parameter.
!
!	SYS_LOGOUT ()
!		Log the job off of the system. (Kill the process).
!
! Error processing:
!
!	KRM_ERROR (Error parameter)
!		This routine will cause an error message to be issued.
!		The error parameter is defined by KERERR.  This may cause
!		SND_ERROR to be called to send an "E" message to the remote.
!
! Terminal I/O routines:
!
!	TT_CHAR (Character)
!		This routine will cause a character to be output to
!		the terminal.  This routine is called only if the terminal
!		and communications line are different.
!
!	TT_CRLF ()
!		This routine will cause a carriage-return line-feed to be
!		output to the terminal.  This routine is called only if the
!		terminal and communications line are different.
!
!	TT_NUMBER (Value)
!		This routine will output a three digit number to the terminal.
!		This routine is called only if the terminal and communications
!		line are different.
!
!	TT_QCHAR (Character)
!		This routine will output a quoted character.  This routine is
!		similar to TT_CHAR.
!
!	TT_TEXT (ASCIZ string)
!		This routine will output an ASCIZ string to the terminal.
!		This routine is called only if the terminal and communications
!		line are different.
!
!
!			ENTRY POINTS
!
! KERMSG contains the following entry points for the KERMIT.
!
!	SERVER ()
!		This routine will cause KERMIT go enter server mode.
!
!	SEND_SWITCH ()
!		This routine will send a file.  It expects that the user
!		has stored the text of the file name into FILE_NAME and
!		the length of the text into FILE_SIZE.
!
!	REC_SWITCH ()
!		This routine will receive a file.  It expects that the default
!		file name is set up in FILE_NAME and the length is in
!		FILE_SIZE.
!
!
!		GLOBAL Storage
!
! The following are the global storage locations that are used to interface
! to KERMSG.  These locations contains the various send and receive parameters.
!
! Receive parameters:
!
!	RCV_PKT_SIZE
!		Receive packet size.
!	RCV_NPAD
!		Padding length
!	RCV_PADCHAR
!		Padding character
!	RCV_TIMEOUT
!		Time out
!	RCV_EOL
!		End of line character
!	RCV_QUOTE_CHR
!		Quote character
!	RCV_8QUOTE_CHR
!		8-bit quoting character
!
! Send parameters:
!
!	SND_PKT_SIZE
!		Send packet size
!	SND_NPAD
!		Padding length
!	SND_PADCHAR
!		Padding character
!	SND_TIMEOUT
!		Time out
!	SND_EOL
!		End of line character
!	SND_QUOTE_CHR
!		Quote character
!	SND_8QUOTE_CHR
!		8-bit quoting character
!
! Statistics:
!
!	SND_TOTAL_CHARS
!		Total characters sent
!	RCV_TOTAL_CHARS
!		Total characters received
!	SND_DATA_CHARS
!		Total number of data characters sent
!	RCV_DATA_CHARS
!		Total number of data characters received
!
! Misc constants:
!
!	FILE_NAME
!		Vector containing the ASCII characters of the file name.
!	FILE_SIZE
!		Number of characters in the FILE_NAME vector.
!	DELAY
!		Amount of time to delay
!	DEBUG_FLAG
!		Debugging mode on/off
!	WARN_FLAG
!		File warning flag
!	ECHO_FLAG
!		Local echo flag
!	CONNECT_FLAG
!		Connected flag; True if terminal and SET LINE are the same
!	PARITY_TYPE
!		Type of parity to use if 8-bit mode.
!
!--

%SBTTL 'Declarations -- Forward definitions'
!<BLF/NOFORMAT>
!
! Forward definitions
!

FORWARD ROUTINE

! Send processing routines

    SEND_SWITCH,		! Major send routine
    SEND_DATA,			! Send data to the micro
    SEND_FILE,			! Send file name
    SEND_EOF,			! Send EOF
    SEND_INIT,			! Send initialization msg
    SEND_BREAK,			! Send break end of transmission
    SND_ERROR	: NOVALUE,	! Send an error to the remote

! Receive processing routines

    REC_SWITCH,			! Receive main loop
    REC_SWITCH_WORKER,		! Worker routine for REC_SWITCH and SERVER
    REC_INIT,			! Receive initialization
    REC_FILE,			! Receive file information
    REC_DATA,			! Receive data

! Server processing routines

    SERVER_GENERIC,		! Process generic KERMIT commands
!
! Statistic gathering routines

    INIT_STATS	: NOVALUE,	! Initialize the stats area

! Low level send/receive routines

    SET_SEND_INIT : NOVALUE,	! Set up the MSG_SND_INIT parameters.
    PRS_SEND_INIT : NOVALUE,	! Parse MSG_SND_INIT parameters.
    SEND_PACKET,		! Send a packet to the remote
    REC_MESSAGE,		! Receive a message with retry processing
    REC_PACKET,			! Receive a packet from the remote

! Utility routines

    BFR_EMPTY,			! Empty the data buffer
    BFR_FILL,			! Fill the data buffer from a file
    CLEAR	: NOVALUE,	! Clear the message buffer.
!
! Debugging routines
!
    DBG_MESSAGE	: NOVALUE,	! Type out a formatted message
    DBG_SEND	: NOVALUE,	! Send message debugging routine
    DBG_RECEIVE	: NOVALUE;	! Receive message debugging routine
	%SBTTL	'Require files'

!
!<BLF/FORMAT>
!
! REQUIRE FILES:
!

%IF %BLISS (BLISS32)
%THEN

LIBRARY 'SYS$LIBRARY:STARLET';

%FI						! End of %IF %BLISS(BLISS32)

REQUIRE 'KERCOM';

REQUIRE 'KERERR';

%SBTTL 'Macro definitions'
!
! MACROS:
!

MACRO
    CTL (C) =
 C XOR %O'100'%,
    CHAR (C) =
 C + %O'40'%,
    UNCHAR (C) =
 C - %O'40'%;

%SBTTL 'KERMIT Protocol Definitions'

!++
! The following describes the various items that are found in the
! KERMIT messages.  A complete and through desription of the protocol can be
! found in the KERMIT PROTOCOL MANUAL.
!
!
! All KERMIT messages have the following format:
!
! <Mark><CHAR(Count)><CHAR(Seq)><Message-dependent information><CHAR(Chksum)><EOL>
!
! <MARK>
!	Predefined as SOH (Control-A, octal 001).
!
! <CHAR(Count)>
!	Count of the number of characters following this position.
!	Character counts of ONLY 0 to 94 are valid.
!
! <CHAR(Seq)>
!	Packet sequence number, modulo 100 (octal).
!
! <MESSAGE-DEPENDENT INFORMATION>
!	This field contains the message dependent information.  There can
!	be multiple fields in this section.  See the KERMIT Protocol document
!	for a complete description of this.
!
! <CHAR(Chksum)>
!	Checksum of the packet.
!
! <EOL>
!	End of line.  Any line terminator that may be required by the host.
!--

%SBTTL 'KERMIT Protocol Definitions -- Packet offsets'

!++
! The following define the various offsets of the standard KERMIT
! packets.
!--

LITERAL
    PKT_MARK = 0,				! <MARK>
    PKT_COUNT = 1,				! <CHAR(Count)>
    PKT_SEQ = 2,				! <CHAR(Seq)>
    PKT_TYPE = 3,				! <Message type>
    PKT_MSG = 4,				! <MESSAGE-DEPENDENT INFORMATION>
    PKT_MAX_MSG = 94 - 5,			! Maximum size of the message dependent
    						!  information
    PKT_CHKSUM = 0,				! <CHAR(Chksum)> offset from end of
    						!    Message dependent information
    PKT_EOL = 1,				! <Eol> offset from end of data
    PKT_OVR_HEAD_B = 2,				! Header overhead
    PKT_OVR_HEAD_E = 1,				! Overhead at the end
    PKT_OVR_HEAD = 3,				! Overhead added to data length
    PKT_TOT_OVR_HEAD = 6;			! Total overhead of the message

%SBTTL 'KERMIT Protocol Definitions -- Message dependent field'

!++
! The MESSAGE-DEPENDENT information field of the message contains at
! least one part.  That is the type of message.  The remainder of the message
! MESSAGE-DEPENDENT field is different depending on the message.
!
! <TYPE><TYPE-DEPENDENT-INFORMATION>
!
! <TYPE>
!	The type defines the type of message that is being processed.
!
!--

! Protocol version 1.0 message types

LITERAL
    MSG_DATA = %C'D',				! Data packet
    MSG_ACK = %C'Y',				! Acknowledgement
    MSG_NAK = %C'N',				! Negative acknowledgement
    MSG_SND_INIT = %C'S',			! Send initiate
    MSG_BREAK = %C'B',				! Break transmission
    MSG_FILE = %C'F',				! File header
    MSG_EOF = %C'Z',				! End of file (EOF)
    MSG_ERROR = %C'E';				! Error

! Protocol version 2.0 message types

LITERAL
    MSG_RCV_INIT = %C'R',			! Receive initiate
    MSG_COMMAND = %C'C',			! Host command
    MSG_KERMIT = %C'G';				! Generic KERMIT command.

!++
! Generic KERMIT commands
!--

LITERAL
    MSG_GEN_LOGIN = %C'I',			! Login
    MSG_GEN_EXIT = %C'F',			! Finish (exit to OS)
    MSG_GEN_CONNECT = %C'C',			! Connect to a directory
    MSG_GEN_LOGOUT = %C'L',			! Logout
    MSG_GEN_DIRECTORY = %C'D',			! Directory
    MSG_GEN_DISK_USAGE = %C'U',			! Disk usage
    MSG_GEN_DELETE = %C'E',			! Delete a file
    MSG_GEN_TYPE = %C'T',			! Type a file specification
    MSG_GEN_SUBMIT = %C'S',			! Submit
    MSG_GEN_PRINT = %C'P',			! Print
    MSG_GEN_WHO = %C'W',			! Who's logged in
    MSG_GEN_SEND = %C'M',			! Send a message to a user
    MSG_GEN_HELP = %C'H',			! Help
    MSG_GEN_QUERY = %C'Q';			! Query status

%SBTTL 'KERMIT Protocol Definitions -- SEND initiate packet'

!++
!
! The following describes the send initiate packet.  All fields in the message
! data area are optional.
!
! <"S"><CHAR(Bufsiz)><CHAR(Timeout)><CHAR(npad)><CTL(pad)><CHAR(Eol)><Quote>
!	<8-bit-quote><Reserved><Reserved><Reserved><Reserved>
!
! BUFSIZ
!	Sending Kermit's maximum buffer size.
!
! Timeout
!	Number of seconds after which the sending Kermit wishes to be timed out
!
! Npad
!	Number of padding caracters the sending Kermit needs preceding each
!	packet.
!
! PAD
!	Padding character.
!
! EOL
!	A line terminator required on all packets set by the receiving
!	Kermit.
!
! Quote
!	The printable ASCII characer the sending Kermit will use when quoting
!	the control cahracters.  Default is "#".
!
! 8-bit-quote
!	Specify quoting mecanism for 8-bit quantities.  A quoting mecanism is
!	mecessary when sending to hosts which prevent the use of the 8th bit
!	for data.  When elected, the quoting mechanism will be used by both
!	hosts, and the quote character must be in the range of 41-76 or 140-176
!	octal, but different from the control-quoting character.  This field is
!	interpreted as follows:
!
!	"Y" - I agree to 8-bit quoting if you request it.
!	"N" - I will not do 8-bit quoting.
!	"&" - (or any other character in the range of 41-76 or 140-176) I want
!	      to do 8-bit quoting using this character (it will be done if the
!	      other Kermit puts a "Y" in this field.
!	Anything else: Quoting will not be done.
!
! Fields 8 to 11 reserved.
!--

LITERAL
    P_SI_BUFSIZ = 0,				! Buffersize
    MY_PKT_SIZE = 80,				! My packet size
    P_SI_TIMOUT = 1,				! Time out
    MY_TIME_OUT = 15,				! My time out
    P_SI_NPAD = 2,				! Number of padding characters
    MY_NPAD = 0,				! Amount of padding I require
    P_SI_PAD = 3,				! Padding character
    MY_PAD_CHAR = 0,				! My pad character
    P_SI_EOL = 4,				! End of line character
    MY_EOL_CHAR = %O'015',			! My EOL cahracter
    P_SI_QUOTE = 5,				! Quote character
    MY_QUOTE_CHAR = %C'#',			! My quoting character
    P_SI_8QUOTE = 6,				! 8-bit quote
    MY_8BIT_QUOTE = %C'N',			! Don't do it
    P_SI_LENGTH = 7;				! Length of the message

%SBTTL 'KERMIT Protocol States'

!++
! The following are the various states that KERMIT can be in.
! The state transitions are defined in the KERMIT Protocol manual.
!--

LITERAL
    STATE_MIN = 0,				! Min state number
    STATE_START = 0,				! Start state
    STATE_S = 1,				! Send init state
    STATE_SF = 2,				! Send file header
    STATE_SD = 3,				! Send file data packet
    STATE_SZ = 4,				! Send EOF packet
    STATE_SB = 5,				! Send break
    STATE_R = 6,				! Receive state (wait for send-init)
    STATE_RF = 7,				! Receive file header packet
    STATE_RD = 8,				! Receive file data packet
    STATE_C = 9,				! Send complete
    STATE_A = 10,				! Abort
    STATE_MAX = 10;				! Max state number

%SBTTL 'Storage - Global'
!
! OWN STORAGE:
!

GLOBAL
!
! Receive parameters
!
    RCV_PKT_SIZE,				! Receive packet size
    RCV_NPAD,					! Padding length
    RCV_PADCHAR,				! Padding character
    RCV_TIMEOUT,				! Time out
    RCV_EOL,					! EOL character
    RCV_QUOTE_CHR,				! Quote character
    RCV_8QUOTE_CHR,				! 8-bit quoting character
!
! Send parameters
!
    SND_PKT_SIZE,				! Send packet size
    SND_NPAD,					! Padding length
    SND_PADCHAR,				! Padding character
    SND_TIMEOUT,				! Time out
    SND_EOL,					! EOL character
    SND_QUOTE_CHR,				! Quote character
    SND_8QUOTE_CHR,				! 8-bit quoting character
!
! Statistics
!
    SND_TOTAL_CHARS,				! Total characters sent
    RCV_TOTAL_CHARS,				! Total characters received
    SND_DATA_CHARS,				! Total number of data characters sent
    RCV_DATA_CHARS,				! Total number of data characters received
!
! Misc constants.
!
    FILE_NAME : VECTOR [CH$ALLOCATION (MAX_FILE_NAME, CHR_SIZE)],
    FILE_SIZE,
    DELAY,					! Amount of time to delay
    PARITY_TYPE,				! Type of parity to use
    DEBUG_FLAG,					! Debugging mode on/off
    WARN_FLAG,					! File warning flag
    ECHO_FLAG,					! Local echo flag
    CONNECT_FLAG;				! Connected flag; True if

						!  terminal and SET LINE are
						!  the same
%SBTTL 'Storage - Local'
!
! LOCAL OWN STORAGE:
!

OWN
    STATE,					! Current state
    SIZE,					! Size of the current message
    OLD_RETRIES,				! Saved number of retries done.
    NUM_RETRIES,				! Number of retries
    MSG_NUMBER,					! Current message number
    REC_SEQ,					! Sequence number of msg in REC_MSG
    REC_LENGTH,					! Length of the message recv'd
    REC_TYPE,					! Type of the message received.
    REC_MSG : VECTOR [CH$ALLOCATION (MAX_MSG, CHR_SIZE)],	! Message received
    SND_MSG : VECTOR [CH$ALLOCATION (MAX_MSG, CHR_SIZE)],	! Message sent
    FILE_OPEN_FLAG;				! File is opened.

%SBTTL 'External references'
!
! EXTERNAL REFERENCES:
!
! Packet I/O routines

EXTERNAL ROUTINE
    SEND,					! Send a packet to the remote
    RECEIVE;					! Receive a packet from the remote

!
! Terminal I/O routines
!

EXTERNAL ROUTINE
    TT_QCHAR : NOVALUE,				! Output a single quoted character
    TT_CHAR : NOVALUE,				! Output a single character
    TT_CRLF : NOVALUE,				! Output a CRLF
    TT_NUMBER : NOVALUE,			! Output a three digit number to the
    						!  terminal
    TT_TEXT : NOVALUE;				! Output a string to the user's

! Operating system routines

EXTERNAL ROUTINE
    KRM_ERROR : NOVALUE,			! Issue an error message
    SYS_LOGOUT : NOVALUE,			! Log the job off
    DISMISS : NOVALUE;				! Routine to dismiss for n seconds.

!
! External file processing routines
!

EXTERNAL ROUTINE
    FILE_OPEN,					! Open a file for reading/writing
    FILE_CLOSE,					! Close an open file
    NEXT_FILE,					! Determine if there is a next file
    						!  and open it for reading.
    GET_FILE,					! Get a byte from the file
    PUT_FILE,					! Put a byte in the file.
    FILE_DUMP;					! Dump the contents of the current

						!  record.  Used for binary files
						!  if needed.
%SBTTL 'MSG_INIT'

GLOBAL ROUTINE MSG_INIT : NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will initialize the message processing for
!	KERMIT-32/36.
!
! CALLING SEQUENCE:
!
!	MSG_INIT();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN
!
! Initialize some variables
!
! Receive parameters first
!
    RCV_PKT_SIZE = MY_PKT_SIZE;
    RCV_NPAD = MY_NPAD;
    RCV_PADCHAR = MY_PAD_CHAR;
    RCV_TIMEOUT = MY_TIME_OUT;
    RCV_EOL = MY_EOL_CHAR;
    RCV_QUOTE_CHR = MY_QUOTE_CHAR;
    RCV_8QUOTE_CHR = MY_8BIT_QUOTE;
!
! Send parameters.
!
    SND_PKT_SIZE = MY_PKT_SIZE;
    SND_NPAD = MY_NPAD;
    SND_PADCHAR = MY_PAD_CHAR;
    SND_TIMEOUT = MY_TIME_OUT;
    SND_EOL = MY_EOL_CHAR;
    SND_QUOTE_CHR = MY_QUOTE_CHAR;
    SND_8QUOTE_CHR = MY_8BIT_QUOTE;
!
! Other random parameters
!
    DELAY = INIT_DELAY;
    DEBUG_FLAG = FALSE;
    WARN_FLAG = FALSE;
    ECHO_FLAG = FALSE;
    FILE_OPEN_FLAG = FALSE;
    END;					! End of MSG_INIT

%SBTTL 'SERVER - Server mode'

GLOBAL ROUTINE SERVER =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will handle the server function in the v2.0 protocol
!	for KERMIT.  This routine by it's nature will call various operating
!	system routines to do things like logging off the system.
!
! CALLING SEQUENCE:
!
!	EXIT_FLAG = SERVER();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN

    LOCAL
	STATUS;					! Low level status returned

!++
!
! This routine will determine if the type of message receive is valid for
! this routine.  This is a co-routine for the REC_MESSAGE routine.  This
! is called once we are sure that message parses correctly.
!
!--

    ROUTINE CHK_SERVER =
	BEGIN

	IF .REC_TYPE EQL MSG_SND_INIT OR .REC_TYPE EQL MSG_KERMIT OR .REC_TYPE EQL MSG_RCV_INIT
	THEN
	    RETURN TRUE
	ELSE
	    RETURN FALSE;

	END;
    RETURN

	WHILE TRUE DO
	    BEGIN
	    STATUS = REC_MESSAGE (CHK_SERVER);
!
! Now determine what to do by the type of message we have receive.
! The type can only be one of the type checked by the CHK_SERVER routine
!

	    SELECTONE .REC_TYPE OF
		SET

		[MSG_SND_INIT] :
		    BEGIN
		    MSG_NUMBER = (.REC_SEQ + 1) AND %O'77';
		    PRS_SEND_INIT();
		    SET_SEND_INIT();
		    IF SEND_PACKET (MSG_ACK, P_SI_LENGTH, .REC_SEQ) THEN
			BEGIN
			STATE = STATE_RF;
			REC_SWITCH_WORKER ();
			END;
		    END;

		[MSG_RCV_INIT] :
		    BEGIN
		    MSG_NUMBER = .REC_SEQ;
		    IF .REC_LENGTH GTR 0
		    THEN
			BEGIN
			CH$MOVE (.REC_LENGTH, CH$PTR (REC_MSG, PKT_MSG, CHR_SIZE),
			 CH$PTR(FILE_NAME, 0, CHR_SIZE));
			FILE_SIZE = .REC_LENGTH;
			END;

		    DELAY = 0;
		    SEND_SWITCH ();
		    END;
!
! Generic KERMIT commands
!

		[MSG_KERMIT] :
		    BEGIN
		    STATUS = SERVER_GENERIC ();

		    IF .STATUS EQL KER_EXIT THEN EXITLOOP KER_EXIT;

		    END;
!
! Unimplimented server routines
!

		[OTHERWISE] :
		    KRM_ERROR (KER_UNISRV);
		TES;

	    END;

    END;					! End of GLOBAL ROUTINE SERVER

%SBTTL 'SERVER - Generic commands'
ROUTINE SERVER_GENERIC =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will handle the generic server messages.
!	The generic server messages include FINISH, LOGOUT.
!
! CALLING SEQUENCE:
!
!	SERVER_GENERIC();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	Generic message receive in REC_MSG.
!
! OUTPUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN

    SELECTONE CH$RCHAR (CH$PTR (REC_MSG, PKT_MSG, CHR_SIZE)) OF
	SET

	[MSG_GEN_EXIT] :
	    BEGIN
	    SEND_PACKET (MSG_ACK, 0, .REC_SEQ);
	    RETURN KER_EXIT;
	    END;
    [MSG_GEN_LOGOUT] :
	BEGIN
	SEND_PACKET (MSG_ACK, 0, .REC_SEQ);
	SYS_LOGOUT ();
	RETURN KER_NORMAL;
	END;
!
!    [MSG_GEN_DELETE] :
!	BEGIN
!	END;
!
!    [MSG_GEN_PRINT] :
!	BEGIN
!	END;

	[OTHERWISE] :
	    BEGIN
	    KRM_ERROR (KER_UNIMPLGEN);
	    RETURN KER_UNIMPLGEN;
	    END;
	TES;

    END;					! End of SERVER_GENERIC
%SBTTL 'SEND_SWITCH'

GLOBAL ROUTINE SEND_SWITCH =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine is the state table switcher for sending files.  It
!	loops until either it is finished or an error is encountered.  The
!	routines called by SEND_SWITCH are responsible for changing the state.
!
! CALLING SEQUENCE:
!
!	SEND_SWITCH();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	Returns:
!	    TRUE - File sent correctly.
!	    FALSE - Aborted sending the file.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN

    LOCAL
	VAL_RETURN,
	FINISHED;

    INIT_STATS ();				! Initialize stats
    STATE = STATE_S;				! Initial state is send
    NUM_RETRIES = 0;				! Initialize number of retries
    MSG_NUMBER = 0;				! Initial message number
    CLEAR ();					! Clear the message buffers
    DISMISS (.DELAY);				! Sleep if the user wanted us to
    FINISHED = FALSE;

    DO
	BEGIN

	CASE .STATE FROM STATE_MIN TO STATE_MAX OF
	    SET

	    [STATE_SD] :
		STATE = SEND_DATA ();

	    [STATE_SF] :
		STATE = SEND_FILE ();

	    [STATE_SZ] :
		STATE = SEND_EOF ();

	    [STATE_S] :
		STATE = SEND_INIT ();

	    [STATE_SB] :
		STATE = SEND_BREAK ();

	    [STATE_C] :
		BEGIN
		FINISHED = TRUE;
		VAL_RETURN = TRUE;
		END;

	    [STATE_A] :
		BEGIN

		IF .FILE_OPEN_FLAG
		THEN
		    BEGIN
		    FILE_CLOSE ();
		    FILE_OPEN_FLAG = FALSE;
		    END;

		FINISHED = TRUE;
		VAL_RETURN = FALSE;
		END;

	    [INRANGE, OUTRANGE] :
		BEGIN
		FINISHED = TRUE;
		VAL_RETURN = FALSE;
		END;
	    TES

	END
    UNTIL (.FINISHED EQL TRUE);

    RETURN .VAL_RETURN;
    END;

%SBTTL 'SEND_DATA'
ROUTINE SEND_DATA =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will send a data message to the remote KERMIT.
!
! CALLING SEQUENCE:
!
!	STATE = SEND_DATA();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	New state to change the finite state machine to.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN
!
! Check to see if the number of retries have been exceeded.
!

    IF .NUM_RETRIES GTR MAX_RETRIES THEN RETURN STATE_A;

!
! Not exceeded yet.  Increment the number of retries we have attempted
! on this message.
!
    NUM_RETRIES = .NUM_RETRIES + 1;
!
! Attempt to send the packet and abort if the send fails.
!

    IF NOT SEND_PACKET (MSG_DATA, .SIZE, .MSG_NUMBER) THEN RETURN STATE_A;

!
! Attempt to receive a message from the remote KERMIT.
!

    IF NOT REC_PACKET () THEN RETURN STATE_A;

!
! Determine if the message is a NAK and the NAK is for the message number
! that we are current working on.  If the NAK is for the next packet then
! ignore the NAK
!

    IF .REC_TYPE EQL MSG_NAK AND .REC_SEQ NEQ ((.MSG_NUMBER + 1) AND %O'77') THEN RETURN .STATE;

!
! Make sure we have a NAK or ACK
!

    IF .REC_TYPE EQL MSG_ACK OR .REC_TYPE EQL MSG_NAK
    THEN
	BEGIN
!
! Is this for this message?
!

	IF .REC_SEQ NEQ .MSG_NUMBER THEN RETURN .STATE;

!
! It was.  Set up for sending the next data message to the remote KERMIT
! and return.
!
	NUM_RETRIES = 0;
	MSG_NUMBER = (.MSG_NUMBER + 1) AND %O'77';

	IF (BFR_FILL () EQL KER_NORMAL)
	THEN
	    RETURN STATE_SD
	ELSE
	    BEGIN
	    FILE_CLOSE ();
	    FILE_OPEN_FLAG = FALSE;
	    RETURN STATE_SZ;
	    END;

	END
    ELSE
!
! Not an ACK or NAK, abort.
!
	RETURN STATE_A;

    END;
%SBTTL 'SEND_FILE'
ROUTINE SEND_FILE =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will send the file specification that is being
!	transfered.
!
! CALLING SEQUENCE:
!
!	STATE = SEND_FILE();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	New state to change the finite state machine to.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN
!
! First determine if we have exceed the number of retries that are
! allowed to attempt to send this message.
!

    IF .NUM_RETRIES GTR MAX_RETRIES THEN RETURN STATE_A;

!
! The number of retries are not exceeded.  Increment the number and then
! attempt to send the packet again.
!
    NUM_RETRIES = .NUM_RETRIES + 1;

    IF .FILE_SIZE NEQ 0
    THEN
	CH$MOVE (.FILE_SIZE, CH$PTR (FILE_NAME, 0, CHR_SIZE),
	    CH$PTR (SND_MSG, PKT_MSG, CHR_SIZE));

    IF NOT SEND_PACKET (MSG_FILE, .FILE_SIZE, .MSG_NUMBER) THEN RETURN STATE_A;

!
! Now get the responce from the remote KERMIT.
!

    IF NOT REC_PACKET () THEN RETURN STATE_A;

!
! Determine if the packet is good.
!

    IF NOT (.REC_TYPE EQL MSG_ACK OR .REC_TYPE EQL MSG_NAK) THEN RETURN STATE_A;

!
! If this is a NAK and the message number is not the one we just send
! treat this like an ACK, otherwise resend the last packet.
!

    IF .REC_TYPE EQL MSG_NAK AND .REC_SEQ NEQ ((.MSG_NUMBER + 1) AND %O'77') THEN RETURN .STATE;

    IF .REC_SEQ NEQ .MSG_NUMBER THEN RETURN .STATE;

!
! Here to send the file name to the other end.
!
    NUM_RETRIES = 0;
    MSG_NUMBER = (.MSG_NUMBER + 1) AND %O'77';

    IF BFR_FILL () THEN RETURN STATE_SD ELSE RETURN STATE_A;

    END;					! End of
%SBTTL 'SEND_EOF'
ROUTINE SEND_EOF =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will send the end of file message to the remote
!	KERMIT.  It will then determine if there are more files to
!	send to the remote.
!
! CALLING SEQUENCE:
!
!	STATE = SEND_EOF();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	New state to change the finite state machine to.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	Sets up for the next file to be processed if there is one.
!
!--

    BEGIN

    LOCAL
	STATUS;					! Local status of routine

!
! First determine if we have exceed the number of retries that are
! allowed to attempt to send this message.
!

    IF .NUM_RETRIES GTR MAX_RETRIES THEN RETURN STATE_A;

!
! The number of retries are not exceeded.  Increment the number and then
! attempt to send the packet again.
!
    NUM_RETRIES = .NUM_RETRIES + 1;

    IF NOT SEND_PACKET (MSG_EOF, 0, .MSG_NUMBER) THEN RETURN STATE_A;

!
! Now get the responce from the remote KERMIT.
!

    IF NOT REC_PACKET () THEN RETURN STATE_A;

!
! Determine if the packet is good.
!

    IF NOT (.REC_TYPE EQL MSG_ACK OR .REC_TYPE EQL MSG_NAK) THEN RETURN STATE_A;

!
! If this is a NAK and the message number is not the one we just send
! treat this like an ACK, otherwise resend the last packet.
!

    IF .REC_TYPE EQL MSG_NAK AND .REC_SEQ NEQ ((.MSG_NUMBER + 1) AND %O'77') THEN RETURN .STATE;

    IF .REC_SEQ NEQ .MSG_NUMBER THEN RETURN .STATE;

!
! Here to determine if there is another file to send.
!
    NUM_RETRIES = 0;
    MSG_NUMBER = (.MSG_NUMBER + 1) AND %O'77';
    STATUS = NEXT_FILE ();

    IF ( NOT .STATUS) OR (.STATUS EQL KER_NOMORFILES) THEN RETURN STATE_SB ELSE RETURN STATE_SF;

    END;
%SBTTL 'SEND_INIT'
ROUTINE SEND_INIT =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will send the initialization packet to the remote
!	KERMIT.  The message type sent is S.
!
! CALLING SEQUENCE:
!
!	STATE = SEND_INIT();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	New state to change the finite state machine to.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN
    SET_SEND_INIT ();

    IF .NUM_RETRIES GTR MAX_RETRIES THEN RETURN STATE_A;

    IF NOT SEND_PACKET (MSG_SND_INIT, P_SI_LENGTH, .MSG_NUMBER) THEN RETURN STATE_A;

    IF NOT REC_PACKET () THEN RETURN STATE_A;

!
! Determine if the packet is good.
!

    IF NOT (.REC_TYPE EQL MSG_ACK OR .REC_TYPE EQL MSG_NAK) THEN RETURN STATE_A;

!
! If this is a NAK and the message number is not the one we just send
! treat this like an ACK, otherwise resend the last packet.
!

    IF .REC_TYPE EQL MSG_NAK AND .REC_SEQ NEQ ((.MSG_NUMBER + 1) AND %O'77') THEN RETURN .STATE;

    IF .REC_SEQ NEQ .MSG_NUMBER THEN RETURN .STATE;

!
! Here if we have an ACK for the initialization message that was just sent
! to the remote KERMIT.
!

    PRS_SEND_INIT ();
    NUM_RETRIES = 0;
    MSG_NUMBER = (.MSG_NUMBER + 1) AND %O'77';

    IF NOT FILE_OPEN (FNC_READ)
    THEN
	RETURN STATE_A
    ELSE
	BEGIN
	FILE_OPEN_FLAG = TRUE;
	RETURN STATE_SF;
	END;

    END;
%SBTTL 'SEND_BREAK'
ROUTINE SEND_BREAK =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will send the break (end of transmission) message
!	to the remote KERMIT.  On an ACK the state becomes STATE_C.
!
! CALLING SEQUENCE:
!
!	STATE = SEND_BREAK();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	New state for the finite state machine.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN
!
! First determine if we have exceed the number of retries that are
! allowed to attempt to send this message.
!

    IF .NUM_RETRIES GTR MAX_RETRIES THEN RETURN STATE_A;

!
! The number of retries are not exceeded.  Increment the number and then
! attempt to send the packet again.
!
    NUM_RETRIES = .NUM_RETRIES + 1;

    IF NOT SEND_PACKET (MSG_BREAK, 0, .MSG_NUMBER) THEN RETURN STATE_A;

!
! Now get the responce from the remote KERMIT.
!

    IF NOT REC_PACKET () THEN RETURN STATE_A;

!
! Determine if the packet is good.
!

    IF NOT (.REC_TYPE EQL MSG_ACK OR .REC_TYPE EQL MSG_NAK) THEN RETURN STATE_A;

!
! If this is a NAK and the message number is not the one we just send
! treat this like an ACK, otherwise resend the last packet.
!

    IF .REC_TYPE EQL MSG_NAK AND .REC_SEQ NEQ ((.MSG_NUMBER + 1) AND %O'77') THEN RETURN .STATE;

    IF .REC_SEQ NEQ .MSG_NUMBER THEN RETURN .STATE;

!
! Here to determine if there is another file to send.
!
    NUM_RETRIES = 0;
    MSG_NUMBER = (.MSG_NUMBER + 1) AND %O'77';
    RETURN STATE_C;
    END;
%SBTTL 'SND_ERROR'

GLOBAL ROUTINE SND_ERROR (COUNT, ADDRESS) : NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will send an error packet to the remote KERMIT.  It
!	is called with the count of characters and the address of the text.
!
! CALLING SEQUENCE:
!
!	SND_ERROR(COUNT, %ASCII 'Error text');
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!
!--

    BEGIN

    LOCAL
	CHR,					! Character we are processing
	SIZE,					! Size of data area
	STO_PTR,				! Pointer used to store chars
	FET_PTR;				! Pointer used to fetch characters

!
! Initialize the various pointers and counter.
!
    FET_PTR = CH$PTR (.ADDRESS);
    STO_PTR = CH$PTR (SND_MSG, PKT_MSG, CHR_SIZE);
    SIZE = (IF .COUNT GTR .SND_PKT_SIZE - PKT_TOT_OVR_HEAD THEN .SND_PKT_SIZE - PKT_TOT_OVR_HEAD ELSE .COUNT);
!
! Move the information from the source to the destination
!
    CH$MOVE (.SIZE, .FET_PTR, .STO_PTR);

    IF NOT SEND_PACKET (MSG_ERROR, .SIZE, .MSG_NUMBER) THEN RETURN STATE_A;

    END;					! End of SND_ERROR

%SBTTL 'REC_SWITCH'

GLOBAL ROUTINE REC_SWITCH =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will cause file(s) to be received by the remote
!	KERMIT.  This routine contains the main loop for the sending of the
!	data.
!
! CALLING SEQUENCE:
!
!	REC_SWITCH();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	FILE_DESC - Descriptor describing the file to be received by
!		the remote KERMIT.
!
! OUTPUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	True - File received correctly.
!	FALSE - File transfer aborted.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN

    STATE = STATE_R;				! Initialize the state
    RETURN REC_SWITCH_WORKER();
END;				! End of REC_SWITCH

	%SBTTL	'Receive - REC_SWITCH_WORKER'

ROUTINE REC_SWITCH_WORKER =
!++
! FUNCTIONAL DESCRIPTION:
!
!	This is the worker routine for either REC_SWITCH or SERVER.
!	This routine will be called with the STATE variable set to the
!	correct state for either the SERVER or the REC_SWITCH routine.
!
! CALLING SEQUENCE:
!
!	Status = REC_SWITCH_WORKER();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUPTUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

BEGIN

    LOCAL
	RETURN_VALUE;

    INIT_STATS ();				! Initialize the stats
    NUM_RETRIES = 0;				! Initialize the number of retries

    WHILE TRUE DO

	CASE .STATE FROM STATE_MIN TO STATE_MAX OF
	    SET
!
! Receiving of the data and the end of file message.
!

	    [STATE_RD] :
		STATE = REC_DATA ();
!
! Receiving the FILE information of the break to end the transfer of
! one or more files
!

	    [STATE_RF] :
		STATE = REC_FILE ();
!
! Initialization for the receiving of a file
!

	    [STATE_R] :
		STATE = REC_INIT ();
!
! Here if we have completed the receiving of the file
!

	    [STATE_C] :
		BEGIN
		RETURN_VALUE = TRUE;
		EXITLOOP;
		END;
!
! Here if we aborted the transfer or we have gotten into some random
! state (internal KERMSG problem).
!

	    [STATE_A, INRANGE, OUTRANGE] :
		BEGIN
		RETURN_VALUE = FALSE;
		!
		! Determine if the file is still open and if so close it
		!

		IF .FILE_OPEN_FLAG
		THEN
		    BEGIN
		    FILE_OPEN_FLAG = FALSE;
		    FILE_CLOSE ();
		    END;

		EXITLOOP;
		END;
	    TES;

    RETURN .RETURN_VALUE;
    END;					! End of REC_SWITCH_WORKER

%SBTTL 'REC_INIT'
ROUTINE REC_INIT =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will process an initialization message received from
!	the remote KERMIT.
!
! CALLING SEQUENCE:
!
!	STATE = REC_INIT();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	New machine state.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN
    ROUTINE CHECK_INIT =
	BEGIN

	IF .REC_TYPE EQL MSG_SND_INIT THEN RETURN TRUE ELSE RETURN FALSE;

	END;

    IF NOT REC_MESSAGE (CHECK_INIT) THEN RETURN STATE_A;

    MSG_NUMBER = .REC_SEQ;
    PRS_SEND_INIT ();
    SET_SEND_INIT ();
    SEND_PACKET (MSG_ACK, P_SI_LENGTH, .MSG_NUMBER);
    OLD_RETRIES = .NUM_RETRIES;
    NUM_RETRIES = 0;
    MSG_NUMBER = (.MSG_NUMBER + 1) AND %O'77';
    RETURN STATE_RF;
    END;					! End of REC_INIT
%SBTTL 'REC_FILE'
ROUTINE REC_FILE =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine expects to receive an MSG_FILE packet from the remote
!	KERMIT.  If the message is correct this routine will change the state
!	to STATE_RD.
!
!	This routine also expects MSG_SND_INIT, MSG_EOF, or MSG_BREAK.
!
! CALLING SEQUENCE:
!
!	STATE = REC_FILE();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	New state.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN
    ROUTINE CHECK_FILE =
	BEGIN

	IF (.REC_TYPE EQL MSG_SND_INIT) OR (.REC_TYPE EQL MSG_EOF) OR (.REC_TYPE EQL MSG_FILE) OR (.REC_TYPE
	    EQL MSG_BREAK)
	THEN
	    RETURN TRUE
	ELSE
	    RETURN FALSE;

	END;

    IF NOT REC_MESSAGE (CHECK_FILE) THEN RETURN STATE_A;

    SELECTONE .REC_TYPE OF
	SET

	[MSG_SND_INIT] :
	    BEGIN

	    IF .OLD_RETRIES GTR MAX_RETRIES THEN RETURN STATE_A;

	    OLD_RETRIES = .OLD_RETRIES + 1;

	    IF .MSG_NUMBER - 1 EQL .REC_SEQ
	    THEN
		BEGIN
		SET_SEND_INIT ();
		SEND_PACKET (MSG_ACK, P_SI_LENGTH, .MSG_NUMBER);
		NUM_RETRIES = 0;
		RETURN .STATE;
		END
	    ELSE
		RETURN STATE_A;

	    END;

	[MSG_EOF] :
	    BEGIN

	    IF .OLD_RETRIES GTR MAX_RETRIES THEN RETURN STATE_A;

	    OLD_RETRIES = .OLD_RETRIES + 1;

	    IF .MSG_NUMBER - 1 EQL .REC_SEQ
	    THEN
		BEGIN
		SEND_PACKET (MSG_ACK, 0, .REC_SEQ);
		NUM_RETRIES = 0;
		RETURN .STATE;
		END
	    ELSE
		RETURN STATE_A;

	    END;

	[MSG_FILE] :
	    BEGIN

	    IF .MSG_NUMBER NEQ .REC_SEQ THEN RETURN STATE_A;

	    FILE_SIZE = .REC_LENGTH;

	    IF .REC_LENGTH GTR 0
	    THEN
		CH$MOVE (.REC_LENGTH, CH$PTR (REC_MSG, PKT_MSG, CHR_SIZE),
		    CH$PTR (FILE_NAME, 0, CHR_SIZE));

	    IF NOT FILE_OPEN (FNC_WRITE) THEN RETURN STATE_A;

	    FILE_OPEN_FLAG = TRUE;
	    SEND_PACKET (MSG_ACK, 0, .MSG_NUMBER);
	    OLD_RETRIES = .NUM_RETRIES;
	    NUM_RETRIES = 0;
	    MSG_NUMBER = (.MSG_NUMBER + 1) AND %O'77';
	    RETURN STATE_RD;
	    END;

	[MSG_BREAK] :
	    BEGIN

	    IF .MSG_NUMBER NEQ .REC_SEQ THEN RETURN STATE_A;

	    SEND_PACKET (MSG_ACK, 0, .REC_SEQ);
	    RETURN STATE_C;
	    END;

	[OTHERWISE] :
	    RETURN STATE_A;
	TES;

    END;					! End of REC_FILE
%SBTTL 'REC_DATA'
ROUTINE REC_DATA =

!++
! FUNCTIONAL DESCRIPTION:
!
! This routine will accept data messages and write them to disk.
! It will also accept MSG_FILE and MSG_EOF messages.
!
! CALLING SEQUENCE:
!
!	STATE = REC_DATA();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	New state for the finite state machine.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN
    ROUTINE CHECK_DATA =
	BEGIN

	IF .REC_TYPE EQL MSG_DATA OR .REC_TYPE EQL MSG_FILE OR .REC_TYPE EQL MSG_EOF
	THEN
	    RETURN TRUE
	ELSE
	    RETURN FALSE;

	END;

    IF NOT REC_MESSAGE (CHECK_DATA) THEN RETURN STATE_A;

    SELECTONE .REC_TYPE OF
	SET

	[MSG_DATA] :
	    BEGIN

	    IF .MSG_NUMBER NEQ .REC_SEQ
	    THEN
		BEGIN

		IF .OLD_RETRIES GTR MAX_RETRIES THEN RETURN STATE_A;

		OLD_RETRIES = .OLD_RETRIES + 1;

		IF .MSG_NUMBER - 1 EQL .REC_SEQ
		THEN
		    BEGIN
		    SEND_PACKET (MSG_ACK, 0, .REC_SEQ);
		    NUM_RETRIES = 0;
		    RETURN .STATE;
		    END
		ELSE
		    RETURN STATE_A;

		END;

!
! Here if we have a message with a valid message number
!

	    IF NOT BFR_EMPTY () THEN RETURN STATE_A;

	    SEND_PACKET (MSG_ACK, 0, .REC_SEQ);
	    OLD_RETRIES = .NUM_RETRIES;
	    NUM_RETRIES = 0;
	    MSG_NUMBER = (.MSG_NUMBER + 1) AND %O'77';
	    RETURN STATE_RD;
	    END;

	[MSG_FILE] :
	    BEGIN

	    IF .OLD_RETRIES GTR MAX_RETRIES THEN RETURN STATE_A;

	    OLD_RETRIES = .OLD_RETRIES + 1;

	    IF .MSG_NUMBER - 1 EQL .REC_SEQ
	    THEN
		BEGIN
		SEND_PACKET (MSG_ACK, 0, .REC_SEQ);
		NUM_RETRIES = 0;
		RETURN .STATE;
		END
	    ELSE
		RETURN STATE_A;

	    END;

	[MSG_EOF] :
	    BEGIN

	    IF .MSG_NUMBER NEQ .REC_SEQ THEN RETURN STATE_A;

	    SEND_PACKET (MSG_ACK, 0, .REC_SEQ);
	    FILE_OPEN_FLAG = FALSE;

	    IF NOT FILE_CLOSE () THEN RETURN STATE_A;

	    MSG_NUMBER = (.MSG_NUMBER + 1) AND %O'77';
	    RETURN STATE_RF;
	    END;

	[OTHERWISE] :
	    RETURN STATE_A;
	TES;

    END;					! End of REC_DATA
%SBTTL 'CLEAR'
ROUTINE CLEAR : NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will clear the message buffer.
!
! CALLING SEQUENCE:
!
!	CLEAR();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN
    CH$FILL (0, MAX_MSG, CH$PTR (SND_MSG, 0, CHR_SIZE));
    END;
	%SBTTL	'Message processing -- PRS_SEND_INIT - Parse send init params'

ROUTINE PRS_SEND_INIT : NOVALUE =
!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will parse the SEND_INIT parameters that were sent by
!	the remote Kermit.  The items will be stored into the low segment.
!
! CALLING SEQUENCE:
!
!	PRS_SEND_INIT ();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	Message stored in REC_MSG.
!
! OUPTUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

BEGIN

LOCAL
    TEMP;

    IF .REC_LENGTH GEQ P_SI_BUFSIZ THEN
	SND_PKT_SIZE = UNCHAR (CH$RCHAR (CH$PTR (REC_MSG, PKT_MSG + P_SI_BUFSIZ, CHR_SIZE)));
    IF .REC_LENGTH GEQ P_SI_TIMOUT THEN
	SND_TIMEOUT = UNCHAR (CH$RCHAR (CH$PTR (REC_MSG, PKT_MSG + P_SI_TIMOUT, CHR_SIZE)));
    IF .REC_LENGTH GEQ P_SI_NPAD THEN
	SND_NPAD = UNCHAR (CH$RCHAR (CH$PTR (REC_MSG, PKT_MSG + P_SI_NPAD, CHR_SIZE)));
    IF .REC_LENGTH GEQ P_SI_PAD THEN
	SND_PADCHAR = CTL (CH$RCHAR (CH$PTR (REC_MSG, PKT_MSG + P_SI_PAD, CHR_SIZE)));
    IF .REC_LENGTH GEQ P_SI_EOL THEN
	SND_EOL = UNCHAR (CH$RCHAR (CH$PTR (REC_MSG, PKT_MSG + P_SI_EOL, CHR_SIZE)));
    IF .REC_LENGTH GEQ P_SI_QUOTE THEN
	SND_QUOTE_CHR = CH$RCHAR (CH$PTR (REC_MSG, PKT_MSG + P_SI_QUOTE, CHR_SIZE));

    IF .REC_LENGTH GEQ P_SI_8QUOTE
    THEN
	SND_8QUOTE_CHR = CH$RCHAR (CH$PTR (REC_MSG, PKT_MSG + P_SI_8QUOTE,
		CHR_SIZE));
END;				! End of PRS_SEND_INIT
%SBTTL 'SET_SEND_INIT'
ROUTINE SET_SEND_INIT : NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will initialize the various parameters for the
!	MSG_SND_INIT message.
!
! CALLING SEQUENCE:
!
!	SET_SEND_INIT();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	SND_MSG parameters set up.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN
    CH$WCHAR (CHAR (.RCV_PKT_SIZE), CH$PTR (SND_MSG, PKT_MSG + P_SI_BUFSIZ, CHR_SIZE));
    CH$WCHAR (CHAR (.RCV_TIMEOUT), CH$PTR (SND_MSG, PKT_MSG + P_SI_TIMOUT, CHR_SIZE));
    CH$WCHAR (CHAR (.RCV_NPAD), CH$PTR (SND_MSG, PKT_MSG + P_SI_NPAD, CHR_SIZE));
    CH$WCHAR (CTL (.RCV_PADCHAR), CH$PTR (SND_MSG, PKT_MSG + P_SI_PAD, CHR_SIZE));
    CH$WCHAR (CHAR (.RCV_EOL), CH$PTR (SND_MSG, PKT_MSG + P_SI_EOL, CHR_SIZE));
    CH$WCHAR (.RCV_QUOTE_CHR, CH$PTR (SND_MSG, PKT_MSG + P_SI_QUOTE, CHR_SIZE));
    CH$WCHAR (.RCV_8QUOTE_CHR, CH$PTR (SND_MSG, PKT_MSG + P_SI_8QUOTE, CHR_SIZE));
    END;					! End of SET_SEND_INIT
%SBTTL 'SEND_PACKET'
ROUTINE SEND_PACKET (TYPE, LENGTH, MN) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will cause a packet to be sent over the line
!	that has been opened by OPEN_TERMINAL.
!
! CALLING SEQUENCE:
!
!	SEND_PACKET(Type, Length);
!
! INPUT PARAMETERS:
!
!	TYPE - Type of packet to send.
!
!	LENGTH - Length of the packet being sent.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN

    LOCAL
	FILLER : VECTOR [CH$ALLOCATION (MAX_MSG, CHR_SIZE)],
	CHKSUM,					! Checksum for the message we calculate
	POINTER;				! Pointer to the information in the message

!
! Do any filler processing that the remote KERMIT requires.
!

    IF .SND_NPAD NEQ 0
    THEN
	BEGIN
	CH$FILL (.SND_PADCHAR, MAX_MSG, CH$PTR (FILLER, 0, CHR_SIZE));
	SEND (FILLER, .SND_NPAD);
	END;

!
! Store the header information into the message.
!
    CH$WCHAR (.TYPE, CH$PTR (SND_MSG, PKT_TYPE, CHR_SIZE));
    CH$WCHAR (CHR_SOH, CH$PTR (SND_MSG, PKT_MARK, CHR_SIZE));
    CH$WCHAR (CHAR (.LENGTH + PKT_OVR_HEAD), CH$PTR (SND_MSG, PKT_COUNT, CHR_SIZE));
    CH$WCHAR (CHAR (.MN), CH$PTR (SND_MSG, PKT_SEQ, CHR_SIZE));
!
! Do the initial checksum calculation and set up the pointer to read cahracters
! from the message dependent part of the message
!
    CHKSUM = CHAR (.LENGTH + PKT_OVR_HEAD) + CHAR (.MN) + .TYPE;
    POINTER = CH$PTR (SND_MSG, PKT_MSG, CHR_SIZE);
!
! Loop through the message dependent information adding the characters to
! the total checksum
!

    IF .LENGTH NEQ 0
    THEN

	INCR I FROM 1 TO .LENGTH DO
	    CHKSUM = .CHKSUM + CH$RCHAR_A (POINTER);

!
! Do the final checksum calculation
!
    CHKSUM = (.CHKSUM + (.CHKSUM AND %O'300')/%O'100') AND %O'77';
!
! Store the checksum into the message
!
    CH$WCHAR_A (CHAR (.CHKSUM), POINTER);
!
! Store in the end of line character
!
    CH$WCHAR_A (.SND_EOL, POINTER);
!
! If we are debugging then type out the message we are sending.
!
    DBG_SEND (SND_MSG, (.LENGTH + PKT_TOT_OVR_HEAD));
!
! Now call the O/S routine to send the message out to the remote KERMIT
!
    RETURN SEND (SND_MSG, .LENGTH + PKT_TOT_OVR_HEAD);
    END;					! End of SEND_PACKET
%SBTTL 'REC_MESSAGE - Receive a message'
ROUTINE REC_MESSAGE (CHK_ROUTINE) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will handle the retry processing for the various
!	messages that can be received.
!
! CALLING SEQUENCE:
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUPTUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	KER_NORMAL - Normal return
!	KER_RETRIES - Too many retries
!	(What ever REC_PACKET returns).
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN

    LOCAL
	STATUS;					! Status returned by lower level routines

    RETURN

	WHILE TRUE DO
	    BEGIN

	    IF .NUM_RETRIES GTR MAX_RETRIES THEN RETURN KER_RETRIES;

	    NUM_RETRIES = .NUM_RETRIES + 1;
	    STATUS = REC_PACKET ();

	    IF NOT .STATUS AND .STATUS NEQ KER_CHKSUMERR THEN EXITLOOP .STATUS;

	    IF NOT .STATUS
	    THEN
		SEND_PACKET (MSG_NAK, 0, .MSG_NUMBER - 1)
	    ELSE
		BEGIN

		IF NOT (.CHK_ROUTINE) () THEN SEND_PACKET (MSG_NAK, 0, .REC_SEQ) ELSE EXITLOOP KER_NORMAL;

		END;

	    END;

    END;					! End of REC_PARSE
%SBTTL 'REC_PACKET'
ROUTINE REC_PACKET =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will do the oppoiste of SEND_PACKET.  It will wait
!	for the message to be read from the remote and then it will
!	check the message for validity.
!
! CALLING SEQUENCE:
!
!	Flag = REC_PACKET();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	REC_MSG - Contains the message received.
!
! COMPLETION CODES:
!
!	True - Packet receive ok.
!	False - Problem occured during the receiving of the packet.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN

    BIND
	ATTEMPT_TEXT = UPLIT (%ASCIZ'Attempting to receive');

    LOCAL
	BUFFER : VECTOR [CH$ALLOCATION (MAX_MSG, CHR_SIZE)],
	ERR_BUFFER : VECTOR [CH$ALLOCATION (MAX_MSG)],
	STATUS,					! Status returned by RECEIVE
	MSG_LENGTH,
	ERR_POINTER,				! Pointer to the error buffer
	POINTER,
	OLD_POINTER,				! Character pointer to the data section
	NEW_POINTER,
	CHKSUM;					! Checksum of the message

!
! Attempt to read the message from the remote.
!

    DO
	BEGIN

	IF .DEBUG_FLAG AND NOT .CONNECT_FLAG
	THEN
	    BEGIN
	    TT_TEXT (ATTEMPT_TEXT);
	    TT_CRLF ();
	    END;

	STATUS = RECEIVE (BUFFER, MSG_LENGTH);

	IF NOT .STATUS THEN RETURN .STATUS;

	IF .MSG_LENGTH EQL 0 THEN RETURN KER_PROTOERR;

	OLD_POINTER = CH$PTR (BUFFER, 0, CHR_SIZE);
	NEW_POINTER = CH$FIND_CH (.MSG_LENGTH, .OLD_POINTER, CHR_SOH);

	WHILE TRUE DO

	    IF CH$FAIL (.NEW_POINTER) EQL 0
	    THEN
		BEGIN
		MSG_LENGTH = .MSG_LENGTH - CH$DIFF (.NEW_POINTER, .OLD_POINTER);

		IF .MSG_LENGTH EQL 0 THEN EXITLOOP;

		OLD_POINTER = .NEW_POINTER;
		NEW_POINTER = CH$FIND_CH (.MSG_LENGTH, CH$PLUS (.OLD_POINTER, 1), CHR_SOH);
		END
	    ELSE
		EXITLOOP;

	END
    UNTIL .MSG_LENGTH NEQ 0;

    CH$MOVE (.MSG_LENGTH, .OLD_POINTER, CH$PTR (REC_MSG, 0, CHR_SIZE));
!
! Initialize the checksum and others
!
    REC_TYPE = CH$RCHAR (CH$PTR (REC_MSG, PKT_TYPE, CHR_SIZE));
    CHKSUM = CH$RCHAR (CH$PTR (REC_MSG, PKT_COUNT, CHR_SIZE)) + CH$RCHAR (CH$PTR (REC_MSG, PKT_SEQ, CHR_SIZE))
    + .REC_TYPE;
!
! Now break the message apart byte by byte.
!
    REC_LENGTH = UNCHAR (CH$RCHAR (CH$PTR (REC_MSG, PKT_COUNT, CHR_SIZE))) - PKT_OVR_HEAD;
    REC_SEQ = UNCHAR (CH$RCHAR (CH$PTR (REC_MSG, PKT_SEQ, CHR_SIZE)));
!
! Now compute the final checksum and make sure that it is identical
! to what we received from the remote KERMIT
!
    POINTER = CH$PTR (REC_MSG, PKT_MSG, CHR_SIZE);

    INCR I FROM 1 TO .REC_LENGTH DO
	CHKSUM = .CHKSUM + CH$RCHAR_A (POINTER);

    CHKSUM = (.CHKSUM + (.CHKSUM AND %O'300')/%O'100') AND %O'77';

    IF .CHKSUM NEQ UNCHAR (CH$RCHAR_A (POINTER)) THEN RETURN KER_CHKSUMERR;

!
! Typed the packet if we are debugging
!
    DBG_RECEIVE (REC_MSG);
!
! Now check to see if we have an E type (Error) packet.
!

    IF .REC_TYPE NEQ MSG_ERROR THEN RETURN KER_NORMAL;

!
! Here to process an error packet.  Call the user routine to output the
! error message to the terminal.
!
    ERR_POINTER = CH$PTR (ERR_BUFFER);
    POINTER = CH$PTR (REC_MSG, PKT_MSG, CHR_SIZE);

    INCR I FROM 1 TO .REC_LENGTH DO
	CH$WCHAR_A (CH$RCHAR_A (POINTER), ERR_POINTER);

    CH$WCHAR (CHR_NUL, ERR_POINTER);
    TT_TEXT (ERR_BUFFER);
    RETURN KER_ERRMSG;
    END;					! End of REC_PACKET
%SBTTL 'Buffer filling -- Main routine'
ROUTINE BFR_FILL =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will fill the buffer with data from the file.  It
!	will do all the quoting that is required.
!
! CALLING SEQUENCE:
!
!	EOF_FLAG = BFR_FILL();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	True - Buffer filled may be at end of file.
!	False - At end of file.
!
! IMPLICIT OUTPUTS:
!
!	Number of characters stored in the buffer.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN

    LOCAL
	STATUS,					! Status returned by GET_FILE
	CHARACTER,				! Character read from the file
	POINTER;				! Pointer into the message buffer

!
! Initialize the variables
!
    SIZE = 0;
    POINTER = CH$PTR (SND_MSG, PKT_MSG, CHR_SIZE);
!
! Loop getting characters from the file
!

    WHILE (STATUS = GET_FILE (CHARACTER)) EQL KER_NORMAL DO
	BEGIN

!
! Determine if this is a character that must be quoted.
!

	IF ((.CHARACTER AND %O'177') LSS %C' ')
	    OR ((.CHARACTER AND %O'177') EQL CHR_DEL)
	    OR ((.CHARACTER AND %O'177') EQL .SND_QUOTE_CHR)
	THEN
	    BEGIN
	    SIZE = .SIZE + 1;
	    CH$WCHAR_A (.SND_QUOTE_CHR, POINTER);

	    IF ((.CHARACTER AND %O'177') NEQ .SND_QUOTE_CHR) THEN CHARACTER = CTL (.CHARACTER);

	    END;

!
! Now write the character into the buffer.
!
	SIZE = .SIZE + 1;
	CH$WCHAR_A (.CHARACTER, POINTER);

	IF (.SIZE GEQ .SND_PKT_SIZE - PKT_TOT_OVR_HEAD - 2) THEN EXITLOOP;

	END;

!
! Determine if we really stored anything into the buffer.
!

    IF .SIZE NEQ 0 THEN RETURN KER_NORMAL ELSE RETURN .STATUS;

    END;					! End of BFR_FILL
	%SBTTL	'Buffer filling -- Parity routine'

ROUTINE PARITY (CHARACTER) = 

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will add parity to the character that is supplied.
!
! CALLING SEQUENCE:
!
!	CHARACTER = PARITY(CHARACTER)
!
! INPUT PARAMETERS:
!
!	CHARACTER - Produce the parity for this character depending on the
!		setting of the SET PARITY switch.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUPTUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

BEGIN

LOCAL
    TEMP_CHAR;

    CASE .PARITY_TYPE FROM PAR_MIN TO PAR_MAX OF
    SET
	[PAR_NONE] :
		RETURN .CHARACTER AND %O'177';

	[PAR_MARK] :
		RETURN (.CHARACTER AND %O'177') OR %O'200';

	[PAR_ODD] :
	    BEGIN
	    TEMP_CHAR = .CHARACTER AND %O'177' OR %O'200';
	    TEMP_CHAR = .TEMP_CHAR<0, 4> XOR .TEMP_CHAR <4, 4>;
	    TEMP_CHAR = .TEMP_CHAR<0, 2> XOR .TEMP_CHAR <2, 2>;
	    IF .TEMP_CHAR EQL %B'01' OR .TEMP_CHAR EQL %B'10' THEN
		RETURN .CHARACTER AND %O'177' OR %O'200'
	    ELSE
		RETURN .CHARACTER AND %O'177';
	    END;

	[PAR_EVEN] :
	    BEGIN
	    TEMP_CHAR = .CHARACTER AND %O'177';
	    TEMP_CHAR = .TEMP_CHAR<0, 4> XOR .TEMP_CHAR <4, 4>;
	    TEMP_CHAR = .TEMP_CHAR<0, 2> XOR .TEMP_CHAR <2, 2>;
	    IF .TEMP_CHAR EQL %B'01' OR .TEMP_CHAR EQL %B'10' THEN
		RETURN .CHARACTER AND %O'177' OR %O'200'
	    ELSE
		RETURN .CHARACTER AND %O'177';
	    END;
    TES;
END;				! End of PARITY
%SBTTL 'BFR_EMPTY'
ROUTINE BFR_EMPTY =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will empty the data from the REC_MSG message buffer
!	to the file.  It will process quoting characters.
!
! CALLING SEQUENCE:
!
!	Flag = BFR_EMPTY();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUTPUT PARAMETERS:
!
!	True - No problems writing the file.
!	False - I/O error writing the file.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN

    LOCAL
	STATUS,					! Status returned by PUT_FILE
	COUNTER,				! Count of the characters left
	CHARACTER,				! Character we are processing
	POINTER;				! Pointer to the data

    POINTER = CH$PTR (REC_MSG, PKT_MSG, CHR_SIZE);
    COUNTER = 0;

    WHILE (.COUNTER LSS .REC_LENGTH) DO
	BEGIN
	CHARACTER = CH$RCHAR_A (POINTER);
	COUNTER = .COUNTER + 1;

	IF .CHARACTER EQL .RCV_QUOTE_CHR
	THEN
	    BEGIN
	    CHARACTER = CH$RCHAR_A (POINTER);
	    COUNTER = .COUNTER + 1;

	    IF (.CHARACTER AND %O'177') NEQ .RCV_QUOTE_CHR THEN CHARACTER = CTL (.CHARACTER);

	    END;

	STATUS = PUT_FILE (.CHARACTER);

	IF NOT .STATUS THEN RETURN .STATUS;

	END;

    STATUS = FILE_DUMP ();

    IF NOT .STATUS THEN RETURN .STATUS;

    RETURN KER_NORMAL;
    END;					! End of BFR_EMPTY
%SBTTL 'Statistics -- Initialization'
ROUTINE INIT_STATS : NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will initialize the various locations that the
!	send and receive statistics are kept.
!
! CALLING SEQUENCE:
!
!	INIT_STATS();
!
! INPUT PARAMETERS:
!
!	None.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUPTUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN
    SND_TOTAL_CHARS = 0;
    RCV_TOTAL_CHARS = 0;
    SND_DATA_CHARS = 0;
    RCV_DATA_CHARS = 0;
    END;					! End of INIT_STATS
%SBTTL 'Debugging -- DBG_SEND'
ROUTINE DBG_SEND (ADDRESS, LENGTH) : NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will output the message that is going to be sent
!	as part of the debugging information that is turned on in the
!	SET DEBUG command.
!
! CALLING SEQUENCE:
!
!	DBG_SEND(MSG_ADDRESS, MSG_LENGTH);
!
! INPUT PARAMETERS:
!
!	MSG_ADDRESS - Address of the message that is going to be sent
!		to the remote KERMIT.  The bytes are CHR_SIZE.
!	MSG_LENGTH - Length of the message.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUPTUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN

    BIND
	SEND_TEXT = UPLIT (%ASCIZ'Sending...');

    IF .DEBUG_FLAG AND NOT .CONNECT_FLAG
    THEN
	BEGIN
	TT_TEXT (SEND_TEXT);
	DBG_MESSAGE (.ADDRESS, .LENGTH);
	END;

    END;					! End of DBG_SEND
%SBTTL 'Debugging -- DBG_RECEIVE'
ROUTINE DBG_RECEIVE (ADDRESS) : NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will output the message that was received from
!	the remote KERMIT.  This routine is called only if the DEBUG_FLAG
!	is true.
!
! CALLING SEQUENCE:
!
!	DBG_RECEIVE(MSG_ADDRESS);
!
! INPUT PARAMETERS:
!
!	MSG_ADDRESS - Address of the message received by the remote KERMIT.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUPTUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN

    BIND
	RECEIVE_TEXT = UPLIT (%ASCIZ'Received...');

    IF .DEBUG_FLAG AND NOT .CONNECT_FLAG
    THEN
	BEGIN
	TT_TEXT (RECEIVE_TEXT);
	DBG_MESSAGE (.ADDRESS, .REC_LENGTH);
	END;

    END;					! End of DBG_RECEIVE
%SBTTL 'Debugging -- DBG_MESSAGE'

GLOBAL ROUTINE DBG_MESSAGE (MSG_ADDRESS, MSG_LENGTH) : NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine will display a message that is either being sent
!	or received on the user's terminal.
!
! CALLING SEQUENCE:
!
!	DBG_MESSAGE(MSG_ADDRESS, MSG_LENGTH);
!
! INPUT PARAMETERS:
!
!	MSG_ADDRESS - Address of the message to be output
!	MSG_LENGTH - Length of the message to be output.
!
! IMPLICIT INPUTS:
!
!	None.
!
! OUPTUT PARAMETERS:
!
!	None.
!
! IMPLICIT OUTPUTS:
!
!	None.
!
! COMPLETION CODES:
!
!	None.
!
! SIDE EFFECTS:
!
!	None.
!
!--

    BEGIN

    MAP
	MSG_ADDRESS : REF VECTOR [CH$ALLOCATION (MAX_MSG)];	! Point to the vector

    LOCAL
	TEMP_POINTER,				! Temporary character pointer
	MSG_LEN;

!
! Message type text
!

    BIND
	DATA_TEXT = UPLIT (%ASCIZ' (Data)'),
	ACK_TEXT = UPLIT (%ASCIZ' (ACK)'),
	NAK_TEXT = UPLIT (%ASCIZ' (NAK)'),
	SND_INIT_TEXT = UPLIT (%ASCIZ' (Send init)'),
	BREAK_TEXT = UPLIT (%ASCIZ' (Break)'),
	FILE_TEXT = UPLIT (%ASCIZ' (File)'),
	EOF_TEXT = UPLIT (%ASCIZ' (EOF)'),
	ERROR_TEXT = UPLIT (%ASCIZ' (Error)'),
	RCV_INIT_TEXT = UPLIT (%ASCIZ' (Receive initiate)'),
	COMMAND_TEXT = UPLIT (%ASCIZ' (Command)'),
	KERMIT_TEXT = UPLIT (%ASCIZ' (Generic KERMIT command)');

!
! Header information
!

    BIND
	MN_TEXT = UPLIT (%ASCIZ'Message number: '),
	LENGTH_TEXT = UPLIT (%ASCIZ'	Length: '),
	DEC_TEXT = UPLIT (%ASCIZ' (dec)'),
	MSG_TYP_TEXT = UPLIT (%ASCIZ'Message type: '),
	CHKSUM_TEXT = UPLIT (%ASCIZ'Checksum: '),
	OPT_DATA_TEXT = UPLIT (%ASCIZ'Optional data: '),
	PRE_CHAR_TEXT = UPLIT (%ASCIZ' "');

!
! Preliminary calculations
!
    MSG_LEN = UNCHAR (CH$RCHAR (CH$PTR (.MSG_ADDRESS, PKT_COUNT, CHR_SIZE)));
!
! First output some header information for the packet.
!
    TT_CRLF ();
    TT_TEXT (MN_TEXT);
    TT_NUMBER (UNCHAR (CH$RCHAR (CH$PTR (.MSG_ADDRESS, PKT_SEQ, CHR_SIZE))));
    TT_TEXT (DEC_TEXT);
    TT_TEXT (LENGTH_TEXT);
    TT_NUMBER (.MSG_LEN);
    TT_TEXT (DEC_TEXT);
    TT_CRLF ();
!
! Now output the message type and dependent information
!
    TT_TEXT (MSG_TYP_TEXT);
    TT_CHAR (CH$RCHAR (CH$PTR (.MSG_ADDRESS, PKT_TYPE, CHR_SIZE)));

    SELECTONE CH$RCHAR (CH$PTR (.MSG_ADDRESS, PKT_TYPE, CHR_SIZE)) OF
	SET

	[MSG_DATA] :
	    TT_TEXT (DATA_TEXT);

	[MSG_ACK] :
	    TT_TEXT (ACK_TEXT);

	[MSG_NAK] :
	    TT_TEXT (NAK_TEXT);

	[MSG_SND_INIT] :
	    TT_TEXT (SND_INIT_TEXT);

	[MSG_BREAK] :
	    TT_TEXT (BREAK_TEXT);

	[MSG_FILE] :
	    TT_TEXT (FILE_TEXT);

	[MSG_EOF] :
	    TT_TEXT (EOF_TEXT);

	[MSG_ERROR] :
	    TT_TEXT (ERROR_TEXT);
	TES;

    TT_CRLF ();
!
! Now output any of the optional data.
!

    IF .MSG_LEN - PKT_OVR_HEAD NEQ 0
    THEN
	BEGIN
	TT_TEXT (OPT_DATA_TEXT);
	TT_CRLF ();
	TEMP_POINTER = CH$PTR (.MSG_ADDRESS, PKT_MSG, CHR_SIZE);

	INCR I FROM 1 TO .MSG_LEN - PKT_OVR_HEAD DO
	    BEGIN

	    IF (.I MOD 10) EQL 1
	    THEN
		BEGIN
		TT_CRLF ();
		TT_CHAR (CHR_TAB);
		END;

	    TT_TEXT (PRE_CHAR_TEXT);
	    TT_QCHAR (CH$RCHAR_A (TEMP_POINTER));
	    TT_CHAR (%C'"');
	    END;

	IF ((.MSG_LEN - PKT_OVR_HEAD) MOD 10) EQL 1 THEN TT_CRLF ();

	TT_CRLF ();
	END;

!
! Now output the checksum for the message that we received
!
    TT_TEXT (CHKSUM_TEXT);
    TT_NUMBER (UNCHAR (CH$RCHAR (CH$PTR (.MSG_ADDRESS, PKT_MSG + .MSG_LEN + PKT_CHKSUM - PKT_OVR_HEAD,
		    CHR_SIZE))));
    TT_TEXT (DEC_TEXT);
    TT_CRLF ();
    END;					! End of DBG_MESSAGE

%SBTTL 'End of KERMSG'
END

ELUDOM
