TITLE REPLY - Reply to last terminal message
	SUBTTL Frank M. Fujimoto

	SEARCH MONSYM,MACSYM
	.REQUIRE SYS:MACREL
	.TEXT "REPLY/SAVE"	;Save as REPLY.EXE
	ASUPPRESS

	VMAJOR==6
	VMINOR==1
	VEDIT==^D12
	VWHO==4

	COMMENT \

REPLY is a program designed for use with the Stanford Tops-20 SEND
protocol.  It looks at the user's SENDS.TXT in the login directory,
and from that determines the last sender of a terminal message.
From that information it will set up JCL and run SEND.

If the last message was from a local sender, REPLY will attempt to
send to the terminal number of the sender.  If that terminal is no
longer in use by the same user, REPLY will send to the username.

	\

SUBTTL Definitions

A=:1				;Scratch ACs
B=:2
C=:3
D=:4
PT=:5				;Byte pointer
LP=:6				;Loop for finding last sender
DBYTE=:14			;To do 'dldb'
FP=:15				;Frame pointer for TRVAR
CX=:16				;Scratch for MACREL
P=:17				;Stack pointer

	SNDPAG==10		;Map SENDS.TXT to this page
SNDADR==SNDPAG*1000

	PDLEN==100		;Length of push down stack
	BUFLEN==50		;Length of directory string buffer
	JCLLEN==300		;Most that can be put in JCL
	TRASHL==100		;Length of trash buffer
	INBUFL==240		;Length of input buffer
	OVRBFL==10		;Modest overflow buffer



SUBTTL Impure storage

PDL:	BLOCK PDLEN		;Push down stack
BUFFER:	BLOCK BUFLEN		;To hold directory string
RECPT:	BLOCK BUFLEN		;Recipient string (TTY number usually)
RSITE:	BLOCK BUFLEN		;And the site it should go back to
RNAME:	BLOCK BUFLEN		;Pretty name or zero for net sends
SNDJCL:	BLOCK JCLLEN		;To be sent to SEND.EXE
TRASH:	BLOCK TRASHL		;To hold trash
INBUF:	BLOCK INBUFL		;Input buffer length
OVRBUF:	BLOCK OVRBFL		;Overflow buffer
GTJBLK:	BLOCK .JIMAX		;For GETJI
JCLPTR:	BLOCK 1			;Point to JCL for SEND.EXE
JFN:	BLOCK 1			;Hold JFN for SENDS.TXT
BYTCNT:	BLOCK 1			;Number of bytes in file
PAGCNT:	BLOCK 1			;Number of pages in file
WHOSND:	BLOCK 1			;Who sent to us?
SNGLLN:	BLOCK 1			;Non-zero if text of reply on command line

; Command parsing stuff

DEFINE PROMPT (AC,LOC) <
    IFB <LOC>,<
	HRROI CX,AC
	MOVEM CX,CMDBLK+.CMRTY
    >
    IFNB <LOC>,<
	HRROI AC,LOC
	MOVEM AC,CMDBLK+.CMRTY
    >
>

PRMBUF:	BLOCK BUFLEN		;Buffer for prompts
CMDBUF:	BLOCK BUFLEN		;Buffer for command input
ATMBUF:	BLOCK BUFLEN		;Atom buffer

CMDBLK:	REPARS			;Command state block
	.PRIIN,,.PRIOU		;To and from terminal
	0			;Prompt, filled in later
	-1,,CMDBUF		;Start of text buffer
	-1,,CMDBUF		;Next input to be parsed
	BUFLEN*5-1		;Space left in buffer
	0			;Unparsed chars in buffer
	-1,,ATMBUF		;Start of atom buffer
	BUFLEN*5-1		;Space in atom buffer
	0			;GTJFN argument pointer

SUBTTL Macros

DEFINE EMSG (STRING) <		;;Like TMSG but does an ESOUT% instead
	HRROI A,[ASCIZ\String\]
	ESOUT%
>

DEFINE UPCASE (AC) <		;;Uppercase a letter
	CAIL AC,"a"
	 CAILE AC,"z"
	  CAIA
	   SUBI AC,"a"-"A"
>

DEFINE MATCH (AC,STRING) <	;;See if what AC points to matches string
				;;Will leave updated pointer to AC in A
				;;Returns +1 if no match, +2 if match
	CALL [	SAVEAC <B,C,D>
		MOVE A,AC	;;Get the ac here
		MOVE B,[POINT 7,[ASCIZ\STRING\]]
		JRST DOMATC ]	;;Go call handler routine
>
	      
DOMATC:	ILDB C,B		;Get a byte from the literal
	UPCASE C		;Make sure it's upper case
	JUMPE C,RSKP		;If it's a null, then we win, return +2
	ILDB D,A		;Get a byte from SENDS.TXT
	UPCASE D		;Make sure it's upper case
	CAMN C,D		;Do the two match?
	 JRST DOMATC		;Yes, get next pair of characters otherwise
	RET			;No match, return +1

SUBTTL Main program

EVEC:	JRST START
	JRST START
	BYTE (3)VWHO (9)VMAJOR (6)VMINOR (18)VEDIT
EVECL==.-EVEC

START:	RESET%			;Initialize things
	MOVE P,[IOWD PDLEN,PDL]	;Set up stack pointer
	CALL GETJCL		;Get arguments from command line
	CALL FILE		;Open up SENDS.TXT
	CALL GETSND		;Get pointer to start of last send
	CALL GETINF		;Find out who to send it to, tell user
	CALL CHKLOG		;Check if user is still there
	CALL PUTJCL		;Put JCL so sender program can see it
	CALL UNMAP		;Get rid of map to SENDS.TXT

	MOVX A,GJ%SHT!GJ%OLD	;Short form, old file
	HRROI B,[ASCIZ\SYS:SEND.EXE\]
	GTJFN%			;For SEND.EXE
	 ERCAL ERROR
	HRLI A,.FHSLF		;For this process
	MOVE D,A		;Save it
	SETO A,			;Unmapping
	MOVX B,<.FHSLF,,0>	;From the top
	MOVX C,PM%CNT!777	;To the bottom
	DMOVE 5,[  PMAP%	; 5  Unmap
		   MOVE A,D ]	; 6  Get back GET% argument
	DMOVE 7,[  GET%		; 7  Map file into ourself
		   MOVEI A,.FHSLF ] ; This process
	DMOVE 11,[ SETZ B,	; 11 Main entry point
		   SFRKV% ]	; 12 Start ourself
	JRST 5			;Do it in ACs

SUBTTL GETJCL - Process input line

GETJCL:	MOVEI A,.RSINI		;Initialize RSCAN buffer
	RSCAN%			;Do it
	 ERCAL ERROR
	JUMPE A,JCLSND		;None, this is a multi-line reply

	;; Found out how many chars, see if they fit and read all that will
	MOVNM A,C		;Save this in the register SIN% wants it in
	SUBI A,INBUFL*5-1	;Sub maximum chars we can have + 1
	SKIPLE D,A		;Put extras in a safe place.  Do they fit?
	 MOVNI C,INBUFL*5-2	;No, only fill up buffer
	MOVEI A,.PRIIN		;Want to get from tty input
	HRROI B,INBUF		;-1,,input buffer
	SIN%			;Input the characters

	;; Now handle overflow if we had it
	IFGE. D			;If chars left is negative, it's a safe size
	  MOVEI C,.CHLFD	;Get a linefeed
	  IDPB C,B		;Terminate input buffer
	  MOVEI C,.CHNUL	;And a null
	  IDPB C,B		;To tie it off
	  TMSG <%Message too long, truncating "> ;Start error message
	  DO.
	    PBIN%		;Read a char
	    PBOUT%		;And send it out
	    SOJGE D,TOP.	;Until done
	  ENDDO.
	  TMSG <"
    >				;End truncating message
	ENDIF.

	;; JCL all read, now parse it a little
	MOVE B,[POINT 7,INBUF]	;Point to what was typed in
	DO.
	  ILDB A,B		;Get a byte
	  CAIE A,.CHCRT		;Carriage return?
	   CAIN A,.CHLFD	;or linefeed?
	    JRST JCLSND		;One or the other
	  CAIE A," "		;If a space, then use 'to'
	   LOOP.		;If neither, get more junk
	ENDDO.
	MOVEM B,JCLPTR		;Save pointer for later
	SETOM SNGLLN		;This is a single line, so remember that
	RET

; Here when we are doing a multi-line send
JCLSND:	SETZM SNGLLN		;Remember not doing it
	HRROI A,[ASCIZ/
/]	;Point to a bare linefeed
	MOVEM A,JCLPTR		;Save as new JCL
	RET

SUBTTL FILE - Open up SENDS.TXT to get last send

FILE:	HRROI B,[ASCIZ/SENDS.TXT.0/] ;Want SENDS.TXT
	CALL FILNAM		;Make file name
	MOVX A,GJ%SHT!GJ%OLD	;Short form, old file
	GTJFN%			;Get a handle
	IFJER.
	  EMSG <No last send>
	  JRST STOP
	ENDIF.
	HRRZM A,JFN		;Save the jfn
	MOVE B,[FLD(7,OF%BSZ)!OF%RD] ;Text, reading
	OPENF%			;Open it
	 ERCAL ERROR
	SIZEF%			;Find out how big it is
	 ERCAL ERROR
	IFE. B
	  EMSG <No last send>
	  JRST STOP
	ENDIF.
	MOVEM B,BYTCNT		;Save byte count
	HRLZ A,JFN		;From file, first page
	MOVE B,[.FHSLF,,SNDPAG]	;Where to map it
	MOVEM C,PAGCNT		;Store for unmapping
	HRLI C,(PM%CNT!PM%RD)	;Multiple page PMAP, read access
	PMAP%			;Map file in
	RET

; Here to construct file name in home directory
FILNAM:	PUSH P,B		;Save main filename part
	SETO A,			;this job
	HRROI B,D		;one word, to D
	MOVEI C,.JILNO		;get job's logged in directory number
	GETJI%			;go look it up
	 ERCAL ERROR		;fatal error, can't recover
	MOVE B,D		;get dir no to B
	HRROI A,BUFFER		;Pointer to directory name buffer
	DIRST%			;Make a directory string
	 ERCAL ERROR
	POP P,B			;Get filename part back
	SETZ C,			;End on null
	SOUT%			;Append filespec
	IDPB C,A		;Make sure null at end
	HRROI B,BUFFER		;File spec
	RET			;Return with it


SUBTTL GETSND - Make pointer to start of send in PT

GETSND:	MOVE A,[POINT 7,SNDADR]	;Pointer to file
	MOVE PT,BYTCNT		;Get how many bytes in file
	ADJBP PT,A		;Point to end of file, pointer now in PT
	SOS LP,BYTCNT		;Get number of bytes - 1 into LP
	DO.
	  SETO DBYTE,		;decrement byte pointer
	  ADJBP DBYTE,PT
	  MOVE PT,DBYTE		;Get updated pointer into 'pointr'
	  LDB B,PT		;Get the byte
	  SOS LP		;Subtract one from count of chars seen
	  CAIN B,.CHESC		;Escape (delimiter of sends in file)?
	  IFSKP.
	    JUMPG LP,TOP.	;No, but still more chars, go ahead and loop
	    JRST BADFIL		;Else complain
	  ENDIF.
	  ILDB A,DBYTE		;Next char
	  CAIE A,.CHCRT		;Carriage return?
	   LOOP.
	  ILDB A,DBYTE		;Finally, need
	  CAIE A,.CHLFD		;A linefeed
	   LOOP.
	ENDDO.
	MOVE PT,DBYTE		;Save this as our real pointer, then
	RET			;All done

SUBTTL GETINF - Decide who to reply to 

GETINF:	SETZM RECPT		;No real recipient as yet
	SETZM RNAME		;Or pretty name
	SETZM RSITE		;Or site name
	MOVE A,[POINT 7,RNAME]	;Pointer to pretty name
	MOVE B,PT		;And start of message
	DO.
	  CALL CPYDLM		;Copy until delimiter
	  MOVEM A,PT		;Save pointer for later
	  CAIN C,","		;Comma?
	   JRST GCOMMA		;Yes, go process
	  CAIE C,"@"		;At-sign?
	  IFSKP.
	    MOVE A,B		;Yes, get pointer in different place
	    EXIT.
	  ENDIF.
	  CAIE C," "		;Space?
	   JRST BADFIL		;Confusing file format, give up
	  MATCH B,<at >		;Start of host?
	  IFNSK.
	    MOVE A,PT		;No, get pointer back
	    IDPB C,A		;Drop space in
	    LOOP.
	  ENDIF.
	ENDDO.
	SETZ C,			;Get a null
	IDPB C,PT		;And drop it in to RNAME
	PUSH P,A		;Save pointer to site name
	HRROI A,RECPT		;Get recipient pointer
	HRROI B,RNAME		;And name we have
	SOUT%			;Copy across
	SETZM RNAME		;Forget we had a pretty name
SSITE:	POP P,B			;Get site name pointer back
	MOVE A,[POINT 7,RSITE]	;And where it belongs
	CALL CPYDLM		;Copy until delimiter (don't care which)
	SETZ C,			;Get a null
	IDPB C,A		;Drop it in
	RET

GCOMMA:	MATCH B,< tty>		;Terminal number coming up next?
	IFNSK.
	  SETZ C,		;Nope, that must be all
	  IDPB C,PT		;Drop in a null
	  RET
	ENDIF.
	MOVE B,A		;Get source in its proper place
	MOVE A,[POINT 7,RECPT]	;Get place to put tty number
	DO.
	  ILDB C,B		;Get next byte
	  CAIL C,"0"		;Is it too small
	   CAILE C,"7"		;Or too large to be an octal digit?
	    EXIT.		;Yes, done
	  IDPB C,A		;Else drop it in
	  LOOP.
	ENDDO.
	SETZ D,			;Get a null
	IDPB D,A		;And finish it off
	CAIN C,","		;Comma?
	 RET
	CAIE C,"@"		;Is it an at-sign?
	IFSKP.
	  PUSH P,B		;Yes, save pointer
	  JRST SSITE
	ENDIF.
	CAIE C," "		;Is it a space?
	 RET			;No, must be finished
	MATCH B,<at >		;Site name following?
	 RET			;No, all done
	PUSH P,A		;Save pointer to site name
	JRST SSITE		;And go copy site name

; CPYDLM - Copy from B to A until a delimiter (returned in C, not added to A)

CPYDLM:	ILDB C,B		;Get next char
	JUMPE C,R		;Stop for null
	CAIE C," "		;Space
	 CAIN C,","		;or comma?
	  RET
	CAIE C,.CHCRT		;Carriage return
	 CAIN C,.CHLFD		;or linefeed?
	  RET
	CAIE C,.CHTAB		;Tab
	 CAIN C,"@"		;or at-sign?
	  RET
	IDPB C,A		;None of the above, drop char in
	JRST CPYDLM		;Loop back for the next

SUBTTL CHKLOG - Make sure user is still logged in to given TTY

CHKLOG:	SKIPE RSITE		;Net send?
	 RET			;Yes, no way to check it
	STKVAR <USRNUM>
	SETZM USRNUM		;No user num as yet
	MATCH <[POINT 7,RNAME]>,<not logged in>
	IFNSK.
	  MOVX A,RC%EMO		;Require exact match
	  HRROI B,RNAME		;Get pretty name
	  SETZ C,		;No stepping
	  RCUSR%		;Read user name
	  TXNE A,RC%NOM		;Was it matched?
	   RET
	  MOVEM C,USRNUM	;Save user number
	ENDIF.
	HRROI A,RECPT		;Get pointer to recipient terminal number
	MOVEI C,^D8		;Radix octal
	NIN%			;Read in the number
	 ERCAL ERROR
	MOVEI A,.TTDES(B)	;Turn into terminal designator
	HRROI B,D		;One word, into D
	MOVEI C,.JIUNO		;Read in user number
	GETJI%			;Find out who there
	 ERCAL ERROR
	CAME D,USRNUM		;Was it the user number we expected?
	IFSKP.
	  JUMPN D,R		;and real user
	  JUMPGE B,R		;or someone there?
	  EMSG <TTY>		;No, start message
	  HRROI A,RECPT		;Get terminal number
	  PSOUT%		;Type it
	  TMSG < is no longer active.>
	  JRST STOP
	ENDIF.
	SKIPN USRNUM		;Did we have a user number?
	 JRST NOWUSR		;No, but there's someone there now!
	HRROI A,RNAME		;Get pretty name (all we have left now)
	PSOUT%
	TMSG < is no longer at TTY>
	HRROI A,RECPT		;Get no-good terminal
	PSOUT%			;Add that too
	TMSG <
>
	PROMPT [ASCIZ/Sending to username instead [Confirm] /]
	CALL CONFRM		;Ask user to say yes
	 JRST ABTSND
	SETZM RECPT		;No terminal number any more
	RET

NOWUSR:	TMSG <TTY>
	HRROI A,RECPT		;With given tty number
	PSOUT%			;Added to the string
	TMSG < now in use by >
	MOVEI A,.PRIOU		;To terminal
	MOVE B,D		;With user number we got back
	DIRST%			;Translate user number to string
	 ERCAL ERROR
	TMSG <
>
	PROMPT [ASCIZ/Sending there anyway [Confirm] /]
	CALL CONFRM		;Make sure we want to send there
	 JRST ABTSND
	HRROI A,RNAME		;Get pointer to pretty name
	MOVE B,D		;And user there again
	DIRST%			;Translate again
	 ERCAL ERROR
	RET

ABTSND:	EMSG <Not confirmed, send aborted>
	JRST STOP

; CONFRM - Ask for carriage-return confirmation
; returns +1/not confirmed, +2/confirmed

; If any COMND% parsing is ever needed elsewhere this should be made
; into a real parser that handles stack saving and the rest.

CONFRM:	SAVEAC <A,B,C>		;Save some registers
	MOVEI A,CMDBLK		;Get CSB
	MOVEI B,[FLDDB. .CMINI]	;Initialization
	COMND%			;Parse it
	 ERJMP R		;Lost????????
REPARS:	MOVEI B,[FLDDB. .CMCFM]	;Confirmation
	COMND%			;Parse that too
	 ERJMP R		;Lost????
	TXNE A,CM%NOP		;Not parsed?
	 RET			;Fail
	RETSKP			;Success

SUBTTL PUTJCL - Put JCL so send can see it

PUTJCL:	HRROI A,SNDJCL		;Destination send JCL
	HRROI B,[ASCIZ\SEND \]	;Add program name
	SETZ C,			;Ending on null
	SOUT%			;Do it
	SETZ C,			;All SOUTs end on nulls
	SKIPE RECPT		;Are we trying for a username?
	IFSKP.
	  HRROI B,RNAME		;Yes, get it back
	  SOUT%			;Send it off
	ELSE.
	  HRROI B,RECPT		;Get recipient
	  SOUT%			;Add to JCL
	  SKIPN RSITE		;Is there a site to send this to?
	  IFSKP.
	    MOVEI B,"@"		;Get an at-sign
	    IDPB B,A		;Drop it in
	    HRROI B,RSITE	;Now get pointer to site name
	    SOUT%		;Add it to JCL
	  ENDIF.
	ENDIF.
	MOVEI B," "		;Space for luck
	IDPB B,A		;Drop it in
	MOVE B,JCLPTR		;And pointer to rest of JCL
	SOUT%			;Finish off JCL

	;; Now that we've made JCL, make string saying who sending to.
	PROMPT A,PRMBUF		;Start making prompt or response
	FMSG <Replying to >	;Begin message
	SKIPE RECPT		;Are we trying for a username?
	IFSKP.
	  HRROI B,RNAME		;Yes, get it back
	  SOUT%			;Send it off
	ELSE.
	  SKIPN RNAME		;If we have a pretty name
	  IFSKP.
	    HRROI B,RNAME	;Get pretty name
	    SOUT%		;Add it
	    FMSG <, TTY>	;But that's not the real recipient
	  ENDIF.
	  HRROI B,RECPT		;Get recipient
	  SOUT%			;Add to line
	  SKIPN RSITE		;Is there a site to send this to?
	  IFSKP.
	    FMSG <@>
	    HRROI B,RSITE	;Now get pointer to site name
	    SOUT%		;Add it to JCL
	  ENDIF.
	ENDIF.

	;; Either print string, or use as prompt for one-liner confirmation.
	SKIPN SNGLLN		;All-on-one-line form of REPLY?
	IFSKP.			;Yes
	  FMSG < [Confirm] >	;Finish prompt
	  CALL CONFRM		;Make sure user REPLYing to the right person
	   JRST ABTSND		;Not confirmed, give up
	ELSE.
	  TMSG < >		;Start with a space
	  MOVE A,CMDBLK+.CMRTY	;Now point to string
	  PSOUT%		;Send that too
	  TMSG <
>				;Finish "Replying to ..." message
	ENDIF.

	;; All confirmed, safe to set RSCAN buffer
	HRROI A,SNDJCL		;Get jcl for SEND.EXE
	RSCAN%			;Put it in RSCAN buffer
	 ERCAL ERROR
	RET

SUBTTL UNMAP - Cleans up SENDS.TXT

UNMAP:	SETO A,			;-1
	MOVE B,[.FHSLF,,SNDPAG]	;Where to unmap from
	MOVX C,PM%CNT		;Multiple page unmap
	HRR C,PAGCNT		;Number of pages to unmap
	PMAP%			;Unmap the file
	MOVE A,JFN		;Get the JFN back
	CLOSF%			;Close the file
	 ERCAL ERROR
	RET

SUBTTL Error handler

ERROR:	EMSG <Error at >	;Start error message
	MOVEI A,.PRIOU		;To terminal
	MOVE B,(P)		;Get location after ERCAL
	SUBI B,2		;Point back to losing JSYS
	MOVEI C,^D8		;Octal
	NOUT%			;Output location
	 NOP
	TMSG < - >
	MOVEI A,.PRIOU		;To terminal again
	HRLOI B,.FHSLF		;With last error on our own fork
	SETZ C,			;No limit on how many chars to type
	ERSTR%			;print error string
	 NOP			;Undefined error number
	 NOP			;Other error

STOP:	SKIPN SNGLLN		;Single line?
	IFSKP.			;Yes, want to write SENDS.FAILED
	  SETZM SNGLLN		;But don't try again if we lose
	  HRROI B,[ASCIZ/SENDS.FAILED.-1;P770202;T/]	;Want this file
	  CALL FILNAM		;Go make it
	  MOVX A,GJ%SHT!GJ%FOU	;Short GTJFN, for output
	  GTJFN%		;Get the JFN
	   ERCAL ERROR
	  MOVX B,FLD(7,OF%BSZ)!OF%WR ;Writing, byte size 7
	  OPENF%		;Open it up
	   ERCAL ERROR
	  MOVE B,JCLPTR		;Get pointer to the one line message
	  SETZ C,		;Ending on null
	  SOUT%			;Send it off to the file
	   ERCAL ERROR
	  CLOSF%		;Close it up
	   ERCAL ERROR
	  TMSG <
%Failing message written to SENDS.FAILED
>
	ENDIF.

	HALTF%			;Stop
	EMSG <Can't continue>
	JRST STOP

BADFIL:	EMSG <Bad format for SENDS.TXT>
	JRST STOP

	END <EVECL,,EVEC>