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' ! ! ! 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' ! ! ! ! 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: ! ! ! ! ! Predefined as SOH (Control-A, octal 001). ! ! ! Count of the number of characters following this position. ! Character counts of ONLY 0 to 94 are valid. ! ! ! Packet sequence number, modulo 100 (octal). ! ! ! 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. ! ! ! Checksum of the packet. ! ! ! 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, ! PKT_COUNT = 1, ! PKT_SEQ = 2, ! PKT_TYPE = 3, ! PKT_MSG = 4, ! PKT_MAX_MSG = 94 - 5, ! Maximum size of the message dependent ! information PKT_CHKSUM = 0, ! offset from end of ! Message dependent information PKT_EOL = 1, ! 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. ! ! ! ! ! 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"> ! <8-bit-quote> ! ! 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