TITLE SNDMSG - Routines to send local and network TTY messages SUBTTL David Eppstein/DE/KSL/MRC/WHP4 SEARCH MONSYM,MACSYM,SNDDEF EXTERN $WAKE EXTERN $GTLCL,$RMREL ASUPPRESS SALL IFNDEF OT%822,OT%822==:1 ; Definitions and Storage A=:1 ;Temporary AC's for JSYS use etc B=:2 C=:3 D=:4 ADR=10 ;Pointer to current address block ABLOCK=11 ;Pointer to argument block FP=:15 ;TRVAR frame pointer CX=:16 ;Scratch for MACREL P=:17 ;Main stack pointer BUFLEN==MAXCHR/5 ;Length of command buffers (for long msgs) PDLEN==100 ;Length of pushdown stack ; Macros for returning error strings and codes DEFINE RETSTR (ERTEXT,ERCODE,OP) < OP [ HRROI A,[ASCIZ\ERTEXT\] MOVX C,ERCODE RET ] > ; Storage allocation .PSECT DATPAG ;Paged storage BUFFER: BLOCK 1000 ;Random text storage etc (must be whole page) BUFPAG==BUFFER/1000 ;The associated page number .ENDPS .PSECT CODE ;Rest of this file is pure code ; $SEND - Send a terminal message to multiple recipients ; Call with A/pointer to ASCIZ message text ; B/list of recipients to send to ; C/argument block ; Returns +1/Failed, A/error string ; B/points to failed recipient ; C/error code (TTXxxx defined in SNDDEF) ; +2/Success, A, B, and C changed ; ; Format of the recipient list: ; Each recipient block is in the form of a header word possibly ; followed by some data words. The format of the header word is ; RC%TYP,,RC%NXT where RC%TYP is a code for the type of recipient ; coded for by this block, and RC%NXT is the next recipient. ; End the list with a 0 RC%NXT field. Values for RC%TYP are: ; RC.TTY - Recipient is a local terminal. ; Data is the terminal number (without .TTDES) ; RC.USR - Recipient is a local user - data is user number. ; RC.ALL - Send to all local users (no data). ; RC.NET - Send to a net user. Data are two word-aligned ; ASCIZ strings for the user and host names. ; ; Example: to send to TTY4, FMF, and CSD.KDO@SCORE one might have: ; RBLOCK: RC.TTY,,RBLK0 ; 4 ;TTY 4 (not 400004) ; RBLK0: RC.USR,,RBLK1 ; 500000,,137 ;FMF's user number (use RCUSR%) ; RBLK1: RC.NET,,0 ; ASCIZ/CSD.KDO/ ;Net user name (CSD.KDO) ; ASCIZ/SCORE/ ;Net host name (SCORE) ; ; ; Format of the argument block: ; Word 0 (.SDPID): PID for IPCF messages. Set to zero to make $SEND ; create a new PID - it will be filled in if created. ; Word 1 (.SDFLG): Flags for the IPCF send server. Defined flags: ; 1B0 (T%USER) - "User program features" like typing the status ; of a send to user with multiple ttys ; 1B1 (T%RAFT) - Obey REFUSE SYS after this message ; 1B2 (T%HDR) - Don't supply a message header (needs WHEEL) ; 1B3 (T%RSYS) - Obey REFUSE SYS always $SEND:: SAVEAC ;Don't mung anything for caller TRVAR ;Make some pseudo-globals SETZM QUEJFN ;So nobody thinks random crud is a JFN MKPTR (A) ;Turn into a byte pointer MOVEM A,MSPTR ;Get message text pointer MOVE ADR,B ;Get address block MOVE ABLOCK,C ;Get argument block DO. CALL SNDONE ;Send one message IFSKP. MOVE B,.SDFLG(ABLOCK) ;Done, get SNDSRV flags TXZN B,T%RAFT ;Obey REFUSE SYS after that message IFSKP. TXON B,T%RSYS ;Yes, set flag for that MOVEM B,.SDFLG(ABLOCK) ;Save changed flag word ENDIF. LOAD ADR,RC%NXT,(ADR) ;Done, get next JUMPN ADR,TOP. ;And loop back with it ELSE. CALL MQUEUE ;Failed, queue net sends MOVE B,ADR ;Get address block back RET ;And return failure ENDIF. ENDDO. CALL MQUEUE ;Done, finish queueing net sends MOVE A,MSPTR ;Get message text pointer again MOVE B,ADR ;Get final value in ADR (should be 0) MOVE C,ABLOCK ;Get address block address RETSKP ;Return success with them ; Finish queueing net sends MQUEUE: SAVEAC ;Don't mung error code or whatever SKIPN QUEJFN ;Are we queueing anything? RET ;No, done already ;; Finish creating the queued mail file HRROI A,BUFFER ;Get buffer back FMSG < Date: > ;Yes, start text SETO B, MOVX C,OT%DAY!OT%SPA!OT%TMZ!OT%SCL!OT%822 ; RFC 822 standard date/time ODTIM% FMSG < From: > CALL .DIRST ;Put in my username FMSG <@> PUSH P,A CALL $GTLCL ;Local host name IFNSK. POP P,A RETSTR ,TTXNET ENDIF. POP P,A CALL $RMREL MOVE A,QUEJFN HRROI B,BUFFER ;Get buffer back SETZ C, ;Until null SOUT% ;Start making file FMSG < > MOVE B,MSPTR ;Get pointer to message text SETZ C, ;Ending on null SOUT% ;Send it off CLOSF% ;Close the file NOP CALLRET $WAKE ;Wake up MMailr and return ; SNDONE - Send one message ; returns +1/failure, A and C set up; +2/success SNDONE: LOAD A,RC%TYP,(ADR) ;Get send type CAIG A,RC.NET ;In range? IFSKP. HRROI A,[ASCIZ/Unknown function code/] MOVEI C,TTXUNK ;No, get error string and code RET ;Return failure ENDIF. CAIN A,RC.NET ;If it's a net send JRST SNDNET ;Then send that way JRST SNDLOC ;Else it's a local send ; Here to make error string for JSYS error and return JSRET: HRROI A,BUFFER+200 ;Use a likely buffer HRLOI B,.FHSLF ;With ourself MOVEI C,40*5-1 ;Number of characters available ERSTR% ;Copy error string NOP NOP HRROI A,BUFFER+200 ;Now point to the string we made MOVX C,TTXNET ;Random network error (not called from SNDLOC) RET ; Sending a Local Message SNDLOC: LOAD A,RC%TYP,(ADR) ;Fetch type of field we parsed MOVE A,[ SIXBIT/SNDLIN/ SIXBIT/SNDUSR/ SIXBIT/SNDALL/ ](A) MOVEM A,BUFFER+SN$HDR ;Set up appropriate function code MOVE A,1(ADR) ;Retrieve word of data (garbage for SNDALL) MOVEM A,BUFFER+SN$DAT ;Set up data MOVE A,.SDFLG(ABLOCK) ;Get flags for IPCF MOVEM A,BUFFER+SN$FLG ;Set them HRROI A,BUFFER+SN$MSG ;Into the appropriate place in the IPCF page MOVE B,MSPTR ;From message pointer given to us CALL MOVSTR ;Copy the message MOVE D,.SDPID(ABLOCK) ;With PID we were given CALL DOIPCF ;Go send it off IFNSK. MOVEM D,.SDPID(ABLOCK) ;Failed, copy PID back anyway RET ;Give fail return ENDIF. MOVEM D,.SDPID(ABLOCK) ;Succeeded, copy PID back SKIPN BUFFER+SN$HDR ;Header word is -1 on errors IFSKP. HRROI A,BUFFER+SN$STR ;Point to error string MOVE C,BUFFER+SN$ERR ;And get error code returned RET ;Return failure ENDIF. MOVE A,BUFFER+SN$TTY ;Fetch number of terminals sent to CAIG A,1 ;One or none? RETSKP ;Yes, done (none?? should be error) MOVE A,.SDFLG(ABLOCK) ;Get argument flags TXNN A,T%USER ;We running a user program? RETSKP ;No, be quiet ;; Multiple jobs with T%USER set - give status of each LOAD A,RC%TYP,(ADR) ;Get address type CAIE A,RC.ALL ;Is it to *? IFSKP. ;Yes, "* has multiple jobs" is ugly, so: TMSG ELSE. MOVX A,.PRIOU ;To the terminal MOVE B,ADR ;With current address CALL $WTRCP ;Write recipient name TMSG < has multiple jobs: > ;Start message ENDIF. MOVN D,BUFFER+SN$TTY ;Fetch and negate no. of terminals pawed over HRLZS D ;Swap and clear DO. TMSG < > ;Space over for pretty MOVEI A,.PRIOU ;A to the terminal HRRZ B,BUFFER+SN$TTY+1(D) ADDI B,.TTDES ;Make TTY number into a device designator DEVST% ;Write device name, e.g. TTY6 ERJMP .+1 TMSG <: -- > ;Add a colon and separating dashes HLRZ B,BUFFER+SN$TTY+1(D) ;Fetch status flag for that line HRRO A,[ [ASCIZ/refused/] [ASCIZ/ok/] [ASCIZ/timed out/] [ASCIZ/refused/] ]+1(B) PSOUT% ;Print out the status TMSG < > ;End with a CRLF AOBJN D,TOP. ;Loop over the list ENDDO. RETSKP ; DOIPCF - send an IPCF page to the send server and await the response ; call with BUFFER/message, D/PID of this process (zero if none) ; returns +1/failure, +2/success, reply page in BUFFER PACLEN==7 ;Length of MSEND/MRECV packet DOIPCF: STKVAR ,> MOVX A,IP%CPD ;Get create pid flag into place SKIPE D ;Do we already have a pid? SETZ A, ;Yes, no special flags needed MOVEM A,.IPCFL+PACKET ;Set up flag word MOVEM D,.IPCFS+PACKET ;We are the sender SETZM .IPCFR+PACKET ;INFO is the receiver MOVEI A,SNDARG HRLI A,4 MOVEM A,.IPCFP+PACKET ;Set up pointer to argument block MOVX A,.IPCIW MOVEM A,.IPCI0+SNDARG ;Get pid for this name SETZM .IPCI1+SNDARG ;No duplicate DMOVE A,[ASCIZ/SNDSRV/] ;Point to string for SNDSRV's PID name DMOVEM A,.IPCI2+SNDARG ;Stash the id MOVEI A,PACLEN MOVEI B,PACKET MSEND% ;Ask info for server PID, maybe create our PID RETSTR ,TTXIPC MOVE D,.IPCFS+PACKET ;Fetch our PID in case it was created MOVEM D,.IPCFR+PACKET ;We are receiver this time SETZM .IPCFL+PACKET ;Sender is INFO MOVEI A,PACLEN MOVEI B,PACKET MRECV% ;Receive reply from INFO RETSTR ,TTXIPC LDB A,[POINT 6,.IPCFL+PACKET,29] ;Get INFO error code field IFN. A CAIN A,76 ;Couldn't find it? RETSTR ,TTXIPC RETSTR ,TTXIPC ENDIF. MOVE A,.IPCI1+SNDARG MOVEM A,SRVPID ;Store server's PID MOVX A,IP%CFV MOVEM A,.IPCFL+PACKET ;Sending a page of data MOVEM D,.IPCFS+PACKET ;We are the sender MOVE A,SRVPID MOVEM A,.IPCFR+PACKET ;The server is the receiver MOVEI A,BUFPAG ;From the data page HRLI A,1000 ;A whole page full MOVEM A,.IPCFP+PACKET MOVEI A,PACLEN MOVEI B,PACKET MSEND% ;Send off the request RETSTR ,TTXIPC MOVX A,IP%CFV MOVEM A,.IPCFL+PACKET ;Receiving a page of data MOVE A,SRVPID MOVEM A,.IPCFS+PACKET ;From the server MOVEM D,.IPCFR+PACKET ;To our own PID MOVEI A,BUFPAG ;Back to the same data page HRLI A,1000 MOVEM A,.IPCFP+PACKET MOVEI A,PACLEN MOVEI B,PACKET MRECV% ;Receive a reply RETSTR ,TTXIPC RETSKP ; SNDNET - Send a Network Message ; returns +1/failure, A and C set for return; +2/success SNDNET: SKIPE A,QUEJFN ;Do we have a queued mail file yet? JRST NETRCP ;Yes, just add recipient HRROI A,BUFFER ;Into a free place HRROI B,[ASCIZ/MAILQ:[--QUEUED-MAIL--].NEW-/] SETZ C, SOUT% ;Start making name PUSH P,A ;Create frame to save string pointer GTAD% ;Now output date/time MOVE B,A MOVE A,(P) MOVEI C,^D8 ;Output in octal NOUT% RETSTR ,TTXNET FMSG <-SNDMSG-J> MOVEM A,(P) ;Update saved string pointer GJINF% ;Get my job number POP P,A ;Get string pointer back MOVE B,C ;Get job number in B MOVEI C,^D10 ;Output in octal NOUT% RETSTR ,TTXNET FMSG <.-1;P770000> ;Next generation, set protection MOVX A,GJ%SHT!GJ%FOU ;File for output HRROI B,BUFFER ;With nice file name GTJFN% ;Get handle on queue file RETSTR ,TTXNET MOVX B,FLD(7,OF%BSZ)!OF%WR ;Writing OPENF% ;Open it up RETSTR ,TTXNET MOVEM A,QUEJFN ;Save for later HRROI A,BUFFER ;Into buffer space FMSG < =DELIVERY-OPTIONS:SEND _> ;This is a send, from someone CALL $GTLCL ;Get local host name RETSTR ,TTXNET FMSG < > ;CRLF (still in buffer) CALL .DIRST ;And our user name FMSG < > ;Another CRLF MOVE A,QUEJFN ;Get file again HRROI B,BUFFER ;Get buffer back SETZ C, ;Until null SOUT% ;Start making file ; Here with A/QUEJFN, add one recipient to the list in the file NETRCP: MOVEI B,"L"-100 ;Control L BOUT% ;To start another host/recip pair HRROI A,BUFFER ;Get a place to buffer user name HRROI B,1(ADR) ;With pointer from address block CALL MOVSTR ;Copy string MOVE A,QUEJFN ;Get JFN back again HRROI B,1(B) ;With next string in block SOUT% ;Send host name first FMSG < > ;Then a CRLF HRROI B,BUFFER ;Then point to user name SETZ C, ;To null SOUT% ;Add user name FMSG < > ;Another CRLF to tie it off RETSKP ;Done for now with the net send ; $SSTAT - Send a request for statistics to the send server ; call with D/PID, returns +1/always, D/updated PID $SSTAT::SAVEAC ;Don't mung caller's registers MOVE A,['SNDSTA'] ;SIXBIT function code MOVEM A,BUFFER+SN$HDR ;Set it in IPCF page SETZM BUFFER+SN$FLG ;No format flags CALL DOIPCF ;Send it off NOP ;Ignore failure RET ; $WTRCP - Make string for recipient block ; Call with A/Destination pointer (string or JFN) ; B/Recipient block (as for $SEND) ; Returns +1/Always $WTRCP::SAVEAC ;Don't mung caller's registers MKPTR (A) ;Make sure we have a real byte pointer MOVE ADR,B ;Copy address block pointer LOAD B,RC%TYP,(ADR) ;Find out what kind of send this is JRST @[ WRTTTY ;RC.TTY - to line number WRTUSR ;RC.USR - to user number WRTALL ;RC.ALL - to all WRTNET ](B) ;RC.NET - network send WRTTTY: MOVE B,1(ADR) ;Get send data MOVEI B,.TTDES(B) ;Turn into device designator DEVST% ;Type it IFNJE. FMSG RET WRTUSR: MOVE B,1(ADR) ;Get send data DIRST% ;Type user name IFNJE. FMSG RET WRTALL: FMSG <*> ;To everyone, just type a star RET WRTNET: HRROI B,1(ADR) ;Point to user SETZ C, ;No limit SOUT% ;Add it HRROI C,1(B) ;Save it MOVEI B,"@" ;Atsign BOUT% ;Add that too MOVE B,C ;Get pointer back SETZ C, ;Ending on null SOUT% ;Copy host name RET ; Random string copying utilities ;MOVSTR - move ASCIZ string from source in B to dest in A, including the null MOVSTR: MKPTR (A) MKPTR (B) DO. ILDB C,B IDPB C,A JUMPN C,TOP. ENDDO. RET ; CPYSTR - copy an asciz string from source to destination without the null CPYSTR: MKPTR (A) MKPTR (B) DO. ILDB C,B JUMPE C,R IDPB C,A LOOP. ENDDO. ; .DIRST - Copy our user name or string describing n-l-i into pointer in A .DIRST: SAVEAC ;Don't mung other registers PUSH P,A ;Save pointer GJINF% ;Get our user number HLRZ B,A ;Get left half only CAIE B,500000 ;Look like a user number? IFSKP. MOVE B,A ;Save user number POP P,A ;Get pointer back DIRST% ;Type it out IFSKP. ;If succeeded, we're all done HRROI B,[ASCIZ/Unknown user, /] ;Else start making string ELSE. POP P,A ;Get dest back HRROI B,[ASCIZ/Not logged in, /] ENDIF. CALL CPYSTR ;Start string IFL. D ;If detached (how'd a n-l-i det run send???) HRROI B,[ASCIZ/detached/] JRST CPYSTR ;Say so ENDIF. MOVEI B,.TTDES(D) ;Else get as terminal designator DEVST% ;Write "TTYn" ERJMP .+1 ;Ignore errors RET ;All done END