Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_SRC_3_19910112 - mm-dom/cafard.mac
There is 1 other file named cafard.mac in the archive. Click here to see a list.
	TITLE CAFARD Program to exchange mail via TTY line
	SUBTTL Written by Mark Crispin/MRC CMIRH 19 July 1984

; Copyright (C) 1984, 1985, 1986, 1987 Mark Crispin.  All rights reserved.

; Version components

CAFWHO==0			; who last edited Cafard (0=developers)
CAFVER==6			; Cafard's release version (matches monitor's)
CAFMIN==1			; Cafard's minor version
CAFEDT==^D53			; Cafard's edit version

;  This is an interim program, supposedly to be replaced by the real,
; wonderful program, written in a high-level language, knowing the
; answer to Life, the Universe, and Everything...
;
;  I fear that by the time you read these comments, gentle reader, that
; it is years after I wrote this program, and it is running everywhere.
; Hence the name Cafard -- this program might easily become as ubiquitous
; as a cockroach!
;
;  If you don't believe this is where the name comes from, I have another
; story, but it'll cost you a beer to hear it...

	SEARCH MACSYM,MONSYM	; system definitions
	SALL			; suppress macro expansions
	.DIRECTIVE FLBLST	; sane listings for ASCIZ, etc.
	.TEXT "/NOINITIAL"	; suppress loading of JOBDAT
	.TEXT "CAFARD/SAVE"	; save as CAFARD.EXE
	.TEXT "/SYMSEG:PSECT:CODE" ; put symbol table and patch area in CODE
	.REQUIRE HSTNAM		; host name routines
	.REQUIRE WAKEUP		; MMailr wakeup
	.REQUIRE CAFPRO		; Cafard protocol routines
	.REQUIRE CAFDTR		; Cafard DTR routines
	.REQUIRE SYS:MACREL	; MACSYM support routines
IFNDEF OT%822,OT%822==:1
	EXTERN $GTLCL,$SPCNS,$RMREL
	EXTERN $WAKE
	EXTERN $PINIT,$PBIN,$PSOUT,$PEOF
	EXTERN $DTRON,$DTROF
	SUBTTL Definitions

A=1				; JSYS, temporary AC's
B=2
C=3
D=4
E=5
PC=14				; JSP pointer

IFNDEF FILPGS,FILPGS==^D10	; number of file pages to PMAP% at a time
IFNDEF PDLLEN,PDLLEN==300	; stack length
IFNDEF AUTLEN,AUTLEN==^D40	; allow for a pretty big return path
IFNDEF LINBSZ,LINBSZ==^D40	; line buffer size in words
IFNDEF LINTBL,LINTBL==^D20	; max number of lines to try
IFNDEF NOTRYS,NOTRYS==^D10	; number of tries to open before giving up
IFNDEF LOOPMX,LOOPMX==^D10	; maximum number of iterations of a loop

; EMSG string
; Outputs an error message

DEFINE EMSG (STRING) <
 HRROI A,[ASCIZ&STRING&]
 ESOUT%
>;DEFINE EMSG
	SUBTTL Storage defintions

	.PSECT DATA,1000	; enter DATA psect

DEBUGP:	BLOCK 1			; non-zero if debugging
LINJFN:	BLOCK 1			; JFN of line protocol is happening on
LINBFR:	BLOCK LINBSZ		; line buffer
LINPTR:	BLOCK 1			; pointer to current byte in line buffer
LINCTR:	BLOCK 1			; number of bytes left in line buffer
PCSAVE:	BLOCK 1			; save PC on error
PDL:	BLOCK PDLLEN		; stack
AUTHOR:	BLOCK AUTLEN		; return-path copied here

	.ENDPS

	.PSECT PAGDAT,300000	; paged data

FILPAG:	BLOCK FILPGS*1000	; PMAP% readin area for file

	.ENDPS
	SUBTTL Start of program

	.PSECT CODE,100000	; enter CODE psect

; Entry vector

EVEC:	JRST CAFARD		; start address
	JRST DRAFAC		; reenter address
IFNDEF VI%DEC,VI%DEC==:1B18
	<FLD CAFWHO,VI%WHO>!<FLD CAFVER,VI%MAJ>!<FLD CAFMIN,VI%MIN>!VI%DEC!<FLD CAFEDT,VI%EDN>
EVECL==.-EVEC

CAFARD:	JSP PC,INIT		; do initialization crud
	CALL CONECT		; establish connection
	IFSKP.
	  TMSG <Sending mail...
>
	  CALL SENDER		; send what we have over
	ANSKP.
	  TMSG <Receiving mail...
>
	  CALL RECVER		; receive what the other end has for us
	  IFNSK.
	    TMSG <Mail receive lost
>
	  ENDIF.
	  CALL $WAKE		; wake up the mailer
	ELSE.
	  TMSG <Mail transmission lost
>
	ENDIF.
	CALL CLOSER		; close the connection
	HALTF%
	JRST CAFARD

DRAFAC:	JSP PC,INIT		; do initialization crud
	CALL SERVER		; do the server
	HALTF%
	JRST DRAFAC

INIT:	RESET%			; reset all I/O
	MOVE P,[IOWD PDLLEN,PDL] ; init stack
	SETZM LINCTR		; nothing in line input buffer
	JRST (PC)		; return to caller
SERVER:	MOVX A,GJ%SHT		; get JFN on our terminal
	HRROI B,[ASCIZ/TTY:/]
	GTJFN%
	 ERCAL FATAL
	MOVEM A,LINJFN		; save line JFN
	MOVX B,<<FLD 7,OF%BSZ>!OF%WR!OF%RD> ; open read/write
	OPENF%
	 ERCAL FATAL
	RFMOD%			; get current mode
	TRZ B,TT%DAM!TT%PGM	; binary mode
	SFMOD%
	 ERCAL FATAL
	STPAR%
	 ERCAL FATAL
	HRROI B,[ASCIZ/Cockroach
/]				; send expected greeting
	SETZ C,
	CALL $SOUT
	 RET
	CALL $PINIT		; initialize protocol
	CALL RECVER		; receive what the other end has for us
	IFSKP.
	  CALL $WAKE		; wake up the mailer
	  CALL SENDER		; send what we have over
	ANSKP.
	  HRROI B,[ASCIZ/hcaorkcoC
/]				; send expected greeting
	  SETZ C,
	  CALL $SOUT
	   RET
	ENDIF.
	RET
	SUBTTL Establish connection

DLGCTX:	TRVAR <DLGJFN,DLGPTR,<LINTAB,LINTBL+1>,<LINSTR,10>,TRYCNT,LOOPTR,LOOPCN,LOSFLG>
	MOVX A,NOTRYS		; establish try count
	MOVEM A,TRYCNT
	SETOM LOOPTR		; initialize loop pointer
	SETZM LOSFLG		; "not losing" (yet)
	JRST (PC)

CONECT:	JSP PC,DLGCTX		; establish dialog context
	MOVX A,GJ%SHT!GJ%OLD	; try to find dialog file
	HRROI B,[ASCIZ/OPEN-DIALOG.TXT/]
	GTJFN%
	IFJER.
	  EMSG <Can't get dialog file - >
	  CALLRET LSTERR
	ENDIF.
	MOVEM A,DLGJFN		; save JFN we got
	MOVX B,<<FLD 7,OF%BSZ>!OF%RD> ; open 7-bit read
	OPENF%
	IFJER.
	  EMSG <Can't open dialog file - >
	  CALLRET LSTERR
	ENDIF.
	MOVSI E,-LINTBL		; make pointer to line table
	HRRI E,LINTAB
	DO.
	  MOVX C,^D8		; all lines are octal
	  NIN%			; get
	  IFJER.
	    EMSG <Bogus line number in dialog file - >
	    CALLRET LSTERR
	  ENDIF.
	  MOVEM B,(E)		; save line number we got
	  BKJFN%
	   ERCAL FATAL
	  BIN%			; check out next character
	  CAIE B,"/"		; start of speed?
	  IFSKP.
	    MOVX C,^D10		; get speed
	    NIN%
	    IFJER.
	      EMSG <Bogus speed in dialog file - >
	      CALLRET LSTERR
	    ENDIF.
	    HRLM B,(E)		; save speed we got
	  ENDIF.
	  BKJFN%		; sigh...
	   ERCAL FATAL
	  DO.
	    BIN%		; see if another line
	    CAIE B,.CHTAB	; ignore whitespace
	     CAIN B,.CHSPC
	      LOOP.
	    CAIE B,"!"		; start of comment?
	    IFSKP.
	      DO.
		BIN%		; get comment character
		CAIE B,"!"	; end of comment?
		 LOOP.		; no, continue eating comment characters
	      ENDDO.
	      LOOP.		; resume whitespace scan
	    ENDIF.
	  ENDDO.
	  CAIN B,","		; saw a comma?
	   AOBJN E,TOP.		; yes, get another line
	ENDDO.
	IFGE. E			; if ran out of room
	  EMSG <Line table too long in dialog file
>
	  RET
	ENDIF.
	CAIN B,.CHCRT		; saw expected return?
	 BIN%			; yes, get line feed
	CAIN B,.CHLFD		; saw expected line feed?
	IFSKP.
	  EMSG <Extra crud in line table in dialog file
>
	  RET
	ENDIF.
	RFPTR%			; get current file position pointer
	 ERCAL FATAL
	MOVEM B,DLGPTR
	SETOM 1(E)		; indicate end of table
	DO.
	  MOVSI E,-LINTBL	; make pointer to line table
	  HRRI E,LINTAB
	  DO.
	    SKIPGE (E)		; line to grab
	     EXIT.		; no more lines
	    DO.
	      MOVX A,<ASCII/TTY/> ; start TTY string name
	      MOVEM A,LINSTR
	      MOVE A,[POINT 7,LINSTR,20]
	      HRRZ B,(E)	; line number
	      MOVX C,^D8	; in octal
	      NOUT%
	       ERCAL FATAL
	      MOVX B,":"	; finally a device indicator
	      IDPB B,A
	      SETZ B,		; tie off with null
	      IDPB B,A
	      MOVX A,GJ%SHT	; try to get TTY
	      HRROI B,LINSTR
	      GTJFN%
	       ERJMP ENDLP.	; lost, try next line
	      MOVEM A,LINJFN	; save JFN for later
	      MOVX B,<<FLD 7,OF%BSZ>!OF%WR!OF%RD> ; open read/write
	      OPENF%		; try to open the line
	      IFJER.
		TMSG <Line >
		MOVX A,.PRIOU	; output line #
		HRRZ B,(E)
		MOVX C,^D8
		NOUT%
		 ERJMP .+1
		TMSG < can't be opened - >
		CALL LSTERR
		MOVE A,LINJFN	; foo, release the JFN
		RLJFN%
		 ERJMP .+1
		SETZM LINJFN	; no line JFN any more
		EXIT.		; try next line
	      ENDIF.
	      CFIBF%		; clear out any input
	       ERCAL FATAL
	      CFOBF%		; and any output
	       ERCAL FATAL
	      RFMOD%		; get current mode
	       ERCAL FATAL
	      TXZ B,TT%DAM!TT%PGM ; binary mode
	      SFMOD%
	       ERCAL FATAL
	      STPAR%
	       ERCAL FATAL
	      TMSG <Calling out on line >
	      MOVX A,.PRIOU	; output line #
	      HRRZ B,(E)
	      MOVX C,^D8
	      NOUT%
	       ERJMP .+1
	      TMSG <
>
	      MOVE A,LINJFN	; get JFN for line
	      CALL $DTROF	; drop DTR first
	      MOVX A,^D1000	; wait a second
	      DISMS%
	      MOVE A,LINJFN	; now assert DTR
	      CALL $DTRON
	      MOVX A,^D1000	; wait a second
	      DISMS%
	      HLRZ C,(E)	; get speed if any
	      IFN. C		; was there any?
		MOVE A,LINJFN	; yes, set the speed
		MOVX B,.MOSPD
		HLL C,(E)	; in both halfwords
		MTOPR%
		 ERCAL FATAL
	      ENDIF.
	      MOVE A,DLGJFN	; reset dialog to point to start
	      MOVE B,DLGPTR
	      SFPTR%
	       ERCAL FATAL
	      SETOM LOOPTR	; initialize loop pointer
	      DO.
		MOVE A,DLGJFN
		BIN%		; get dialog command
		IFNJE.
		  XCT DLGRTB(B)	; do dialog grammar for this character
		   LOOP.
		  SKIPN LOSFLG	; lost in dialog or grammar?
		   SETZM TRYCNT	; grammar punted, give up!
		  MOVE A,LINJFN	; either way, close the line
		  CFIBF%
		   ERJMP .+1
		  CFOBF%
		   ERJMP .+1
		  CALL $DTROF	; drop DTR...
		  CLOSF%
		   ERJMP .+1
		  SETZM LINJFN	; no line JFN any more
		ELSE.
		  SETZM TRYCNT	; end of file, success, no more retries
		ENDIF.
	      ENDDO.
	    ENDDO.
	    SKIPE TRYCNT	; do we need to retry still?
	     AOBJN E,TOP.	; yes, try next line in list
	  ENDDO.
	  SKIPN TRYCNT		; do we need to retry?
	  IFSKP.
	    SOSG TRYCNT		; yes, count off another retry
	    IFSKP.
	      MOVX A,^D60000	; wait a minute and try again
	      DISMS%
	      LOOP.		; still have a few more
	    ENDIF.
	    EMSG <Exhausted connection retries
>
	    RET
	  ENDIF.
	ENDDO.
	MOVE A,DLGJFN		; now close off the JFN
	CLOSF%
	 ERJMP .+1		; ignore failure
	SKIPN LINJFN		; still have the line?
	 RET			; no, return failure
	CALL $PINIT		; initialize protocol
	RETSKP			; return success
	SUBTTL Close connection

CLOSER:	JSP PC,DLGCTX		; establish dialog context
	MOVX A,GJ%SHT!GJ%OLD	; try to find dialog file
	HRROI B,[ASCIZ/CLOSE-DIALOG.TXT/]
	GTJFN%
	 ERJMP R		; so no close dialog file exists!
	MOVEM A,DLGJFN		; save JFN we got
	MOVX B,<<FLD 7,OF%BSZ>!OF%RD> ; open 7-bit read
	OPENF%
	IFJER.
	  EMSG <Can't open connection close dialog file - >
	  CALLRET LSTERR
	ENDIF.
	DO.
	  MOVE A,DLGJFN
	  BIN%			; get dialog command
	  IFNJE.
	    XCT DLGRTB(B)	; do dialog grammar for this character
	     LOOP.
	  ENDIF.
	ENDDO.
	MOVE A,LINJFN		; end of file, close the line JFN
	CFIBF%			; clear buffers
	 ERJMP .+1
	CFOBF%
	 ERJMP .+1
	CALL $DTROF		; drop DTR...
	CLOSF%
	 ERJMP .+1
	SETZM LINJFN		; no line JFN any more
	MOVE A,DLGJFN		; now close off the dialog JFN
	CLOSF%
	 ERJMP .+1		; ignore failure
	RET			; return
	SUBTTL Open/Close dialog grammar and execution

; Dialog grammar vector

DLGRTB:	NOP			; ^@ ignored
	CALL GBOGUS		; ^A bogus character
	CALL GBOGUS		; ^B bogus character
	CALL GBOGUS		; ^C bogus character
	CALL GBOGUS		; ^D bogus character
	CALL GBOGUS		; ^E bogus character
	CALL GBOGUS		; ^F bogus character
	CALL GBOGUS		; ^G bogus character
	CALL GBOGUS		; ^H bogus character
	NOP			; ^I whitespace ignored
	NOP			; ^J whitespace ignored
	CALL GBOGUS		; ^K bogus character
	NOP			; ^L whitespace ignored
	NOP			; ^M whitespace ignored
	CALL GBOGUS		; ^N bogus character
	CALL GBOGUS		; ^O bogus character
	CALL GBOGUS		; ^P bogus character
	CALL GBOGUS		; ^Q bogus character
	CALL GBOGUS		; ^R bogus character
	CALL GBOGUS		; ^S bogus character
	CALL GBOGUS		; ^T bogus character
	CALL GBOGUS		; ^U bogus character
	CALL GBOGUS		; ^V bogus character
	CALL GBOGUS		; ^W bogus character
	CALL GBOGUS		; ^X bogus character
	CALL GBOGUS		; ^Y bogus character
	CALL GBOGUS		; ^Z bogus character
	CALL GBOGUS		; ^[ bogus character
	CALL GBOGUS		; ^\ bogus character
	CALL GBOGUS		; ^] bogus character
	CALL GBOGUS		; ^^ bogus character
	CALL GBOGUS		; ^_ bogus character
	NOP			; SPACE whitespace ignored
	CALL GCOMNT		; ! comment toggle
	CALL GSTEXT		; " send text
	CALL GBOGUS		; # bogus character
	CALL GBOGUS		; $ bogus character
	CALL GBOGUS		; % bogus character
	CALL GBOGUS		; & bogus character
	CALL GBOGUS		; ' bogus character
	CALL GBOGUS		; ( bogus character
	CALL GBOGUS		; ) bogus character
	CALL GBOGUS		; * bogus character
	NOP			; + success clause, ignored here
	CALL GBOGUS		; , bogus character
	NOP			; - failure clause, ignored here
	CALL GBOGUS		; . bogus character
	CALL GBOGUS		; / bogus character
	CALL GBOGUS		; 0 bogus character
	CALL GBOGUS		; 1 bogus character
	CALL GBOGUS		; 2 bogus character
	CALL GBOGUS		; 3 bogus character
	CALL GBOGUS		; 4 bogus character
	CALL GBOGUS		; 5 bogus character
	CALL GBOGUS		; 6 bogus character
	CALL GBOGUS		; 7 bogus character
	CALL GBOGUS		; 8 bogus character
	CALL GBOGUS		; 9 bogus character
	CALL GBOGUS		; : bogus character
	CALL GBOGUS		; ; bogus character
	CALL GBEGIN		; < begin loop
	CALL GEQUAL		; = test for desired string
	SETOM LOOPTR		; > end of loop
	CALL GBOGUS		; ? bogus character
	CALL GBOGUS		; @ bogus character
	CALL GBOGUS		; A bogus character
	CALL GBOGUS		; B bogus character
	CALL GBOGUS		; C bogus character
	CALL GBOGUS		; D bogus character
	CALL GEAT		; E eat input
	CALL GBOGUS		; F bogus character
	CALL GBOGUS		; G bogus character
	CALL GBOGUS		; H bogus character
	CALL GBOGUS		; I bogus character
	CALL GBOGUS		; J bogus character
	CALL GBOGUS		; K bogus character
	CALL GLOSE		; L dialog lossage
	CALL GBOGUS		; M bogus character
	CALL GBOGUS		; N bogus character
	CALL GBOGUS		; O bogus character
	CALL GBOGUS		; P bogus character
	CALL GBOGUS		; Q bogus character
	CALL GBOGUS		; R bogus character
	CALL GBOGUS		; S bogus character
	CALL GBOGUS		; T bogus character
	CALL GBOGUS		; U bogus character
	CALL GBOGUS		; V bogus character
	CALL GWAIT		; W wait 250 ms
	CALL GLEXIT		; X exit loop
	CALL GBOGUS		; Y bogus character
	CALL GBOGUS		; Z bogus character
	CALL GMESAG		; [ output to terminal
	CALL GBOGUS		; \ bogus character
	CALL GBOGUS		; ] bogus character
	CALL GLOOP		; ^ go to top of loop
	CALL GBOGUS		; _ bogus character
	CALL GBOGUS		; ` bogus character
	CALL GBOGUS		; a bogus character
	CALL GBOGUS		; b bogus character
	CALL GBOGUS		; c bogus character
	CALL GBOGUS		; d bogus character
	CALL GEAT		; e eat input
	CALL GBOGUS		; f bogus character
	CALL GBOGUS		; g bogus character
	CALL GBOGUS		; h bogus character
	CALL GBOGUS		; i bogus character
	CALL GBOGUS		; j bogus character
	CALL GBOGUS		; k bogus character
	CALL GLOSE		; l dialog lossage
	CALL GBOGUS		; m bogus character
	CALL GBOGUS		; n bogus character
	CALL GBOGUS		; o bogus character
	CALL GBOGUS		; p bogus character
	CALL GBOGUS		; q bogus character
	CALL GBOGUS		; r bogus character
	CALL GBOGUS		; s bogus character
	CALL GBOGUS		; t bogus character
	CALL GBOGUS		; u bogus character
	CALL GBOGUS		; v bogus character
	CALL GWAIT		; w wait 250 ms
	CALL GLEXIT		; x exit loop
	CALL GBOGUS		; y bogus character
	CALL GBOGUS		; z bogus character
	CALL GBOGUS		; { bogus character
	CALL GBOGUS		; | bogus character
	CALL GBOGUS		; } bogus character
	NOP			; ~ end of conditional, ignored
	CALL GBOGUS		; RUBOUT bogus character
; Bogus command

GBOGUS:	EMSG <Bogus command character in dialog file: >
	MOVX A,.PRIOU
	BOUT%
	TMSG <
>
	RETSKP			; indicate we should punt

; "L" dialog lossage

GLOSE:	SETOM LOSFLG		; flag dialog lossage
	RETSKP
; "<" begin loop

GBEGIN:	SKIPGE LOOPTR		; already in a loop?
	IFSKP.
	  EMSG <Nested loops not allowed
>
	  RETSKP		; maybe remove this restriction someday
	ENDIF.
	MOVE A,DLGJFN		; note this pointer
	RFPTR%			; pointer to start of test string
	IFJER.
	  CALL FATAL		; strange lossage
	  RETSKP
	ENDIF.
	MOVEM B,LOOPTR
	MOVNI B,LOOPMX		; maximum number of times loops run
	MOVEM B,LOOPCN
	RET

; "^" resume iteration

GLOOP:	SKIPL B,LOOPTR		; already in a loop?
	IFSKP.
	  EMSG <"^" outside of loop
>
	  RETSKP		; maybe remove this restriction someday
	ENDIF.
	AOSLE LOOPCN		; ran out of iterations?
	 RET			; yes, no-op now
	MOVE A,DLGJFN		; reset point pointer
	SFPTR%
	IFJER.
	  CALL FATAL		; strange lossage
	  RETSKP
	ENDIF.
	RET
; "X" exit loop

GLEXIT:	MOVE A,DLGJFN		; end of loop search
	DO.
	  BIN%
	   ERJMP R
	  CAIN B,">"		; found end of loop?
	   EXIT.		; yes, continue from that point
	  CAIE B,""""		; quoted string?
	   CAIN B,"!"		; comment?
	  IFNSK.
	    MOVE C,B		; yes, search for end of string first
	    DO.
	      BIN%
	       ERJMP R
	      CAME B,C		; end of string yet?
	       LOOP.		; no
	    ENDDO.
	  ELSE.
	    CAIE B,"["		; terminal string?
	    IFSKP.
	      DO.
		BIN%
		 ERJMP R
		CAIE B,"]"	; end of string yet?
		 LOOP.		; no
	      ENDDO.
	    ENDIF.
	  ENDIF.
	  LOOP.			; keep searching
	ENDDO.
	RET
; "E" eat excess input

GEAT:	MOVX C,^D6
	DO.
	  CALL $SIBE		; any characters available for us?
	  IFSKP.
	    MOVX A,^D500	; not expired yet, wait 1/2 second
	    DISMS%
	    SOJGE C,TOP.	; and try again
	  ELSE.
	    CALL $BIN		; read byte from input
	     RETSKP
	    LOOP.
	  ENDIF.
	ENDDO.
	RET

; "W" wait 250 ms

GWAIT:	MOVX A,^D250		; sleep a bit
	DISMS%
	RET
; "!" comment toggle

GCOMNT:	DO.
	  MOVE A,DLGJFN		; get a comment byte
	  BIN%
	  IFNJE.
	    CAIE B,"!"		; end of comment?
	     LOOP.
	  ENDIF.
	ENDDO.
	RET

; `"' output text to the line we're doing protocol on

GSTEXT:	ACVAR <PRV>
	DO.
	  MOVE A,DLGJFN		; get a text byte to output
	  BIN%
	  IFNJE.
	    CAIN B,""""		; end of text?
	  ANSKP.
	    CAIN B,.CHLFD	; no, is this a line feed?
	     CAIE PRV,.CHCRT	; yes, was previous character CR?
	    IFNSK.
	      CALL $BOUT	; send character to line
	       RETSKP
	      MOVE PRV,B	; save as previous character
	    ENDIF.
	    LOOP.		; do next byte
	  ENDIF.
	ENDDO.
	RET

	ENDAV.

; "[" output message to our terminal

GMESAG:	DO.
	  MOVE A,DLGJFN		; get a text byte to output
	  BIN%
	  IFNJE.
	    CAIN B,"]"		; end of text?
	  ANSKP.
	    MOVX A,.PRIOU	; output to terminal
	    BOUT%
	    LOOP.
	  ENDIF.
	RET
; "=" test for desired string

GEQUAL:	STKVAR <TSTCHR,TSTBEG,TSTTMO>
	SETZ C,			; clear running timeout value
	MOVE A,DLGJFN		; get expected quote
	DO.
	  BIN%
	   ERJMP .+1		; EOF is pretty losing...
	  CAIL B,"0"		; numeric?
	   CAILE B,"9"
	    EXIT.		; not a digit!
	  IMULI C,^D10		; a digit, another decade...
	  ADDI C,-"0"(B)	; add in new digit
	  LOOP.
	ENDDO.
	SKIPN C			; was a timeout given?
	 MOVX C,1		; allow at least 1 second
	LSH C,1			; timeout ticks are in 1/2 seconds
	MOVEM C,TSTTMO		; set timeout
	CAIN B,""""		; was it what we wanted?
	IFSKP.
	  EMSG <Missing required quote
>
	  RETSKP
	ENDIF.
	RFPTR%			; pointer to start of test string
	IFJER.
	  CALL FATAL		; strange lossage
	  RETSKP
	ENDIF.
	MOVEM B,TSTBEG
	DO.
	  MOVE A,DLGJFN		; get byte to compare with
	  BIN%
	   ERJMP R
	  CAIN B,""""		; end of test string?
	  IFSKP.
	    MOVEM B,TSTCHR	; save character to test for
	    DO.
	      CALL $SIBE	; any characters available for us?
	      IFSKP.
		SOSGE TSTTMO	; no, has timeout expired?
		IFSKP.
		  MOVX A,^D500	; not expired yet, wait 1/2 second
		  DISMS%
		  LOOP.		; and try again
		ENDIF.
		SETO B,		; "time out" character
	      ELSE.
		CALL $BIN	; read byte from input
		 RETSKP
	      ENDIF.
	    ENDDO.
	    IFGE. B		; exit if timed out
	      CAMN B,TSTCHR	; was it what we expected?
	       LOOP.		; yes, try for another
	      MOVEM B,TSTCHR	; no, save what we got
	      MOVE A,DLGJFN	; reset test string and get first byte
	      MOVE C,TSTBEG
	      RIN%
	      CAMN B,TSTCHR	; matches last character we got?
	       LOOP.		; yes, continue search from here
	      BKJFN%		; oh well, back up to it again
	       ERJMP .+1
	      LOOP.		; and continue search
	    ENDIF.
	    MOVE A,DLGJFN	; search for end of string
	    DO.
	      BIN%		; get random character
	       ERJMP R
	      CAIE B,""""	; end of search string yet?
	       LOOP.		; not yet
	    ENDDO.
	    MOVX C,"-"		; must search for failure clause
	  ELSE.
	    MOVX C,"+"		; search for success clause
	  ENDIF.
	ENDDO.
	MOVE A,DLGJFN		; clause search
	DO.
	  BIN%
	   ERJMP R
	  CAIE B,"~"		; end of conditional or
	   CAMN B,C		;  found what we are looking for?
	    EXIT.		; yes, continue from that point
	  CAIE B,""""		; quoted string?
	   CAIN B,"!"		; comment?
	  IFNSK.
	    MOVE D,B		; yes, search for end of string first
	    DO.
	      BIN%
	       ERJMP R
	      CAME B,D		; end of string yet?
	       LOOP.		; no
	    ENDDO.
	  ELSE.
	    CAIE B,"["		; terminal string?
	    IFSKP.
	      DO.
		BIN%
		 ERJMP R
		CAIE B,"]"	; end of string yet?
		 LOOP.		; no
	      ENDDO.
	    ENDIF.
	  ENDIF.
	  LOOP.			; keep searching
	ENDDO.
	RET

	ENDSV.

	ENDTV.			; end of dialog conventions
	SUBTTL Send mail to remote

SENDER:	STKVAR <QUEJFN,CURPAG,<FILSIZ,2>>
	MOVX A,GJ%IFG!GJ%OLD!GJ%SHT!.GJALL ; look at all the mail queued for us
	HRROI B,[ASCIZ/-MAIL.*.*/]
	GTJFN%
	IFNJE.
	  MOVEM A,QUEJFN	; found it, save the JFN
	  DO.
	    HRRZ A,QUEJFN	; open up file
	    MOVX B,<<FLD 7,OF%BSZ>!OF%RD> ; for 7-bit read
	    OPENF%
	     ERCAL FATAL
	    MOVE B,[2,,.FBBYV]	; get file I/O and byte size info
	    MOVEI C,FILSIZ	; into FILSIZ/FILSIZ+1
	    GTFDB%		; NOTE: depends upon .FBSIZ=.FBBYV+1
	    MOVE B,1+FILSIZ	; get byte count
	    LOAD C,FB%BSZ,FILSIZ ; get file byte size
	    CAIN C,7		; 7-bit bytes?
	    IFSKP.
	      CAIE C,^D36	; allow 36-bit files
	       RET		; otherwise lose!!
	      IMULI B,5		; 5 7-bit bytes per 36-bit word
	    ENDIF.
	    MOVEM B,FILSIZ	; save file size
	    SETZM CURPAG	; start at beginning of file
	    DO.
	      MOVS A,QUEJFN	; file to map from
	      HRR A,CURPAG	; current page to map from
	      MOVX B,<.FHSLF,,<FILPAG/1000>> ; map into this process at FILPAG
	      MOVX C,PM%CNT!PM%RD!PM%PLD!FILPGS ; preload FILPGS pages for read
	      PMAP%		; map the pages
	      HRROI A,FILPAG	; 7-bit bytes
	      MOVX B,FILPGS*5*^D512 ; number of bytes in this page set
	      EXCH B,FILSIZ	; bytes to do yet in C
	      CAMG B,FILSIZ	; more bytes after this page set?
	      IFSKP.
		SUB B,FILSIZ	; yes, account for this page set in count
		EXCH B,FILSIZ	; full page set to output
	      ELSE.
		SETZM FILSIZ	; no, this is the last page set to do
	      ENDIF.
	      CALL $PSOUT	; send it out
	       RET		; failed
	      SKIPG FILSIZ	; more to do yet?
	      IFSKP.
		MOVEI A,FILPGS	; yes, bump CURPAG by number of pages just done
		ADDM A,CURPAG
		LOOP.		; do next set of pages
	      ENDIF.
	      SETO A,		; unmap file pages so we can close the file
	      MOVX B,<.FHSLF,,<FILPAG/1000>>
	      MOVX C,PM%CNT!FILPGS
	      PMAP%
	    ENDDO.
	    CALL $PEOF		; send EOF
	     RET		; hard error
	    HRRZ A,QUEJFN	; close the file
	    TXO A,CO%NRJ
	    CLOSF%
	     ERJMP .+1
	    HRRZ A,QUEJFN	; all done, flush this file
	    TXO A,DF%NRJ	; don't flush the JFN
	    DELF%
	     ERJMP .+1		; shouldn't happen, but don't barf
	    MOVE A,QUEJFN	; get next file
	    GNJFN%
	     ERJMP ENDLP.	; no more files
	    LOOP.
	  ENDDO.
	ENDIF.
	CALL $PEOF		; send EOF
	 RET			; hard error
	RETSKP			; and return

	ENDSV.
	SUBTTL Receive mail from remote and queue

RECVER:	STKVAR <<HSTBUF,^D20>,<TMPBUF,^D20>,TMPPTR,MLQJFN,SAVCHR,ATPTR,BEGPTR,AUTPTR,AUTCNT>
	HRROI A,HSTBUF		; get local name
	CALL $GTLCL
	 RET			; can't possibly happen
	DO.
	  CALL $PBIN		; get character from line
	   RET			; the link died
	  JUMPL B,ENDLP.	; if EOF, nothing more to do
	  MOVEM B,SAVCHR	; save character
	  DO.
	    HRROI A,TMPBUF	; build queued mail filename
	    HRROI B,[ASCIZ/MAILQ:[--QUEUED-MAIL--].NEW-/]
	    SETZ C,
	    SOUT%		; set up initial part of name
	     ERCAL FATAL
	    MOVEM A,TMPPTR	; save string pointer
	    GTAD%		; get system date/time
	    MOVE B,A		; now output it in octal
	    MOVE A,TMPPTR
	    MOVX C,^D8
	    NOUT%
	     ERCAL FATAL
	    HRROI B,[ASCIZ/-CAFARD-J/] ; add originating process name
	    SETZ C,
	    SOUT%
	     ERCAL FATAL
	    MOVEM A,TMPPTR
	    GJINF%
	    MOVE A,TMPPTR
	    HRRZ B,C		; insert job number for unique name
	    MOVX C,^D10		; in decimal
	    NOUT%
	     ERCAL FATAL
	    HRROI B,[ASCIZ/.-1;P770000/] ; next generation, protection 770000
	    SETZ C,
	    SOUT%
	     ERCAL FATAL
	    MOVX A,GJ%NEW!GJ%FOU!GJ%PHY!GJ%SHT ; want new file
	    HRROI B,TMPBUF
	    GTJFN%		; try to get JFN on it
	    IFJER.
	      MOVX A,^D3000	; wait three seconds and try again
	      DISMS%
	      LOOP.
	    ENDIF.
	    MOVEM A,MLQJFN	; save JFN we got
	    MOVX B,<<FLD 7,OF%BSZ>!OF%WR> ; open for write, 7-bit bytes
	    OPENF%
	    IFJER.
	      MOVE A,MLQJFN	; OPENF% failed, release the JFN
	      RLJFN%
	       ERJMP .+1
	      SETZM MLQJFN	; forget about it
	      MOVX A,^D3000	; wait 3 seconds
	      LOOP.		; and try again
	    ENDIF.
	  ENDDO.
	  MOVE B,SAVCHR		; restore first character
	  CAIE B,.CHCRT		; was there a sender argument?
	  IFSKP.
	    CALL $PBIN		; Nein, fress das LF
	    IFSKP.
	      CAIE B,.CHLFD	; Es muss hier ein LF sein!
	    ANSKP.
	    ELSE.
	      TXO A,CZ%ABT	; Scheisse!
	      CLOSF%
	       ERJMP .+1
	      RET		; Goetterdaemmerung...
	    ENDIF.
	  ELSE.
	    MOVX B,.CHFFD	; yes, output form feed first
	    BOUT%
	     ERCAL FATAL
	    HRROI B,[ASCIZ/=RETURN-PATH:/]
	    SOUT%		; output return path header
	     ERCAL FATAL
	    MOVE B,SAVCHR	; output first character of return path
	    MOVE C,[POINT 7,AUTHOR] ; init beginning of author pointer
	    MOVEM C,BEGPTR
	    IDPB B,C		; copy first character
	    MOVEM C,AUTPTR
	    SETZM ATPTR		; init pointer to atsign
	    MOVX C,<AUTLEN*5>-1	; bytes remaining in buffer
	    MOVEM C,AUTCNT
	    BOUT%
	     ERCAL FATAL
	    DO.
	      CALL $PBIN	; get a byte
	      IFSKP.
	      ANDGE. B		; must not be end of file here
		BOUT%		; output byte to file
		 ERCAL FATAL
		CAIE B,"\"	; quote next character?
		IFSKP.
		  CALL $PBIN	; get a byte
		  IFSKP.
		  ANDGE. B	; must not be end of file here
		    SOSL AUTCNT	; space left in buffer?
		     IDPB B,AUTPTR ; yes, add char to author
		    BOUT%	; output byte to file
		     ERCAL FATAL
		  ELSE.
		    TXO A,CZ%ABT ; protocol error or link died, abort this file
		    CLOSF%
		     ERJMP .+1
		    RET		; return lossage
		  ENDIF.
		ENDIF.
		CAIE B,""""	; quoted string?
		IFSKP.
		  DO.
		    CALL $PBIN	; get a byte
		    IFSKP.
		    ANDGE. B	; must not be end of file here
		      BOUT%	; output byte to file
		       ERCAL FATAL
		      CAIN B,"""" ; end of quoted string?
		       EXIT.	; yes, get out now!
		      CAIE B,"\" ; quote next character?
		      IFSKP.
			CALL $PBIN ; get a byte
			IFSKP.
			ANDGE. B ; must not be end of file here
			  SOSL AUTCNT ; space left in buffer?
			   IDPB B,AUTPTR ; yes, add char to author
			  BOUT%	; output byte to file
			   ERCAL FATAL
			ELSE.
			  TXO A,CZ%ABT ; protocol error or link died
			  CLOSF%
			   ERJMP .+1
			  RET	; return lossage
			ENDIF.
		      ENDIF.
		      SOSL AUTCNT ; space left in buffer?
		       IDPB B,AUTPTR ; yes, add char to author
		    ELSE.
		      TXO A,CZ%ABT ; protocol error or link died
		      CLOSF%
		       ERJMP .+1
		      RET	; return lossage
		    ENDIF.
		  ENDDO.
		ENDIF.
		SOSL AUTCNT	; space left in buffer?
		 IDPB B,AUTPTR	; yes, add char to author
		CAIE B,"@"	; hostname starts now?
		IFSKP.
		  MOVE C,AUTPTR	; yes, save pointer to atsign
		  MOVEM C,ATPTR
		ENDIF.
		CAIE B,":"	; encountered colon in A-D-L??
		IFSKP.
		  MOVE C,AUTPTR	; yes, all previous is routing garbage
		  MOVEM C,BEGPTR ; so start from here
		  SETZM ATPTR	; and forget any @ seen
		ENDIF.
		CAIE B,.CHLFD	; at end of line?
		 LOOP.		; do next byte
	      ELSE.
		TXO A,CZ%ABT	; protocol error or link died, abort this file
		CLOSF%
		 ERJMP .+1
		RET		; return lossage
	      ENDIF.
	    ENDDO.
	    SETZ B,		; tie off with NUL
	    IDPB B,AUTPTR
	    SKIPE ATPTR		; @ found?
	     SKIPGE AUTCNT	; yes, copy unless buffer overflowed
	    IFSKP.
	      MOVX B,.CHFFD	; specify sender
	      BOUT%
	      MOVEI B,"_"
	      BOUT%
	      MOVE B,ATPTR	; replace @ in copied string with NUL
	      SETZ C,
	      DPB C,B
	      SOUT%		; output host
	       ERCAL FATAL
	      MOVE B,BEGPTR
	      SOUT%		; output username
	       ERCAL FATAL
	      HRROI B,[ASCIZ/
/]
	      SOUT%		; and final CRLF
	       ERCAL FATAL
	    ENDIF.
	  ENDIF.
	  MOVX B,.CHFFD		; output form feed first
	  BOUT%
	   ERCAL FATAL
	  HRROI B,HSTBUF
	  SETZ C,
	  SOUT%
	   ERCAL FATAL
	  HRROI B,[ASCIZ/
/]
	  SOUT%
	   ERCAL FATAL
	  DO.			; loop to slurp up recipient list
	    CALL $PBIN		; get a byte
	    IFSKP.
	      IFGE. B
		BOUT%		; output byte to file
		 ERCAL FATAL
		CAIE B,.CHFFD	; end of recipient list?
		 LOOP.		; no, do next byte
	      ENDIF.
	    ELSE.
	      TXO A,CZ%ABT	; the link died, abort this file
	      CLOSF%
	       ERJMP .+1
	      RET		; return lossage
	    ENDIF.
	  ENDDO.
	  HRROI B,[ASCIZ/
Received: from /]		; write Received: line
	  SETZ C,
	  SOUT%
	   ERCAL FATAL
	  GJINF%
	  HRROI A,TMPBUF	; try to get foreign name
	  CALL $SPCNS
	  IFSKP.
	    HRROI A,TMPBUF	; remove relative domain
	    CALL $RMREL
	    MOVE A,MLQJFN
	    HRROI B,TMPBUF	; write foreign host
	    SETZ C,
	    SOUT%
	     ERCAL FATAL
	  ELSE.
	    MOVE A,MLQJFN	; standby just in case
	    HRROI B,[ASCIZ/???/]
	    SETZ C,
	    SOUT%
	     ERCAL FATAL
	  ENDIF.
	  HRROI B,[ASCIZ/ by /]
	  SOUT%
	   ERCAL FATAL
	  HRROI A,HSTBUF	; remove relative domain
	  CALL $RMREL
	  MOVE A,MLQJFN
	  HRROI B,HSTBUF	; write local host
	  SOUT%
	   ERCAL FATAL
	  HRROI B,[ASCIZ/ with Cafard; /]
	  SOUT%
	   ERCAL FATAL
	  SETO B,		; output current date/time
	  MOVX C,OT%DAY!OT%SPA!OT%TMZ!OT%SCL!OT%822 ; RFC 822 standard date/time
	  ODTIM%
	   ERCAL FATAL
	  DO.			; loop to slurp message
	    CALL $PBIN		; get a byte
	    IFSKP.
	      IFGE. B
		BOUT%		; output byte to file
		 ERCAL FATAL
		LOOP.		; do next byte
	      ENDIF.
	    ELSE.
	      TXO A,CZ%ABT	; the link died, abort this file
	      CLOSF%
	       ERJMP .+1
	      RET		; return lossage
	    ENDIF.
	  ENDDO.
	  CLOSF%		; close off the file
	   ERJMP .+1
	  LOOP.			; do next message
	ENDDO.
	RETSKP

	ENDSV.
	SUBTTL I/O routines

; $SIBE - Skip if bytes available from line
;	CALL $SIBE
; Returns +1: Number of bytes available in B
;	  +2: No bytes available

$SIBE::	SKIPLE B,LINCTR		; anything in line input buffer?
	IFSKP.
	  SAVEAC <A>
	  MOVE A,LINJFN		; no, do the system call
	  SIBE%
	ANSKP.
	  RETSKP
	ENDIF.
	RET

; $BIN - Get byte from line
;	CALL $BIN
; Returns +1: Hard failure
;	  +2: Success, with byte in B

$BIN::	SAVEAC <A,C>
	SOSL LINCTR		; anything in line input buffer?
	IFSKP.
	  CALL $SIBE		; any input in buffer for me?
	   SKIPA C,B		; yes, get that many bytes
	    MOVX C,1		; else just get one byte
	  CAILE C,5*LINBSZ	; bounds check
	   MOVX C,5*LINBSZ	; guess we should reassemble!
	  MOVEM C,LINCTR	; note number of bytes this time
	  MOVE A,LINJFN		; line designator
	  MOVE B,[POINT 7,LINBFR]
	  MOVEM B,LINPTR	; re-initialize pointer
	  MOVN C,LINCTR		; number of bytes
	  SIN%			; slurp up the data
	   ERJMP R
	  SOS LINCTR		; count this byte as having been et
	ENDIF.
	ILDB B,LINPTR		; read a single byte
	MOVX A,.PRIOU
	SKIPE DEBUGP
	 BOUT%
	RETSKP
; $BOUT - Send character to line
; Accepts:
;	B/ character
;	CALL $BOUT
; Returns +1: Hard failure
;	  +2: Success

$BOUT::	SAVEAC <A>
	MOVE A,LINJFN		; output string to terminal
	BOUT%
	 ERJMP R
	MOVX A,.PRIOU
	SKIPE DEBUGP
	 BOUT%
	RETSKP

; $SOUT - Send string to line
; Accepts:
;	B/ string to output
;	C/ size of string to output
;	CALL $SOUT
; Returns +1: Hard failure
;	  +2: Success

$SOUT::	SAVEAC <A>
	STKVAR <<ARGS,2>>
	DMOVEM B,ARGS
	MOVX A,.PRIOU
	SKIPE DEBUGP
	 SOUT%
	MOVE A,LINJFN		; output string to terminal
	DMOVE B,ARGS
	SOUT%
	 ERJMP R
	RETSKP

; $BLOCK - Block for a short duration
;	CALL $BLOCK
; Returns +1: Always

$BLOCK::SAVEAC <A>
	MOVX A,^D250		; block for 250ms
	DISMS%
	RET
	SUBTTL Other crud

; Output last JSYS error

FATAL:	EXCH 16,(P)		; save PC for message
	MOVEM 16,PCSAVE
	EXCH 16,(P)
	SAVEAC <A,B,C>
	EMSG <PC >
	MOVX A,.PRIOU
	HRRZ B,PCSAVE
	MOVX C,^D8
	NOUT%
	 ERJMP .+1
	TMSG <: >
	CALL LSTERR
	HALTF%
	RET

LSTERR:	MOVX A,.PRIOU		; output to terminal
	HRLOI B,.FHSLF		; this fork,,last error
	SETZ C,			; no limit
	ERSTR%
	 JRST ERRUND		; undefined error number
	 NOP			; can't happen
	TMSG <
>
	RET

ERRUND:	EMSG <Undefined error >
	MOVX A,.FHSLF		; get error number
	GETER%
	MOVX A,.PRIOU		; output it
	HRRZS B			; only right half where error code is
	MOVX C,^D8		; in octal
	NOUT%
	 ERJMP R		; ignore error here
	TMSG <
>
	RET

	XLIST
	LIT
	LIST

	END EVECL,,EVEC