Google
 

Trailing-Edge - PDP-10 Archives - BB-PENEA-BM_1990 - t20src/lisvax.mac
There are 26 other files named lisvax.mac in the archive. Click here to see a list.
;	COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1985, 1989.
;	ALL RIGHTS RESERVED.
;
;	THIS SOFTWARE IS FURNISHED UNDER A  LICENSE AND MAY BE USED AND  COPIED
;	ONLY IN  ACCORDANCE  WITH  THE  TERMS OF  SUCH  LICENSE  AND  WITH  THE
;	INCLUSION OF THE ABOVE  COPYRIGHT NOTICE.  THIS  SOFTWARE OR ANY  OTHER
;	COPIES THEREOF MAY NOT BE PROVIDED  OR OTHERWISE MADE AVAILABLE TO  ANY
;	OTHER PERSON.  NO  TITLE TO  AND OWNERSHIP  OF THE  SOFTWARE IS  HEREBY
;	TRANSFERRED.
;
;	THE INFORMATION IN THIS  SOFTWARE IS SUBJECT  TO CHANGE WITHOUT  NOTICE
;	AND SHOULD  NOT  BE CONSTRUED  AS  A COMMITMENT  BY  DIGITAL  EQUIPMENT
;	CORPORATION.
;
;	DIGITAL ASSUMES NO  RESPONSIBILITY FOR  THE USE OR  RELIABILITY OF  ITS
;	SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY DIGITAL.
	TITLE LISVAX
	TWOSEG
	RELOC	400000

	SEARCH	MACSYM,MSUNV

	EXTERN	DB%VD8,MX%UNQ,MX$VAL,MXERRS,SCAN%P,UF%CLO,UF%OPE,UF%WRI,UM%GET
	EXTERN	UM%REL,UMP%GE,UMP%RE,UN%ABO,UN%ACC,UN%CLO,UN%OPE,UN%REA,UN%STA
	EXTERN	UN%WRI,US%SLE,RELASC,LOG,NODNAM,NMLDIE,ELOG

	EXTERN	NW$RCF

	IFNDEF T20SYS,<T20SYS==0>	;***NOTE*** TOPS-10 by default!

;To build the TOPS-20 version, create T20.MAC which contains 1 line:
;T20SYS==-1.  Then COMPILE T20+LISVAX.

	DEFINE TOPS10 <IFE T20SYS,>
	DEFINE TOPS20 <IFN T20SYS,>

    DEFINE $TRACE(S) <
	REPEAT 0,<
		PUSH P,T1
		PUSH P,T2
		PUSH P,T3
		PUSH P,T4
		PUSH P,T5
		MOVEI T1,[ASCIZ /S/]
		PUSH P,T1
		PUSH P,[0]          ;[318]
		CALL LOG
		ADJSP P,-2
		POP P,T5
		POP P,T4
		POP P,T3
		POP P,T2
		POP P,T1
		>>

;AC Definitions

	TF==0			;SCRATCH REGISTERS
	T1==1
	T2==2
	T3==3
	T4==4
	T5==5
	T6==6
	T7==7
	T10==10
	BUFPTR==11		;Pointer to mail file header
	WRDCNT==12		;Number of words in the message
	MSGPTR==13		;Pointer into message being built
	VARPTR==14		;Points to variables and pointers buffer
;Ac 15 reserved for bliss
	P==17			;Stack

	FLGWRD==0		;Flag word for protocol options
	   USRNOT==1B0		   ;Notify receiving user
	   USRBLK==1B1		   ;Send the message in blocks
	   USRPRE==1B2		   ;Master prefixes sender's node
	   USRCC==1B3		   ;There WILL BE A CC LIST
	   SNQTS==1B4		   ;Sender Needs QuoTeS
	UTLPTR==1		;Reusable pointer
	LSTYP==2		;To:/cc: list being processed
	MSGNUM==3		;Number of pages in the message
	NOMORE==4		;Any more destination strings or not
	MSGRCT==5		;Number of records in the message
	IDNUM==6		;Packet id
	LSTADR==7 		;Last entry in the current address list
	NXLST==10		;Last address reached in previous page
	NXUSR==11		;If more than one page, points to the
				;First destination string
	MSGSTR==12		;Address of beginning of the message
	SCRAT1==13		;Scratch space
	SCRAT2==14
	SCRAT3==15
	SCRAT4==16
	MAIPTR==17		;Pointer to mail file spec
	FAINDE==20		;Pointer to unknown node error message
	VALUSR==21		;Pointer for use with mx$val routine
	SUCMSGF==22		;Success message for user validation
	FAIMSG==23		;Failure message for user validation
	NODENM==24		;Points to local node name
	BYTNUM==25		;Number of bytes in failure message
	LOCAL==26		;Local node found flag
	NODE7==27		;Points to local node name
	NAMLIN==30		;Number of names/line in the to: list
	STABUF==31		;Points to start of buffer
	BUFBYT==32		;Number of bytes in the buffer
	EOFMES==33		;End of the vax message has been found
	NUMUSR==34		;Number of recipients
	CNNBLK==35		;Connect block address
	SNDSTR==36		;From string pointer
	NODSTR==37		;Node name pointer (7 bit bytes)
	USRSTR==40		;User name pointer
	ADRSPA==41		;Pointer to recipient list
	WRKBUF==42		;Working buffer address
	WRKBU2==43		;Second working buffer address
	FILNB==44		;File number of mail file
	DATLNK==45		;Lisvax's data link
	NODCNT==46		;Number of node names in recipient string
	LNKCLO==47		;Link closed due to invalid user
	FAIMG2==50		;Second part of user error text
	NODMSG==51		;Pointer to node name error text
	STABLK==52		;Address of first recipient status block
	STAPTR==53		;Address of current recipient status entry
	FILCLO==54		;Mail file closed
	INVPTR==55		;Address of current recipient status block
	NUMVAR==56		;Size of variable address list

	VERSNM==3		;Version number of protocol
	VAXTYP==7
	VAXOBJ==^D27		;Vax object type
	MINSIZ==^D12		;Minimum size for version 3 protocol
	TOPS20==		;Operating system type
	OPRESW==4		;Number of words for slave optional data
	OPRESB==12		;Number of bytes for slave optional data
	LSTREC==^D897		;Used to determine if room for another record
	D2==2			;Block sizes
	BY2WRD==5		;Number of 7 bit bytes in a word
	NUMTRY==6		;Times to check for closed link
	SLPTIM==20		;Time to sleep between link status check
	D68==^D68
	D70==^D70
	CBKSIZ==^D64		;Connection block size
	PROVER==377B7		;Protocol version field
	OSTYPE==377B31		;Operating system type
	MSTSND==1B0		;Master wishes to send message in blocks
	SLVSND==1B1		;Slave willing to receive message in blocks
	MSTPRE==1B2		;Master wishes to prefix node name to send name
	SLVPRE==1B3		;Slave willing to accept node name prefixed
	MSTCC==1B4		;Master wishes to send a cc: list
	SLVCC==1B5		;Slave willing to accept a cc: list
	SUCBIT==1B25		;Success bit
	FAIBIT==1B24		;Failure bit
	ST%CLO==1B33		;Link was closed
	ST%ABT==1B32		;Link was aborted
	INVLND==1B35		;Invalid node in recipient string
	INVLUS==1B34		;Invalid recipient name
	QUOTE==42		;Ascii value of quote character
	MAXSND==^D100		;Maximum number of recipients
	MAXNUM==774		;Maximum size of error block
	OPDEF	PJRST	[JUMPA 17,]
	.DECNT==1		;Decnet listener's id
TOPS10<	.DCX11=13>
TOPS10<	IP.JAC==1B0>
TOPS10< EXTERN	GETDAT>
TOPS20< SEARCH	MONSYM>
	SALL

;All routines use ACs 11-14 as Global registers.  These have the symbols
;BUFPTR, WRDCNT, MSGPTR, and VARPTR.

;Allocate buffer spaces

LISVAX::$TRACE <At LISVAX...>
	CALL GETBUF			;(/)

;Set up the error messages

	CALL SETMSG			;(/)

;Determine the protocol version being used and make the connection

LISNXT: $TRACE <At LISNXT...>
	CALL CONECT			;(/)


;Create and open the unique name mail file.

	CALL MAIOPN		;(/)Open the mail file
	IFNSK.
	  $TRACE <Error opening the mail file>
	  CALL FORDIS		;(/)An error occurred, abort the connection
	  CALL CLSFIL		;(/)Release the message file
	  CALL RELMAI	  	;(/)Release space for the mail file spec
	  JRST LISNXT	  	;Try again
	ENDIF.

;Build the sender record

	CALL SNDREC		;(/)Build the sender record
	IFNSK.
	  $TRACE <Error building the sender record>
	  CALL FORDIS		;(/)An error occurred, abort the connection
	  CALL CLSFIL		;(/)Release the message file
	  CALL RELMAI		;(/)Release space for the mail file spec
	  JRST LISNXT		;Try again
	ENDIF.

;Build the date and from fields for the mail file

	CALL MFHEAD		;(/)Build first part of the mail file header

;Complete and send the mail message

	CALL DSTREC		;(/)Finish the message and send to mx
	IFNSK.
	  $TRACE <Error return from DSTREC>
	  CALL FORDIS	  	;(/)No, force a disconnect
	  SKIPN FILCLO(VARPTR) 	;Skip if message file is already closed
	  CALL CLSFIL	  	;(/)Release the message file
	  CALL RELMAI	  	;(/)Release space for the mail file spec
	  CALL RLSEPG	  	;(/)Release any error block pages
	  JRST LISNXT		;Wait for the next vax call
	ENDIF.

;Disconnect

	CALL DISCON		;(/)Wait for the disconnect
	CALL RELMAI		;(/)Release space for the mail file spec
	JRST LISNXT		;Go for the next connection
SUBTTL GETBUF	GET BUFFERS

;Get space for list of addresses and pointers

GETBUF: $TRACE <At GETBUF...>
	PUSH P,[NUMVAR]		;Get space for the address list
	CALL UM%GET		;(1S/T1)Do it
	ADJSP P,-1		;Reset the stack
	JUMPLE T1,BUFERR	;An error occurred
	MOVE VARPTR,T1		;Save the address of the list

;Get space for the connect block

	PUSH P,[CBKSIZ]		;Size of the connection block
	CALL UM%GET		;(1S/T1)Get the space
	ADJSP P,-1		;Adjust the pointer
	JUMPLE T1,BUFERR	;Error, halt
	MOVEM T1,CNNBLK(VARPTR) ;Save the address for later

;Allocate space for use with mx$val

	PUSH P,[^D60]		;The amount of words
	CALL UM%GET		;(1S/T1)Get them
	ADJSP P,-1		;Reset the stack
	JUMPLE T1,BUFERR	;An error occurred
	HRLI T1,[POINT 7]	;Make into a pointer
	MOVEM T1,VALUSR(VARPTR) ;Save for later

;Allocate a page for message to mx

	CALL UMP%GET		;(/T1)Get a page
	JUMPL T1,BUFERR		;An error occurred, halt
	LSH T1,^D9		;Change page number to an address
	MOVEM T1,MSGSTR(VARPTR) ;Address of start of the message

;Allocate a page for invalid recipients

	CALL UMP%GET		;(/T1)Get a page
	JUMPL T1,BUFERR		;An error occurred, halt
	LSH T1,^D9		;Change page number to an address
	MOVEM T1,STABLK(VARPTR) ;Address of start of invalid recipient buffer

;Get space for building the mail file header (date, from, to, mailed to fields)

	CALL UMP%GET		;(/T1)Get a page
	JUMPL T1,BUFERR		;An error occurred, halt
	LSH T1,^D9		;Change page number to an address
	MOVE BUFPTR,T1		;Make into
	HRLI BUFPTR,(POINT 7)	;A pointer
	MOVEM BUFPTR,STABUF(VARPTR) ;Save start for write to mail file

;Allocate space for the from string

	PUSH P,[D70]			;Amount of space needed
	CALL UM%GET			;(1S/T1)Get the space
	ADJSP P,-1			;Reset the stack
	JUMPLE T1,BUFERR		;An error occurred
	HRLI T1,(POINT 8)		;Make a pointer
	MOVEM T1,SNDSTR(VARPTR)		;Save for later

;Allocate space for the node name and user name

	PUSH P,[D2]			;Two words for the node name
	CALL UM%GET			;(1S/T1)Get them
	ADJSP P,-1			;Reset the stack
	JUMPLE T1,BUFERR		;An error occurred
	HRLI T1,(POINT 7)		;Make it into a pointer
	MOVEM T1,NODSTR(VARPTR)		;Save for later

	PUSH P,[D68]			;Space for the user name
	CALL UM%GET			;(1S/T1)Get them
	ADJSP P,-1			;Reset the stack
	JUMPLE T1,BUFERR		;An error occurred, halt
	HRLI T1,(POINT 7)		;Make it into a pointer
	MOVEM T1,USRSTR(VARPTR)		;Save for later

;Allocate space for the recipient buffer

	PUSH P,[^D100]			;Get room for the recipient
	CALL UM%GET			;(1S/T1)Do it
	ADJSP P,-1			;Reset the stack pointer
	JUMPLE	T1,BUFERR		;Error, halt
	HRLI T1,(POINT 8)		;Make it into a pointer
	MOVEM T1,ADRSPA(VARPTR)		;Save for later

;Allocate space for a working buffer

	PUSH P,[^D100]			;The number of words to get
	CALL UM%GET			;(1S/T1)Get them
	ADJSP P,-1			;Reset the stack
	JUMPLE T1,BUFERR		;An error occurred, halt
	MOVEM T1,WRKBUF(VARPTR)		;Save for later

;Allocate space for a second working buffer

	PUSH P,[^D100]			;The number of words to get
	CALL UM%GET			;(1S/T1)Get them
	ADJSP P,-1			;Reset the stack
	JUMPLE T1,BUFERR		;An error occurred, halt
	MOVEM T1,WRKBU2(VARPTR)		;Save for later
	RET				;Return

BUFERR:	PUSH P,[POINT 7,[ASCIZ /No memory available at BUFERR:(LISVAX)/]]
	PUSHJ P,NMLDIE			;No memory available, halt
SUBTTL	SETMSG - SET UP THE ERROR MESSAGES


;Set up the success/fail messages

SETMSG: $TRACE <At SETMSG...>
	SETZ T2,		;Zero flag word
	TLO T2,SUCBIT		;Set the success bit
	MOVEM T2,SUCMSG(VARPTR) ;Save for later

	SETZ T2,		;Zero the flag word
	TLO T2,FAIBIT		;Set the failure bit
	MOVEM T2,FAIMSG(VARPTR) ;Save for later

	MOVEI T1,ERMSG3		;Address of invalid node name text
	HRLI T1,(POINT 8)	;Make into a pointer
	MOVEM T1,NODMSG(VARPTR) ;Save for later

;Make a pointer to the local node name to be used in error messages and for
;To: list processing

	MOVEI T4,NODNAM		;Address of the local node name asciz string
	HRLI T4,(POINT 7)	;Make into a pointer
	MOVEM T4,NODE7(VARPTR)	;Save for later
	RET 

ERMSG1:	BYTE(8)"%","N","e","t","w","o","r","k"," ","M","a","i","l"," ","E","r"
	BYTE(8)"r","o","r",":"," ","N","o"," ","s","u","c","h"," ","U","s","e"
	BYTE(8)"r"," ","A","t"," ","N","o","d","e"," "

ERMSG3:	BYTE(8)"%","N","e","t","w","o","r","k"," ","M","a","i","l"," ","E","r"
	BYTE(8)"r","o","r"," ","N","o"," ","S","u","c","h"," ","N","o","d","e"
	BYTE(8)" "
SUBTTL	CONECT	DETERMINE THE PROTOCOL AND MAKE THE CONNECTION

;Fill in the connect block

CONECT:	MOVE T1,CNNBLK(VARPTR) ;Get address of the data block

	SETZM 0(T1)			;Zero the first word
	HRLI T2,0(T1)			;Source word
	HRRI T2,1(T1)			;Destination word
	BLT T2,CBKSIZ-1(T1)		;Zero the rest of the block
	MOVX T2,VAXOBJ			;Get the object type
	MOVEM T2,2(T1)			;And store it
	PUSH P,[1]			;Connect type 1, target
	PUSH P,T1			;Address of the connection block
	PUSH P,[0]			;Required by un%ope
	PUSH P,[0]			;Required by un%ope
	PUSH P,[0]			;Required by un%ope
	CALL UN%OPEN			;(5S/T1)Open and block for initiation
	ADJSP P,-5			;Reset the stack
	SKIPLE T1			;Obtained a data link number?
	IFSKP.
	  PUSH P,[^D300]	  	;Error, sleep for awhile
	  CALL US%SLE			;(1S/T1)
	  ADJSP P,-1			;Reset the stack
	  JRST CONECT
	ENDIF.				;Try again
	MOVEM T1,DATLNK(VARPTR)		;Save the data link number

	MOVE T1,[NW$RCF]		;Log a "received connection from" msg
	MOVEI T2,3			;Skip over node number and byte count
	ADJBP T2,@CNNBLK(VARPTR)	;Get the pointer to the node name
	CALL SMXERR			;(T1,T2)Tell mx
	
;Set up to accept the connection

	SETZM FLGWRD(VARPTR)		;Assume version "0.0"
	JRST VERO			;For now, version "0.0"
REPEAT 0,< MOVE	T4,-1(P)		;Any optional data sent
	JUMPE T4,VER0			;No, so version "0.0"
	HRRZ T5,-2(P)			;Get the address of the data
	LOAD T2,PROVER,0(T5)		;Get the version number
	CAIE T2,VERSNM			;Is it the expected protocol version
	JRST VER0			;No, so assume version "0.0"
	LOAD T2,OSTYPE,0(T5)		;Get the type of operating system
	CAIN T2,VAXTYP			;From a vax
	CAIGE T4,MINSIZ			;Are protocol bytes present?
	JRST VER0			;No, so assume version "0.0"

;Protocol version 3.0 being used, check the options

	PUSH P,[OPRESW]			;Allocate space response optional data
	CALL UM%GET			;(1S/T1)Do it
	ADJSP P,-1			;Adjust the stack
	JUMPLE T1,R			;An error occurred
	MOVEM T1,SCRAT1(VARPTR) 	;Save for the buffer release
	SETZM 0(T1)			;Zero the first word
	SETZM 1(T1)			;And the next

	MOVEI T2,VERSNM			;Get our protocol version
	STOR T2,PROVER,0(T1)		;And store it
	MOVEI T2,TOPS20			;Get our operating system type
	STOR T2,OSTYPE,0(T1)		;And store it

	SETZM T2			;Prepare for flag word
	MOVE T3,1(T5)			;Get the master options word
	TXNE T3,USRNOT			;User to be notified?
	TXO T2,USRNOT			;Yes, set the flag bit

	MOVE T3,2(T5)			;Get the master mode word
	SETZM T4			;Prepare for the slave mode word
	TXNE T3,MSTPRE			;Master to prefix node to sender name
	TXO T4,SLVPRE			;Yes, set the slave mode word
	TXNE T3,MSTPRE			;Master to prefix node to sender name
	TXO T2,USRPRE			;Yes, set the flag bit
	TXNE T3,MSTCC			;Master to send a cc: list
	TXO T4,SLVCC			;Yes, set the slave mode word
	TXNE T3,MSTCC			;Master to send a cc: list
	TXO T2,USRCC			;Yes, set the flag bit
	MOVEM T4,2(T1)			;Store in slave mode word
	MOVEM T2,FLGWRD(VARPTR) 	;Store in the flag word

;Accept the connection

	PUSH P,DATLNK(VARPTR)		;The data link identifier
	PUSH P,[OPRESW]			;Number of bytes
	HRLI T1,(POINT 8)		;Make it a pointer to the optional data
	PUSH P,T1			;Place on the stack
	CALL UN%ACC			;(3S/T1)Accept the connection
	ADJSP P,-3			;Reset the stack

	HRRZ T1,SCRAT1(VARPTR)		;Address of buffer to be released
	PUSH P,T1			;Pass as an argument
	PUSH P,[OPRESW]			;Number of words to be released
	CALL UM%REL			;(2S/T1)Release the buffer
	ADJSP P,-2			;Reset the stack
	TRNN T1,1			;An error occurred?
	RET 				;Yes
	RETSKP >			;No

;Using version "0.0"

VERO:	$TRACE <At VERO...>
	PUSH P,DATLNK(VARPTR)		;Data link number
	PUSH P,[0]			;No optional data
	PUSH P,[0]			;No optional data
	CALL UN%ACC			;(3S/T1)Accept the connection
	ADJSP P,-3			;Restore the stack
	TRNN T1,1			;O.k.?
	SKIPA 				;No, try again
	RET				;Yes
	PUSH P,[^D60]			;An error, sleep for awhile
	CALL US%SLE			;(1S/T1)
	ADJSP 	P,-1			;Reset the stack
	JRST VERO			;Try again
SUBTTL	MAIOPN	GET THE MAIL FILE NAME AND OPEN THE MAIL FILE

;Get the mail file name

MAIOPN:	$TRACE <At MAIOPN...>
	CALL MX%UNQ 			;(/T1)Get the file spec
	HRLI T1,(POINT 7)		;Make into a pointer
	MOVEM T1,MAIPTR(VARPTR)		;Save for later

;Open the mail file

	PUSH P,T1			;Pointer to file spec
	PUSH P,[2]			;Write access
	PUSH P,[0]			;No error buffer
	CALL UF%OPE			;(3S/T1)Open the mail file
	ADJSP P,-3			;Restore the stack
	JUMPLE T1,R			;An error occurred
	MOVEM T1,FILNB(VARPTR)		;Save the file number for later
	RETSKP				;And return true
SUBTTL	SNDREC - CREATE THE SENDER RECORD

SNDREC: $TRACE <At SNDREC...>
	MOVE MSGPTR,MSGSTR(VARPTR)	;Address of start of message
	SETZM MSGRCT(VARPTR)		;No records yet
	ADDI MSGPTR,.HDRSZ		;Skip the header record for now
	AOS T4,MSGRCT(VARPTR)		;Increment the number of records
	MOVEM T4,.RECNM(MSGPTR)		;Store it
	MOVEI T4,.SENDR			;Get the record type
	MOVEM T4,.RECTY(MSGPTR)		;Store it
	MOVEI WRDCNT,.HDRSZ		;Number of words = record header size

;Get the from string

	PUSH P,DATLNK(VARPTR)		;Data link number
	PUSH P,[^D280]			;Maximum length of the string
	MOVE T7,SNDSTR(VARPTR)		;Point to the from string
	PUSH P,T7			;Pass as an argument
	SETZM 0(T7)			;Zero start of buffer
	HRLI T4,0(T7)			;Prepare for the blt
	HRRI T4,1(T7)
	BLT T4,^D69(T7)			;Zero out the buffer
	CALL UN%REA			;(S3/T1)Read the string
	ADJSP P,-3			;Reset the stack
	JUMPL T1,R			;An error occurred, return false

;Go through the sender string and set pointers to last node name and the
;User name

	MOVE T1,T7			;Assume sender name
	SETZ T2,			;Assume no node name
	SETZ T4,			;Assume no quotes
	SETZM SCRAT3(VARPTR)		;Assume no personal name
	MOVE T10,T7			;Keep t7 for actual transfer

GOTRU0:	SETZ T6,			;[302] Initialize the byte count
	CALL SKIPQT			;(T6,T10/T3,T4,T6,T10)Dont parse
                                        ; within a quoted string 
GOTRU:	 ILDB T3,T10			;Get the next byte from sender string
	CAIN T3," "			;End of the sender name?
	JRST FNDPER			;Yes, check for personal name
	CAIN T3,0			;End of the sender name?
	JRST TRANSN			;Yes, copy sender name to sender record
	AOS T6				;[302] increment the byte count
	TXNE T4,SNQTS			;Have i seen any special characters?
	JRST GOTRU			;[302] yes, skip these tests
	CAIN T3,"("			;Is it a special character?
	JRST NEEDQT			;Yes.  flag it.
	CAIN T3,")"
	JRST NEEDQT
	CAIN T3,"<"
	JRST NEEDQT
	CAIN T3,">"
	JRST NEEDQT
	CAIN T3,"@"
	JRST NEEDQT
	CAIN T3,","
	JRST NEEDQT
	CAIN T3,";"
	JRST NEEDQT
	CAIN T3,"\"
	JRST NEEDQT
	CAIN T3,QUOTE
	JRST NEEDQT
	CAIN T3,"["
	JRST NEEDQT
	CAIN T3,"]"
	JRST NEEDQT
	CAIE T3,":"			;[302] end of this node name?
	JRST GOTRU			;No, get the next character
	IBP T10				;Point to next node or sender name
	MOVE T2,T1			;Make user pointer the node pointer
	MOVE T1,T10			;The new user pointer
	JRST GOTRU0			;Check for the next name

NEEDQT:	TXO T4,SNQTS			;[302] set "sender needs quotes" bit
	AOS T6				;[302] increment the byte count
	JRST GOTRU			;Continue

; SKIPQT - skips past quoted string if present
; Calling sequence
; 	SETZ T6				;clear the count
;	MOVE T10,POINTER		;put the pointer in t10
;	CALL SKIPQT			;do it
;LOOP:	ILDB T3,T10			;scan non-quoted users
;
; Registers / parameters
;	T3  - contains the last character
;	T4  - will have snqts set to one if no trailing quote was seen
;	T6  - the count of the number of bytes (including quotes)
;	T10 - the ildb byte pointer to the string
;
; Return: skip always

SKIPQT: ILDB T3,T10			;[302] get the next character
	CAIE T3,QUOTE			;[302] is it a quote?
	RETSKP				;[302] no

SKLOOP:	AOS T6				;[302] yes, increment the count
	ILDB T3,T10			;[302] get next character
	CAIN T3,QUOTE			;[302] is it a quote?
	JRST SKIPDN			;[302] yes, finish...
	JUMPN T3,SKLOOP			;[302] not a quote.  is it a null?
					;[302]  here if no trailing quote, so
	MOVNI T3,1			;[302] back the pointer up. -1 into t3
	ADJBP T3,T10			;[302] t3 now has the backed up pointer
	MOVE T10,T3			;[302] store the updated pointer in t10
        LDB T3,T10			;[302] put the last character in t3
	TXO T4,SNQTS			;[302] set the sender needs quotes bit
	RETSKP				;[302] all done

SKIPDN:	ILDB T3,T10			;[302] point past the quote
	AOS T6				;[302] increment the count
	RETSKP				;[302] return

;Determine if there is a personal name

FNDPER:	ILDB T3,T10			;Get the next character
	CAIN T3,0			;End of the sender name
	JRST TRANSN			;Yes, no personal name found
	CAIE T3,QUOTE			;Start of the personal name?
	JRST FNDPER			;No, check the next character
	MOVEM T10,SCRAT3(VARPTR)	;Save pointer to personal name

;Copy sender name to the sender record

TRANSN:	MOVEI T5,.RECTX(MSGPTR)		;Sender name field in sender record
	HRLI T5,(POINT 7)		;Make it a pointer
	MOVEI T3,QUOTE			;Put a quote in t3
	PUSH P,T6			;[302] save the byte count
	TXNN T4,SNQTS			;Skip if quotes are needed
TRANS2:	ILDB T3,T1			;Get the next character
	IDPB T3,T5			;[302] place in the sender record
	SOJG T6,TRANS2			;[302] loop until done

;Copy over to the sender record the node name found, if there is one

TRSNOD:	POP P,T6			;[302]restore the byte count
	TXNN T4,SNQTS			;Skip if sender needs quotes
	JRST TRSNO1			;No...
	MOVEI T3,QUOTE			;Pick up a quote
	IDPB T3,T5			;Write the close-quote in the snder rec
	AOS T6				;[302] include the end-quote in count
TRSNO1:	SETZ T4,			;Done with t4
	MOVEI T3,"@"			;First, pick up an "at" sign
	IDPB T3,T5			;Place in the sender record
	AOS T6				;Increment the byte count
	JUMPE T2,NONODE			;Jump if no node name was found
TRSNO2:	ILDB T3,T2			;Get the next node name character
	CAIE T3,":"			;Found the end?
	IFSKP.
	  MOVEI T3,0			;Yes, make it asciz
	  IDPB T3,T5			;Place in sender record
	  AOS T6			;Increment the byte count
	  JRST NUMWRD 			;Find the sender record size
	ENDIF.
	IDPB T3,T5			;Place in sender record
	AOS T6				;Increment the character count
	JRST TRSNO2			;Get the next character

;No node name in sender string. use the one from mx's data block

NONODE:	MOVE T7,CNNBLK(VARPTR)		;Get address of the data block
	ADDI T7,20			;Node name
	HRLI T7,(POINT 8)		;Make into a pointer
	IBP T7				;Skip over to the node name
	IBP T7
	IBP T7
NONOD2:	ILDB T1,T7			;Get the next character
	AOS T6				;Increment the byte count
	IDPB T1,T5			;Place in the sender record
	CAIE T1,0			;Finish?
	JRST NONOD2			;No, get the next character

;Find the number of words in this record

NUMWRD:	IDIVI T6,BY2WRD			;Find the number of words
	SKIPE T7			;A partial word?
	AOS T6				;Yes, count as a full word
	ADDI T6,.RECHS			;Add the record header size
	MOVEM T6,.RECLN(MSGPTR)		;Store the record length
	MOVEM MSGPTR,SCRAT4(VARPTR)	;Save for building mail file header
	ADD MSGPTR,T6			;Point msgptr to the next record
	ADD WRDCNT,T6			;Number of words in this message
	RETSKP				;And return successfully
SUBTTL	MFHEAD	BUILD THE DATE AND FROM FIELDS FOR MAIL FILE

;First the date field

MFHEAD:	MOVE BUFPTR,STABUF(VARPTR)	;Point to the start of the buffer
	MOVEI T2,[ASCIZ /Date: /]	;The first part of the date line
	HRLI T2,(POINT 7)		;Make into a pointer
	SETZM BUFBYT(VARPTR)		;Number of bytes read so far
DATELP:	ILDB T3,T2			;Get the next byte
	CAIN T3,0			;End of the string?
	JRST DATE			;Yes, get the date
	IDPB T3,BUFPTR			;Place byte into the buffer
	AOS BUFBYT(VARPTR)		;Increment the byte count
	JRST DATELP			;Read the next byte

DATE:
TOPS20<	MOVE T1,BUFPTR			;Where to place the date and time
	SETO T2,			;Current date and time
	MOVSI T3,(OT%4YR!OT%SPA!OT%NCO!OT%NSC%!OT%SCL!OT%TMZ) ;Format options
	ODTIM%
	ERJMP .+1			;Should not happen
>
TOPS10<	PUSH P,BUFPTR			;Where to place the date and time
	CALL GETDAT			;(1S)Pick up the date and time
	ADJSP P,-1>			;Reset the stack

;Calculate the number of bytes in the date field

BYTCNT:	IBP BUFPTR			;Increment the original buffer pointer
	AOS BUFBYT(VARPTR)		;Increment the byte count
	CAMN BUFPTR,T1			;Equal the current buffer pointer?
	JRST MFHEA2			;Yes, continue
	JRST BYTCNT			;Check again

;Now the from field

MFHEA2:	MOVEI T2,[ASCIZ/
From: /]				;The first part of the from: line
	HRLI T2,(POINT 7)		;Make into a pointer
FROMLP:	ILDB T3,T2			;Get the next byte
	CAIN T3,0			;End of the string?
	JRST SNDCPY			;Yes, copy the sender's name
	IDPB T3,BUFPTR			;Deposit the byte in the buffer
	AOS BUFBYT(VARPTR)		;Increment the byte count
	JRST FROMLP			;Get the next byte

SNDCPY:	SKIPN T5,SCRAT3(VARPTR)		;A personal name present?
	JRST SNDCP2			;No, get the user name
	MOVEI T1,QUOTE			;Pick up a quote character
	IDPB T1,BUFPTR			;Place in the buffer
	AOS BUFBYT(VARPTR)		;Increment the byte count
TRSPE2:	ILDB T1,T5			;Get the next character
	CAIN T1,QUOTE			;Possible end of personal name?
	CALL FINEND			;(/T1)Go find out
	JUMPE T1,FINPER			;Finish up the personal name
	IDPB T1,BUFPTR			;Deposit the byte in the buffer
	AOS BUFBYT(VARPTR)		;Increment the byte count
	JRST TRSPE2			;Get the next byte

FINPER:	MOVEI T1," "			;Pick up a blank
	IDPB T1,BUFPTR			;Place in the buffer
	MOVEI T1,"<"			;Pick up a <
	IDPB T1,BUFPTR			;Place in the buffer
	AOS BUFBYT(VARPTR)		;Increment the byte count
	AOS BUFBYT(VARPTR)		;Increment the byte count

SNDCP2:	MOVE T2,SCRAT4(VARPTR)		;Address of sender record
	ADDI T2,.RECHS			;Address of from string
	HRLI T2,(POINT 7)		;Point to from string
SENDLP:	ILDB T3,T2			;Get the next byte
	CAIN T3,0			;End of the string?
	JRST CHKPER			;Yes, add a > if personal name present
	IDPB T3,BUFPTR			;Deposit the byte in the buffer
	AOS BUFBYT(VARPTR)		;Increment the byte count
	JRST SENDLP			;Get the next byte

CHKPER:	JUMPE T5,TOSETU			;Skip this is no personal name present
	MOVEI T1,">"			;Pick up a >
	IDPB T1,BUFPTR			;Place in the buffer
	AOS BUFBYT(VARPTR)		;Incrment the byte count

;Set up first part of to: line

TOSETU:	MOVEI T2,[ASCIZ/
To: /]					;The first part of the to: line
	HRLI T2,(POINT 7)		;Make into a pointer
TOLP:	ILDB T3,T2			;Get the next byte
	CAIN T3,0			;End of the string?
	RET 				;Yes, return
	IDPB T3,BUFPTR			;Deposit the byte in the buffer
	AOS BUFBYT(VARPTR)		;Increment the byte count
	JRST TOLP			;Get the next byte

;Check for end of the personal name

FINEND:	MOVE T2,BUFPTR			;Preserve current buffer pointer
	MOVE T4,BUFBYT(VARPTR)		;Preserve current buffer byte count
	MOVEI T1,"'"			;[361]Replace " with '
FINEN2:	IDPB T1,BUFPTR			;Deposit byte in buffer
	AOS BUFBYT(VARPTR)		;Increment byte count
	ILDB T1,T5			;Get the next byte
	CAIN T1,0			;End of the person name?
	JRST RSTPTR			;Yes, reset the pointer and byte count
	CAIN T1,QUOTE			;Another quote found?
	JRST FINEND			;Yes, start over
	CAIN T1," "			;A blank found
	JRST FINEN2			;Yes, continue to look for end
	RET				;No, quote is part of personal name
RSTPTR: MOVEI T3,QUOTE			;[361]Close "
	DPB T3,BUFPTR			;[361]Write it
	MOVE BUFPTR,T2			;Restore the buffer pointer
	IBP BUFPTR			;Account for the final quote
	AOS T4				;Account for the final quote
	MOVEM T4,BUFBYT(VARPTR)		;Restore the byte count
	RET
SUBTTL	DSTREC	COMPLETES THE MESSAGE AND SENDS IT TO MX

;Do some prelimary setups first

DSTREC:	$TRACE <At DSTREC>
	SETZM NOMORE(VARPTR)		;Zero means message has more pages
	SETOM MSGNUM(VARPTR)		;Page number-1 of current message page
	SETZM IDNUM(VARPTR)		;Id number is zero for 1st page of msg
	SETZM FILCLO(VARPTR)		;Mail file has not been closed
	MOVEI T1,3			;The # of names/line in the to: list
	MOVEM T1,NAMLIN(VARPTR)		;Save for later
	SETZM NUMUSR(VARPTR)		;No recipients yet
	MOVE T1,STABLK(VARPTR)		;Get address of the first error block
	MOVEM T1,INVPTR(VARPTR)		;It's the same as the current error blk
	CALL CLRPAG			;(/)Clear the recipient status buffer
NXTSTR:	$TRACE  <At NXTSTR...>
	CAIG WRDCNT,LSTREC		;Room for another record
	IFSKP.
	  CALL SNDMSG			;(/)No, so send off the message to mx
	   RET	 			;An error occurred
	ENDIF.
	PUSH P,DATLNK(VARPTR)		;Data link number
	PUSH P,[^D400]			;Maximum length of buffer
	MOVE T5,ADRSPA(VARPTR)		;Get the buffer pointer
	PUSH P,T5			;Pass as an argument
	SETZM 0(T5)			;Zero first word of the buffer
	HRLI T1,0(T5)			;Set up the blt
	HRRI T1,1(T5)
	BLT T1,^D99(T5)			;Zero the buffer
	CALL UN%REA			;(3S/T1)Get the next user
	ADJSP P,-3			;Reset the pointer
	JUMPL T1,R			;An error occurred
	MOVE T5,ADRSPA(VARPTR)		;Get the buffer pointer
	ILDB T2,T5			;Get the first byte
	CAIN T1,1			;One byte for this recipient?
	CAIE T2,0			;Yes, a null?
	SKIPA				;Another recipient string
	JRST SNDLST			;No more recipients
	AOS NUMUSR(VARPTR)		;Increment the recipient count
	MOVE T5,ADRSPA(VARPTR)		;Pointer to recipient string

;The following has been zeroed out. This code dealt with transforming
;a recipient string of the form N1::N2::...NN::USER to SMTP format,
;i.e., @N1,@N2,...NN-1:USER@NN. As it turns out, only the last node
;is needed by MX, so the original string is now transformed to USER@NN

REPEAT 0,<
NXTCHR:	SETZ T10,			;Number of characters in user name
	MOVE T4,T3			;Point to start of temporary string
NEXTCH:	ILDB T2,T5			;Get the next character
	CAIE T2,":"			;Found a node?
	JRST CHKUSR			;No, maybe the user name

;A node has been found. if it is the first node validate it. If valid
;or not the first node, then append node name in temporary buffer with
;a comma

	ILDB T2,T5			;Strip off the second colon
	AOS T2,NODCNT(VARPTR)		;Increment the number nodes found
	CAIE T2,1			;The first node?
	JRST ADDCOM			;No, add a comma

	MOVEI T2,0			;Make node name into asciz string
	MOVE T7,T4			;Save current end of temp. buffer
	IDPB T2,T4			;Place into the temporary buffer
	CALL NODVAL			;(T4/T1)Validate the first node
	HRRES T1			;Make the return code a full word
	JUMPGE T1,NODEGD		;Node is valid
	CALL INVNOD			;(T7)Node is invalid
	RET 				;End this session
	JRST NXTSTR			;Get the next recipient

NODEGD:	MOVE T4,T7			;Reset end of temporary buffer
ADDCOM:	MOVEI T2,","			;Pick up a comma
	IDPB T2,T4			;Place in the temporary buffer
	MOVE T3,T4			;Pointer to next string component
	JRST NXTCHR			;Get the next character of recipient

;Check for user name. if found validate it. if invalid, go for the next
;recipient if valid, map the temporary buffer into the form used by
;MX

CHKUSR:	AOS T10				;Increment number of characters in name
	CAIN T2," "			;A user name found?
	JRST VLUSER			;Yes, validate it
	CAIN T2,0			;A user name found?
	JRST VLUSER			;Yes, validate it
	IDPB T2,T4			;No, place character into temp. buffer
	JRST NEXTCH			;Get the next character in user name

;Validate the user name

VLUSER: $TRACE <At VLUSER...>
	MOVEI T2,0			;Make into an asciz string
	IDPB T2,T4
	PUSH P,T3			;Destroyed by the call
	PUSH P,T4			;Destroyed
	PUSH P,T5			;Destroyed
	PUSH P,T10			;Number of bytes in user name
	PUSH P,T3			;Point to the user name
	MOVE T1,VALUSR(VARPTR)		;Pointer to pobox string (we dont need)
	PUSH P,T1			;Pass as an argument
	CALL MX$VAL			;(3S/T1)Validate the user name
	ADJSP P,-3			;Reset the stack
	POP P,T5			;Restore
	POP P,T4			;Restore
	POP P,T3			;Restore
	JUMPG T1,TELVAL			;Valid user
	CALL INVUSR			;(/)Invalid user, tell the vax
	RET				;Error, too many invalid users
	JRST NXTSTR			;Get the next recipient

TELVAL:	$TRACE <At TELVAL...>
	CALL VUSER			;(/)Valid user, tell the vax
	RET				;Network error occurred
	AOS T1,STAPTR(VARPTR)		;Increment the recipient count
	HRRZS T1			;Isolate the address
	ANDI T1,777			;Get the page offset
	CAILE T1,MAXNUM			;End of this error block page?
	CALL NXTBUF			;(/)Yes, get another page
	MOVEM T3,SCRAT1(VARPTR)		;Pointer to user name, for local to:

	MOVE T2,NODCNT(VARPTR)		;Number of nodes in recipient string
	CAIN T2,0			;No node names?
	SETOM LOCAL(VARPTR)		;Yes, set local node flag for local to:

;Finished the building of the temporty buffer. now copy it to the recipient
;Record in the format used by mx

	SETZ T7,			;Number of bytes in string so far
	MOVE T4,WRKBUF(VARPTR)		;Point to start of temporary buffer
	HRLI T4,(POINT 7)		;Make it into a pointer
	SKIPE T10,NODCNT(VARPTR)	;Nodes in recipient string?
	JRST NODFND			;Yes, pick up the first node

;Just a user name in the recipient string, copy over to recipient record

CPYNAM:	ILDB T2,T4			;Get the next character
	AOS T7				;Increment byte count
	CAIN T2,0			;At the end of the user name?
	JRST TRSND1			;Yes, transfer the local node name
	IDPB T2,T6			;Place in the record
	JRST CPYNAM			;No, get the next character
TRSND1:	MOVEI T2,"@"			;Pick up an ampersand
	IDPB T2,T6			;Place in the record
	MOVE T4,NODE7(VARPTR)		;Point to the local node name
TRSND2:	ILDB T2,T4			;Get the next node name character
	AOS T7				;Increment the byte count
	IDPB T2,T6			;Place byte in the record
	CAIE T2,0			;End of the node name?
	JRST TRSND2			;No, get the next character
	CALL FINRCD			;(T4,T5,T7)Yes, finish the record
	JRST NXTSTR			;Get the next recipient

;At least one node name in recipient string

NODFND:	CAIE T10,1			;Only 1 node name in recipient string?
	JRST MRENOD			;No, process differently

;Only 1 node name in recipient string

	MOVE T3,T4			;Remember start of node name
	CALL MOVUSR			;(T4,T6,T7)Copy node,user to user@node
	CALL FINRCD			;(t4,t5,t7)Get next recipient string
	JRST NXTSTR			;Get the next recipient

;N node names in recipient string. copy first n-1 over to recipient record

MRENOD:	SOS T10				;Number of nodes - 1
SEVNOD:	MOVEI T2,"@"			;Pick up an @
	IDPB T2,T6			;Place in the recipient record
	AOS T7				;Increment the byte count

NODMOV:	ILDB T2,T4			;Next character from temporary buffer
	CAIN T2,","			;And of this node name?
	JRST ENDNOD			;Yes, see if it is the n-1th node name
	IDPB T2,T6			;Place in recipient record
	AOS T7				;Increment the byte count
	JRST NODMOV			;Get the next character

ENDNOD:	SOSN T10			;N-1th node?
	IFSKP.
	  MOVEI T2,","			;No, get a comma
	  IDPB T2,T6			;Place in the record
	  AOS T7			;Increment the byte count
	  JRST SEVNOD		        ;Get the next node
	ENDIF.

;The last node name has been found. move the user name over to the recipient
;Record, followed by @node-name

	MOVEI T2,":"			;Pick up a colon
	IDPB T2,T6			;Append to the n-1th node name
	AOS T7				;Increment the byte cound
	MOVE T3,T4			;Remember start of final node name
	CALL MOVUSR			;(T4,T5,T7)Copy node,user to user@node
	CALL FINRCD			;(T4,T6,T7)Get the next recipient
	JRST NXTSTR			;Get the next recipient

;Copy the string in the temporary buffer of the form "node,user" to the
;Recipient record in the form "user@node"

MOVUSR:	ILDB T2,T4			;Get  next character from temp. buffer
	CAIE T2,","			;End of the last node name?
	JRST MOVUSR			;No, try again

NXTONE:	ILDB T2,T4			;Get next user name character
	CAIN T2,0			;End of the user name?
	JRST LSTNOD			;Yes, copy the final node name
	IDPB T2,T6			;Place character in recipient record
	AOS T7				;Increment the chartacter count
	JRST NXTONE			;Get next user name character

;Copy over the final node name

LSTNOD:	MOVEI T2,"@"			;Pick up an @
	IDPB T2,T6			;Place after the user name

CPYOVR:	ILDB T2,T3			;Next character of node name
	AOS T7				;Increment the byte count
	CAIN T2,","			;End of node name?
	JRST FINSTR			;Yes, finish recipient string in record
	IDPB T2,T6			;No, place character in record
	JRST CPYOVR			;Get the next character

FINSTR:	MOVEI T2,0			;Make recipient string in recipient
	IDPB T2,T6			;Record into an asciz string
	RET
>; End of smtp formatting

;Find pointers to the node name (if any) and the recipient name
;       T10 = pointer to recipient name
;       T7  = Node name flag
;       T6  = Temporary node name pointer
;       T5  = Current working pointer
;       T4  = Byte count

        SETZ T10,                       ;Assume no recipient yet
	SETZ T7,			;Assume no node name
	SETZ T4,			;[302] clear the byte count


NXTNOD: MOVE T6,T5                      ;Set the temp node pointer
        ILDB T1,T5                      ;Get next character
        ADDI T4,1                       ;Increment the recipient count
        CAIN T1,QUOTE                   ;Is it a quote?
        JRST GETQU                      ;Yes, get quoted username

GETNU:  ILDB T1,T5                      ;Get next character
        CAIN T1,":"                     ;Is it a colon
        JRST GOTNOD                     ;Yes
        CAIE T1," "                     ;No.  How about a space
        SKIPN T1                        ;Or a null?
         JRST GETQU1                    ;And rejoin quoted username code
        ADDI T4,1                       ;Increment the recipient count
        JRST GETNU                      ;Not at end of string, keep looking

GOTNOD: SETZ T4,                        ;Clear the recipient count
        ILDB T1,T5                      ;Skip past the second ":"
        MOVE T7,T6                      ;Save the node pointer.
        JRST NXTNOD                     ;Go look for another node

GETQU:  ILDB T1,T5                      ;Get next character
        ADDI T4,1                       ;Increment the recipient count
        CAIE T1,QUOTE                   ;Are you a quote?
        JRST GETQU                      ;No.  Keep looking
GETQU1: MOVE T10,T6                     ;Save the recipient pointer

;If a node name is present, validate it; otherwise, validate the recipient

CHKNDE:	SKIPN T7			;Node name present?
	JRST GETUSR			;No, validate the recipient
	MOVE T6,WRKBUF(VARPTR)		;Address of working buffer
	HRLI T6,(POINT 7)		;Make into a pointer
	MOVE T5,T6			;[302] save start of working buffer
TRNSND:	ILDB T1,T7			;Get the next node name character
	CAIN T1,":"			;End of the node name?
	JRST COLFND			;Yes, validate the node name
	IDPB T1,T5			;No, place character in working buffer
	JRST TRNSND			;Get the next character

;The node name has been isolated in the working buffer, now validate it

COLFND:	SETZ T1,			;Asciz termination character
	IDPB T1,T5			;Make node name asciz
	MOVE T7,T6			;[302] remember start of node name
	CALL NODVAL			;(T4/T1)Validate the node name
	HRRES T1,T1			;Make the return code a full word
	JUMPGE T1,GETUSR		;Valid node, move recipient to record
	CALL INVNOD			;(T7)Mark this recipient as invalid
	RET				;Network error occurred, terminate
	JRST NXTSTR			;Get the next recipient

;Place the recipient name in the recipient record. if there is no node
;Name then validate the recipient

GETUSR:	PUSH P,T4			;[302] save the byte count
	MOVE T6,MSGPTR			;Address of start of this record
	ADDI T6,.RECHS			;Address of start of user name string
	HRLI T6,(POINT 7)		;Make into a pointer
	MOVE T5,T6			;Save start of recipient field

GETUS2:	ILDB T1,T10			;Get the next recipient character
	IDPB T1,T6			;[302] no, place in recipient record
	SOJG T4,GETUS2			;[302] loop until done

VALIDU:	POP P,T4			;Restore the byte count
	MOVE T10,T6			;Save record position for node name
	SETZ T1,			;Asciz termination character
	IDPB T1,T6			;Make recipient name asciz
	SKIPE T7			;Node name present?
	JRST TELVAL			;Yes, don't validate recipient name
	PUSH P,T4			;Destroyed by the call
	PUSH P,T5			;Destroyed
	PUSH P,T4			;Number of bytes in user name
	PUSH P,T5			;Point to the user name
	MOVE T1,VALUSR(VARPTR)		;Pointer to pobox string (we dont need)
	PUSH P,T1			;Pass as an argument
	CALL MX$VAL			;(S3/T1)Validate the user name
	ADJSP P,-3			;Reset the stack
	POP P,T5			;Restore
	POP P,T4			;Restore
	JUMPG T1,TELVAL			;Valid user
	CALL INVUSR			;(/)Invalid user, tell the vax
	RET				;Network error occurred

	JRST NXTSTR			;Get the next recipient

;The recipient is valid, send a success message to the vax and then finish
;Building the recipient record

TELVAL:	$TRACE <At TELVAL...>
	CALL VUSER			;(/)Valid user, tell the vax
	RET				;Network error occurred
	AOS STAPTR(VARPTR)		;Increment the recipient count
	MOVEI T1,"@"			;Pick up an @
	IDPB T1,T10			;Place in the recipient record
	SKIPN T7			;Node name in recipient name?
	MOVE T7,NODE7(VARPTR)		;No, use the local node name

TELVA2:	ILDB T1,T7			;Get the next node name character
	AOS T4				;Increment the byte count
	IDPB T1,T10			;Place in the recipient record
	CAIE T1,0			;End of the node name?
	JRST TELVA2			;No, get the next character

	CALL FINRCD			;(T4,T6,T7)Finish the record
	JRST NXTSTR			;Get the next recipient

NODVAL:	$TRACE <At NODVAL...>

	PUSH P,T4			;[rbw]Save T4 (DB%VD8 will smash it)
	PUSH P,T6			;[rbw]Node name
	PUSH P,[1]			;Local domain
	PUSH P,[-1]			;Unknown domain
	CALL DB%VD8			;(3S/T1)Check out the node
	ADJSP P,-3			;Reset the stack
	POP P,T4			;[rbw]Restore T4 (God! This is ugly!)
	RET
SUBTTL INVNOD	 INVALID RECIPIENT DETECTED

INVNOD:	MOVX T2,INVLND			;Invalid node name detected
	SKIPA
INVUSR:	MOVX T2,INVLUS			;Invalid recipient
	MOVE T1,STAPTR(VARPTR)		;Get pointer of current entry
	MOVEM T2,0(T1)			;Place type of error in entry
	AOS T1				;Point to the next word in entry
	TXNN T2,INVLND			;Invalid node name?
	JRST INVUS2			;No, go update recipient status address
TRSCHR:	ILDB T2,T7			;Get next node character
	IDPB T2,T1			;Place in the buffer
	CAIE T2,0			;Finished?
	JRST TRSCHR			;No, get the next character
	MOVE T1,STAPTR(VARPTR)		;Pointer to current entry
	ADDI T1,^D3			;Point to the next entry
INVUS2:	MOVEM T1,STAPTR(VARPTR)		;Save for the next invalid user
	HRRZS T1			;Isolate the address
	ANDI T1,777			;Get the page offset
	CAILE T1,MAXNUM			;End of this page?
	CALL NXTBUF			;(/)Yes, get another page
	CALL VUSER			;(/)Fake out the vax
INVRET:	RET
	RETSKP				;Return true


;Valid user - tell the vax

VUSER:	$TRACE <At VUSER...>
	PUSH P,T4			;Destroyed
	PUSH P,T5			;Destroyed
	PUSH P,DATLNK(VARPTR)		;Data link number
	PUSH P,[1]			;End of message flag
	PUSH P,[4]			;Number of bytes in the message
	MOVEI T1,SUCMSG(VARPTR)		;Message address
	HRLI T1,(POINT 8)		;Make into a pointr
	PUSH P,T1			;Pass as an argument
	CALL UN%WRI			;(4S/T1)Do it
	ADJSP P,-4			;Reset the stack
	POP P,T5			;Restore
	POP P,T4			;Restore
	JUMPLE T1,R			;An error, return false
	RETSKP

;Get an additional page for the recipient status list

NXTBUF:	MOVE T2,STAPTR(VARPTR)		;Get address of last entry
	SETOM 0(T2)			;Mark it as the last entry
	CALL UMP%GET			;(/T1)Get a page
	JUMPL T1,[PUSH P,[POINT 7,[ASCIZ /Could not get page at NXTBUF:/]]
		    PUSHJ P,NMLDIE]	;An error occurred, halt
	LSH T1,^D9			;Change page number to an address
	MOVE T2,INVPTR(VARPTR)		;Address of previous page
	MOVEM T1,0(T2)			;Place address of current page in it
	MOVEM T1,INVPTR(VARPTR)		;Place in current recipient address ptr
;	CALL CLRPAG			;(/)Zero out the page
;	RET

;Clear the invalid recipient page upon detecting first invalid recipient

CLRPAG:	MOVE T1,INVPTR(VARPTR)		;Get starting address of the buffer
	SETZM 0(T1)			;Zero the first word
	HRLS T1				;Place starting adr in correct place
	AOS T1				;Form correct destination address
	MOVE T2,INVPTR(VARPTR)		;Don't want to use t1 twice in the blt
	BLT T1,777(T2)			;Zero the page
	MOVE T2,INVPTR(VARPTR)		;Get address of the buffer
	AOS T2				;First word is the link word
	HRLI T2,(POINT 8)		;Make into a pointer to 1st recipient
	MOVEM T2,STAPTR(VARPTR)		;Save for later
	RET
SUBTTL FINRCD	FINISH THE RECIPIENT RECORD

FINRCD:	$TRACE <At FINRCD...>
	MOVEI T1,.DESTN			;Record type
	MOVEM T1,.RECTY(MSGPTR)		;Place in the record
	AOS T1,MSGRCT(VARPTR)		;Increment number of records
	MOVEM T1,.RECNM(MSGPTR)		;Store in the record

	MOVE T7,T5			;Save recipient name for to: list
	IDIVI T4,BY2WRD			;Find the number of words in record
	SKIPE T5			;Any partial words?
	AOS T4				;Yes, count as a full word
	ADDI T4,.RECHS			;Add in the record header size
	MOVEM T4,.RECLN(MSGPTR)		;Place in the record
	ADDM T4,WRDCNT			;Save updated count
	ADD MSGPTR,T4			;Pointer to the next record

;Check for room on this line, if already 3 names start a new line

	SKIPLE NAMLIN(VARPTR)		;Space for one more?
	IFSKP.
	  MOVE T1,[POINT 7,[ASCIZ/
    /]]
	  DO.
	    ILDB T2,T1			;Get the next character
	    CAIE T2,0			;The last character?
	    IFSKP.
	      MOVEI T2,6		;Yes, Number of new characters
	      ADDM T2,BUFBYT(VARPTR)	;Update the byte count
	      MOVEI T3,3		;Number of names/line
	      MOVEM T3,NAMLIN(VARPTR) 	;Update the counter
	      JRST COPYNM 	  	;Continue with the next line
	    ENDIF.
	    IDPB T2,BUFPTR		;No, place in the buffer
	    LOOP.			;Go get another character
	  ENDDO.
	ENDIF.

;Copy the name

COPYNM:	ILDB T3,T7			;Get the next byte
	CAIN T3,0			;End of the string?
	JRST COMCPY			;Append a comma
	IDPB T3,BUFPTR			;Deposit the next byte
	AOS BUFBYT(VARPTR)		;Increment the byte count
	JRST COPYNM			;Get the next byte

COMCPY:	MOVEM BUFPTR,SCRAT4(VARPTR)	;Remember current position
	MOVEI T2,","			;Get the comma following the name
	IDPB T2,BUFPTR			;Place in the buffer
	MOVEI T2," "			;Get a blank
	IDPB T2,BUFPTR			;Place in the buffer
	MOVEI T2,2			;The number of additional bytes
	ADDM T2,BUFBYT(VARPTR)		;Update the byte count

;Update parameters

	SOS NAMLIN(VARPTR)		;Update names/line counter
	RET
SUBTTL	SNDMSG	SEND THE MESSAGE TO MX

;Set up the header record

SNDMSG:	$TRACE <At SNDMSG...>
	AOS T1,MSGNUM(VARPTR)		;Zero means the first page of message
	SKIPG T1			;Is this the first page?
	MOVEI T2,.POST			;Yes, indicate so
	SKIPE T1			;Is this the first page?
	MOVEI T2,.CONT			;No, indicate so
	HRLI T2,.DECNT			;We're the decnet listener
	MOVE T4,MSGSTR(VARPTR)		;Point to beginning of the message
	MOVEM T2,.PKTYP(T4)		;Store in the record
	MOVE T2,IDNUM(VARPTR)		;Pick up message id number
	MOVEM T2,.PKID(T4)		;Store in the record
	AOS T1				;Current page of the message
	MOVEM T1,.PKSEQ(T4)		;Store in the record
	MOVE T1,NOMORE(VARPTR)		;Pick up the last page flag

	MOVEI T2,.DONE			;Assume this is the last page
	SKIPN T1			;Is it?
	MOVE T2,.MORE			;No, at least one more page
	MOVEM T2,.PKSTS(T4)		;Store in the record
	MOVE T2,MSGRCT(VARPTR)		;Number of records so far
	SKIPE T1			;Is this correct?
	AOS T2				;No, include the file spec record
	MOVEM T2,.PKRCT(T4)		;Place in the record

;If this is the last page of the message, the mail file must be completed
;And the file spec record must be made before sending off the final page

	JUMPE T1,SNDOFF			;If 0 then there are more pages
	CALL FILSPC			;(/)Fill in the file spec record
	CALL FINFIL			;(/)Complete the mail file
	RET				;An error occurred
	CALL CLOFIL			;(/T1)Close the mail file
	CALL SNDOFF			;(/)Send off the final page of the msg
	 RET				;An error occurred
	SETOM FILCLO(VARPTR)		;Indicate the mail file has been closed
	CALL CFIRM			;(/)Send the success/failure messages
	 RET				;An error occurred
	RETSKP				;Return successfully

;Send off the message



SNDOFF: $TRACE <At SNDOFF...>
	MOVE MSGPTR,MSGSTR(VARPTR)	;Point to start of the message
	PUSH P,MSGPTR			;Pass as an argument
	PUSH P,[0]			;Required by scan%p
	PUSH P,[-1,,[ASCIZ/MX Mail-11 Listener/]] ;Required by scan%p
TOPS20<	PUSH P,[SC%WHL]	>		;Wheel privs
TOPS10<	PUSH P,[IP.JAC]	>		;Wheel privs
	CALL SCAN%P			;(4S/T1)Send off to MX
	ADJSP P,-4			;Reset the stack
	CAIN T1,0			;Error?
	RET				;Yes, return false

;Set up for the next page

	SKIPE NOMORE(VARPTR)		;Last page?
	RETSKP				;Yes, return true
	SETZM MSGRCT(VARPTR)		;No records yet in this message
	MOVE T1,.PKID(MSGPTR)		;Get the message id
	MOVEM T1,IDNUM(VARPTR)		;Store for later
	RETSKP				;Return true

;The last page of the message has been completed, send it off

SNDLST:	$TRACE <At SNDLST...>
	SETOM NOMORE(VARPTR)		;This is the last page of the message
	CALL SNDMSG			;(/)Send it off
	 RET 				;An error occurred
	RETSKP				;Return true
SUBTTL FILSPC	CREATE THE FILE SPEC RECORD

FILSPC:	$TRACE <At FILSPC...>
	AOS T1,MSGRCT(VARPTR)		;Pick up the number of records
	MOVEM T1,.RECNM(MSGPTR)		;Store in the record
	MOVEI T1,.FLSPC			;File spec record
	MOVEM T1,.RECTY(MSGPTR)		;Store in the record

;Copy file name over to spec record

	SETZ T4,			;Number of bytes in the file name
	MOVE T2,MAIPTR(VARPTR)		;Point to the file name
	MOVE T3,[POINT 7,.RECTX(MSGPTR)] ;Point to record location
COPNAM:	ILDB T1,T2			;Get the next byte
	IDPB T1,T3			;Place this byte in the record
	AOS T4				;Increment the byte count
	CAIE T1,0			;Is this the end?
	JRST COPNAM			;No, get the next character

;Find the record size

	IDIVI T4,BY2WRD			;Find the number of words in file spec
	SKIPE T5			;Any partial words
	AOS T4				;Yes, count as a full word
	ADDI T4,.RECHS			;Add size of the file header
	MOVEM T4,.RECLN(MSGPTR)		;Store length in record
	RET
SUBTTL FINFIL	COMPLETE THE MAIL FILE

;Get the mailed to: list from the vax

FINFIL:	$TRACE <At FINFIL...>
	SETZM EOFMES(VARPTR)		;End of vax message not yet found
	MOVE BUFPTR,SCRAT4(VARPTR)	;Get rid of last comma, blank
	MOVEI T1,15			;Replace the comma
	IDPB T1,BUFPTR			;With a carriage return
	MOVEI T1,12			;Replace the blank
	IDPB T1,BUFPTR			;With a line feed
	MOVE T6,WRKBU2(VARPTR)		;Prepare for the call
	HRLI T6,(POINT 8)		;Make it an 8 bit byte pointer
	CALL REALIN			;(T6)Read "MAILED-T0: line, copy
                                        ;to buffer...
	 RET				;An error occurred

;Get the subject line from the vax

	MOVE T6,WRKBUF(VARPTR)		;Get the working buffer address
	HRLI T6,(POINT 8)		;Make into an 8 bit byte pointer
	CALL REALIN			;(T6)Copy the subject: to the buffer
	RET				;An error occurred

;Copy the subject line

	MOVEI T2,[ASCIZ/Subject: /]	;Address of the string
	HRLI T2,(POINT 7)		;Make into a pointer
SUBJLP:	ILDB T3,T2			;Get the next byte
	CAIN T3,0			;End of the string?
	JRST SUBCOP			;Yes, save the updated buffer pointer
	IDPB T3,BUFPTR			;Store this byte in the buffer
	AOS BUFBYT(VARPTR)		;Increment the byte count
	JRST SUBJLP			;Get the next byte
SUBCOP:	MOVE T7,WRKBUF(VARPTR)		;Point to the buffer to be copied
	CALL CPYBUF			;(T7)Copy over the subject

;Copy the mailed to: line to the buffer

	MOVEI T2,[ASCIZ/Mailed-to: /]	;[304] line to be copied
	HRLI T2,(POINT 7)		;Make into a pointer
MAILP:	ILDB T3,T2			;Get the next byte
	CAIN T3,0			;End of the string?
	JRST SAVEPT			;Yes, save the updated buffer pointer
	IDPB T3,BUFPTR			;Store this byte in the buffer
	AOS BUFBYT(VARPTR)		;Increment the byte count
	JRST MAILP			;Get the next byte
SAVEPT:	MOVE T7,WRKBU2(VARPTR)		;Address of the mailed to: list
	CALL CPYBUF			;(T7)Copy over to the buffer

;Insert two blank lines

	MOVEI T7,[ASCIZ/
/]
	CALL CPYBUF			;(T7)Copy over to the buffer
	MOVEI T1,2			;Number of new characters
	ADDM T1,BUFBYT(VARPTR)		;Update the buffer character count

;Copy the buffer to the mail file

	MOVE T1,STABUF(VARPTR)		;Point to start of buffer
	PUSH P,FILNB(VARPTR)		;File number of the mail file
	PUSH P,T1			;Point to start of the buffer
	MOVE T1,BUFBYT(VARPTR)		;Number of bytes to copy
	PUSH P,T1			;Pass as an argument
	PUSH P,[0]			;No error buffer
	CALL UF%WRI			;(4S/T1)Write to the mail file
	ADJSP P,-4			;Reset the stack

;Now copy the message to the mail file

CPYMES:	SETZM BUFBYT(VARPTR)		;Number of bytes in buffer so far
	HRR T6,STABUF(VARPTR)		;Address to read next msg line into
	HRLI T6,(POINT 8)		;Make it an 8 bit byte pointer
	CALL REALIN			;(T6)Read the next line
	RET 				;An error occurred, return false
	SKIPE EOFMES(VARPTR)		;End of message?
	JRST CPYDSH			;Yes, add dashes at end of the message
	MOVE T1,STABUF(VARPTR)		;Point to the start of the buffer
	MOVE T2,BUFBYT(VARPTR)		;The number of bytes to copy
	PUSH P,FILNB(VARPTR)		;File number of the mail file
	PUSH P,T1			;Start of the buffer
	PUSH P,T2			;Number of bytes to copy
	PUSH P,[0]			;No error buffer
	CALL UF%WRI			;(4S/T1)Write the buffer to the
                                        ;mail file
	ADJSP P,-4			;Reset the stack
	JRST CPYMES			;Get the next line


CPYDSH:	MOVE T1,STABUF(VARPTR)		;Point to the start of the buffer
	MOVE T6,T1			;Copy for the destination of the blt
	HRLI T6,FINDSH			;Source of the blt
	BLT T6,^D3(T1)			;Copy dashes to the buffer
	MOVEI T2,^D13			;Number of bytes in this line
	PUSH P,FILNB(VARPTR)		;File number of the mail file
	PUSH P,T1			;Start of the buffer
	PUSH P,T2			;Number of bytes to copy
	PUSH P,[0]			;No error buffer
	CALL UF%WRI			;(4S/T1)Write the buffer to the
                                        ;mail file 
	ADJSP P,-4			;Reset the stack
	RETSKP				;Finished, return true

FINDSH:	ASCIZ/   --------
/
SUBTTL REALIN	READS NEXT LINE FROM VAX AND PLACES IN THE MAIL FILE

;First, read in the line
;This routine is called with t6 pointing to where the data is to be read
;To (it must be an 8 bit byte pointer)

REALIN:	PUSH P,DATLNK(VARPTR)		;The data link number
	PUSH P,[^D400]			;The number of words max
	PUSH P,T6			;Where the line is to be read to
	SETZM 0(T6)			;Zero the first word of the buffer
	HRLI T7,0(T6)			;Prepare for the blt
	HRRI T7,1(T6)
	BLT T7,^D99(T6)			;Zero out the buffer
	CALL UN%REA			;(3S/T1)Get the 8 bit line from the vax
	ADJSP P,-3			;Reset the stack
        JUMPL T1,R			;An error, return false
	MOVE T7,T6			;Save for the sout

;Translate the 8 bit line to 7 bits

	HRR T5,T6			;Get the address for the 7 bit line
	HRLI T5,(POINT 7)		;Make it a 7 bit pointer

	CAIE T1,1			;Message 1 byte in length?
	JRST TRNSFR			;No, not end of message
	ILDB T2,T6			;Yes, get the byte
	CAIE T2,0			;A null?
	JRST TRNSF2			;No, change to 7 bits
	SETOM EOFMES(VARPTR)		;Yes, end of message
	RETSKP				;Return true

TRNSFR:	ILDB T2,T6			;Get the next byte
	CAIN T2,0			;End of the line?
	JRST ENDLN			;Yes, finish up
TRNSF2:	IDPB T2,T5			;Place in the 7 bit line
	AOS BUFBYT(VARPTR)		;Increment the byte count
	JRST TRNSFR			;Get the next byte

;Finish the line with a carriage return, line feed

ENDLN:	MOVEI T2,15			;Get the carriage return
	IDPB T2,T5			;Place in the working buffer
	MOVEI T2,12			;Get the line feed
	IDPB T2,T5			;Place in the working buffer
	MOVEI T2,0			;Make it an asciz string
	IDPB T2,T5			;Place in the working buffer
	MOVEI T2,2			;Number of additional bytes in buffer
	ADDM T2,BUFBYT(VARPTR)		;Update the byte count
	RETSKP

;Copy the working buffer to the buffer

CPYBUF:	HRR T2,T7			;Get address of the working buffer
	HRLI T2,(POINT 7)		;Make into a pointer
BUFLP:	ILDB T3,T2			;Get the next working buffer byte
	CAIN T3,0			;End of the string?
	RET				;Yes, return
	IDPB T3,BUFPTR			;Store this byte in the buffer
	JRST BUFLP			;Get the next byte
SUBTTL CFIRM	SEND SUCCESS/FAILURE MESSAGES

CFIRM:	$TRACE <At CFIRM...>
	MOVN T7,NUMUSR(VARPTR)		;Get the number of recipients
	MOVE T10,STABLK(VARPTR)		;Address of the recipient status buffer
	MOVE T6,T10			;Save in case there is more than 1 page
	AOS T10				;Skip over the link word
DONEXT:	MOVE T1,0(T10)			;Get the next recipient status word
	JUMPE T1,CHKSUC			;Zero means recipient received mail
	SKIPG T1			;The last entry of this page?
	CALL GTNXBF			;(T6/T10)Yes, get the next page of
                                        ;statuses
	CALL ERRRCT			;(/)Inform vax of error
	JRST CFIRET			;Network error occurred
	JRST CHKNXT			;Check status of next recipient
CHKSUC:	CALL SUCRCT			;(/)Inform vax recipient received mail
	JRST CFIRET			;Network error occurred
CHKNXT:	AOJN T7,DONEXT			;Check status of next recipient
	CALL RLSEPG			;(T7)Release any recipient status pages
	RETSKP				;Finished, return true
CFIRET:	RET				;Network error occurred, release
					;Recipient status pages later

;Get the address of the next recipient status page

GTNXBF:	MOVE T10,0(T6)			;Get the address from the current page
	MOVE T6,T10			;Save in case there is another page
	AOS T10				;Skip over the link word
	RET

;Release any recipient status pages

RLSEPG:	MOVE T1,STABLK(VARPTR)		;Get address of the resident page
	MOVE T7,0(T1)			;Get contents of the link word
RLSEP1:	SKIPN T7			;Is there any other pages?
	RET				;No, so quit
	MOVE T1,T7			;Get a copy of the address
	MOVE T7,0(T7)			;Get the link word of this page
	LSH T1,-^D9			;Change address to page number
	PUSH P,T1			;Pass the page number as an argument
	CALL UMP%RE			;(1S/T1)Release the page
	ADJSP P,-1			;Reset the stack
	JUMPL T1,[PUSH P,[POINT 7,[ASCIZ /Error releasing page at RLSEP1:/]]
		    PUSHJ P,NMLDIE ]	;Error in releasing the page
	JRST RLSEP1			;Release the next page, if any
SUBTTL ERRRCT SEND INVALID USER OR NODE MESSAGE

ERRRCT: $TRACE <At ERRRCT...>
INVND2:	PUSH P,DATLNK(VARPTR)		;Data link number
	PUSH P,[1]			;End of message flag
	PUSH P,[4]			;Number of bytes in the error message
	MOVEI T1,FAIMSG(VARPTR)		;Error message address
	HRLI T1,(POINT 8)		;Make it into a pointer
	PUSH P,T1			;Pass as an argument
	CALL UN%WRI			;(4S/T1)Tell the vax
	ADJSP P,-4			;Reset the stack
	JUMPLE T1,ENINN			;An error, abort the link
	
	MOVE T1,0(T10)			;Get the error flag
	TXNN T1,INVLND			;Invalid node name?
	JRST IVUSER			;No, invalid user
	MOVE T1,WRKBU2(VARPTR)		;Address of buffer to be sent to vax
	HRLI T1,ERMSG3			;Address of first part of message text
	BLT T1,10(T1)			;Copy to the buffer
	MOVE T1,WRKBU2(VARPTR)		;Address of buffer to be sent to vax
	HRLI T1,(POINT 8)		;Make into a pointer
	ADDI T1,10			;Point to the correct word
	IBP T1				;Skip over this byte
	MOVE T3,T10			;Address of current invalid node entry
	AOS T3				;Address of invalid node name
	HRLI T3,(POINT 8)		;Make into a pointer
	MOVEI T5,^D33			;Number of bytes in message so far
CHSIZE:	ILDB T2,T3			;Get the next 7 bit byte
	CAIN T2,0			;End of the string?
	JRST SNDNOD			;Yes, send to the vax
	IDPB T2,T1			;No, place in 8 bit byte name buffer
	AOS T5				;Increment byte count
	JRST CHSIZE			;Get the next byte

SNDNOD: $TRACE	<At SNDNOD...>
	PUSH P,DATLNK(VARPTR)		;Get the data link number
	PUSH P,[1]			;End of message flag
	PUSH P,T5			;The length of the message
	MOVE T1,WRKBU2(VARPTR)		;Point to the message
	HRLI T1,(POINT 8)		;Make into a pointer
	PUSH P,T1			;Pass as an argument
	CALL UN%WRI			;(4S/T1)Send the message off
	ADJSP P,-4			;Adjust the stack pointer
	JUMPLE T1,ENINN			;An error occurred, return false

	PUSH P,DATLNK(VARPTR)		;Data link
	PUSH P,[1]			;End of message
	PUSH P,[1]			;Message length
	MOVEI T1,[EXP 0]		;The message
	HRLI T1,(POINT 8)		;Make it into a pointer
	PUSH P,T1			;Pass as an argument
	CALL UN%WRI			;(4S/T1)Tell the vax
	ADJSP P,-4			;Reset the stack
	JRST COMCH2			;Check for another invalid node or user

IVUSER:	$TRACE <At IVUSER...>
	MOVE T1,WRKBU2(VARPTR)		;Address of buffer to be sent to vax
	HRLI T1,ERMSG1			;Address of first part of message text
	HRRZ T3,WRKBU2(VARPTR)		;Address for the blt
	BLT T1,12(T3)			;Copy to the buffer
	MOVE T1,WRKBU2(VARPTR)		;Address of buffer to be sent to vax
	HRLI T1,(POINT 8)		;Make into a pointer
	ADDI T1,12			;Point to the correct word
	IBP T1				;Skip over this byte
	IBP T1				;Skip over this byte
NXTPT2:	MOVEI T5,^D42			;Size of the message so far
	MOVE T3,NODE7(VARPTR)		;Pointer to node name
NXTPT3:	ILDB T2,T3			;Get the next character
	CAIN T2,0			;End of the node name?
	JRST INVMSG			;Yes, send off the message
	AOS T5				;No, increment the byte count
	IDPB T2,T1			;Place in the buffer
	JRST NXTPT3			;Get the next character

INVMSG:	$TRACE <At INVMSG...>
	PUSH P,DATLNK(VARPTR)		;Data link
	PUSH P,[1]			;End of message
	PUSH P,T5			;Message length
	MOVE T1,WRKBU2(VARPTR)		;Address of the buffer
	HRLI T1,(POINT 8)		;Make into a pointer
	PUSH P,T1			;Pass as an argument
	CALL UN%WRI			;(4S/T1)Tell the vax
	ADJSP P,-4			;Reset the stack
	JUMPLE T1,ENINN			;Network error, abort the link

	PUSH P,DATLNK(VARPTR)		;Data link
	PUSH P,[1]			;End of message
	PUSH P,[1]			;Message length
	MOVEI T1,[EXP 0]		;The message
	HRLI T1,(POINT 8)		;Make it into a pointer
	PUSH P,T1			;Pass as an argument
	CALL UN%WRI			;(4S/T1)Tell the vax
	ADJSP P,-4			;Reset the stack
	JUMPLE T1,ENINN			;Network error
COMCHK:	AOS T10				;Address of the next status entry
	SKIPA				;Go return
COMCH2:	ADDI T10,3			;Address of the next status entry
	RETSKP				;Return true
ENINN:	RET				;Return false, a network error occurred

;Inform vax of successful recipients

SUCRCT:	PUSH P,DATLNK(VARPTR)		;Data link number
	PUSH P,[1]			;End of message flag
	PUSH P,[4]			;Size of the message
	MOVEI T2,SUCMSG(VARPTR)		;Message address
	HRLI T2,(POINT 8)		;Make into a pointer
	PUSH P,T2			;Pass as an argument

NXTSMG:	$TRACE <At NXTSMG...>
	CALL UN%WRI			;(4S/T1)Send the message
	ADJSP P,-4			;Reset the stack
	JUMPLE T1,R			;Return now if network error occurred
	AOS T10				;Address of next status entry
	RETSKP				;And return
SUBTTL DISCON - DISCONNECT THE CONNECTION

;Close the link

DISCON:	$TRACE <At DISCON...>
	PUSH P,DATLNK(VARPTR)		;Pass the data link number
	PUSH P,[0]			;No optional data
	PUSH P,[0]			;No optional data pointer
	CALL UN%CLO			;(3S/T1)Close the link
	ADJSP P,-3			;Reset the stack
	JUMPLE T1,FORDIS		;On error, abort the link
	RET				;And return

;Fordis aborts the link due to any unrecoverable error

FORDIS:	$TRACE <At FORDIS...>
	PUSH P,DATLNK(VARPTR)		;Data link
	PUSH P,[.DCX11]			;Undefined error
	PUSH P,[0]			;No optional data
	PUSH P,[0]			;No optional data pointer
	CALL UN%ABO			;(4S/T1)Abort the link
	ADJSP P,-4			;Reset the stack
	RET				;And return

;Clsfil closes and deletes the mail file due to an unrecoverable error

CLSFIL:	$TRACE <At CLSFIL...>
	PUSH P,FILNB(VARPTR)		;File number of the mail file
	PUSH P,[1]			;Abort the file
	PUSH P,[0]			;No error string
	CALL UF%CLO			;(3S/T1)Close and delete the mail file
	ADJSP P,-3			;Reset the stack
	RET				;And return

;Clofil closes the mail file prior to the last message page being sent to mx

CLOFIL:	$TRACE <At CLOFIL...>
	PUSH P,FILNB(VARPTR)		;File number of the mail file
	PUSH P,[0]			;Do not abort the file
	PUSH P,[0]			;No error string
	CALL UF%CLO			;(3S/T1)Close the mail file
	ADJSP P,-3			;Reset the stack
	RET				;And return

;Release the space allocated to the mail file spec

RELMAI:	$TRACE <At RELMAI...>
	HRRZ T1,MAIPTR(VARPTR)		;Address of the space
	PUSH P,T1			;Pass as an argument
	CALL RELASC			;(1S/T1)Release it
	ADJSP P,-1			;Reset the stack
	RET

;This routine informs mx of any errors that occurred
;T1 contains the error code
;T2 contains the node name
SMXERR:	PUSH P,T1			;Pass the error code
	PUSH P,[0]			;No message id.
	PUSH P,T2			;Pass the optional data
	PUSH P,[0]			;No reject code
	CALL ELOG			;(4S/)Log it
	ADJSP P,-4			;Reset the stack pointer
	RET				;Return +1 always

RSKP:	AOS 0(P)
R:	RET

	END