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>