Google
 

Trailing-Edge - PDP-10 Archives - tops20_v7_0_tcpip_distribution_tape - tcpip-sources/mmlbx.mac
There are 5 other files named mmlbx.mac in the archive. Click here to see a list.
	TITLE MMailbox Mailing lists for MMailr
	SUBTTL Written by Michael McMahon and Mark Crispin /MMcM/MRC/BillW

VWHO==0				;Who last edited MMAILBOX (0=developers)
VMAJOR==6			;TOPS-20 release 6.1
VMINOR==1
VMMLBX==^D62			;MMLBX's version number

	SEARCH MACSYM,MONSYM	;System definitions
	SALL			;Suppress macro expansions
	.DIRECTIVE FLBLST	;Sane listings for ASCIZ, etc.
	.TEXT "/NOINITIAL"	;Suppress loading of JOBDAT
	.TEXT "MMLBX/SAVE"	;Save as MMLBX.EXE
	.TEXT "/SYMSEG:PSECT:CODE" ;Put symbol table and patch area in CODE
	.TEXT "/SET:.LOW.:7000"	;Define lowseg PSECT
	.REQUIRE HSTNAM		;Host name routines
	.REQUIRE SYS:MACREL	;MACSYM support routines

; *******************************************************************
; *								    *
; *  MMailbox is an multiple network mailbox database program for   *
; * TOPS-20.  It was originally conceived by Mike McMahon (MIT	    *
; * (Artificial Intelligence Lab) and jointly developed for TOPS-20 *
; * with Mark Crispin (Stanford Computer Science Dept.).	    *
; *  The TENEX version of XMailbox was developed by Tom Rindfleisch *
; * (Stanford SUMEX Project) and Mike McMahon in January 1981.	    *
; *  MMailbox was developed from XMailbox version 127 for TCP/IP    *
; * and SMTP by Mark Crispin in September 1982.			    *
; *								    *
; *******************************************************************

;Entry points
;+0: GO		User, asks for address and types out answer
;+1: GO		Unused (reenter)
;+2: n/a	Version
;+3: MMLGO	Expansion entry, used by MMAILR
;+4: MMGO	Existence check entry, used by MM, FTPSER, MAISER, etc.

; Routines invoked externally

	EXTERN $GTPRO,$GTNAM
; AC definitions

F==0
A=1
B=2
C=3
D=4
E=5
I=6
J=7
T=10
TT=11
N=12
O=13
W=15
;CX=16
;P=17

QUOTE==42
LAB=="<"
RAB==">"

;;; Flags
FL%MRD==1B0			;Reading from MAILING-LISTS.TXT file
FL%ADI==1B1			;Allow file indirection
FL%PRV==1B2			;Override file protections
FL%RLY==1B3			;On if local address specified as relay address
FL%CMP==1B4			;Compiling binary file

;; This is where a superior inserts the address to be checked/expanded
	LOC 176
SUCES1:	0			;176 Auxillary value returned to superior
SUCCES:	0			;177 Value returned to superior
ADDRES:	BLOCK 100		;200 Where superior puts address to be expanded
EXPLST:	0			;300 Where expansion is put

	.PSECT DATA,10000	;Regular data starts here

RUNP:	-1			;Program has been run
WHEEL:	0			;We have caps to write new binary file
HAVSUP:	0			;We have a superior fork
MAISUP:	0			;Mail delivery process is superior
ENDPOS:	0			;Last pos before trailing space in MREAD/FREAD
MLSJFN:	0			;JFN for MAILING-LISTS.TXT
BINJFN:	0			;JFN for MAILING-LISTS.BIN
BINSIZ:	0			;Size of file in pages
PRGNAM:	BLOCK 2			;Program name
CMPPDP:	BLOCK 1			;Stack as of compiling
FNGFRK:	BLOCK 1			;Non-zero if we have FINGER mapped
BLKADR:	BLOCK 1			;Address of data block
HSTADR:	BLOCK 1			;Used by local host check
HSTTMP:	BLOCK ^D13		;Ditto

MLSBNM:	BLOCK <^D20>		;Filled in at INIT

NLOCAL==5
LCLADR: BLOCK NLOCAL
LCLPRO: BLOCK NLOCAL

NHSTCH==^D300			;Number of hosts to cache
HSTBFL: 0			;Flag - host cache is full
HSTBIX: 0			;Index into address and protocol tables
HSTBFR: HSTBSS			;Host string free space

HSTBAD: BLOCK NHSTCH+2		;Addresses
HSTBPR: BLOCK NHSTCH+2		;Protocols
HSTBSS: BLOCK 10*NHSTCH		;Strings
HSTBND:	BLOCK 20		;Slush from strings

HOSTTB:	NHSTCH			;TBLUK% table
	BLOCK NHSTCH

NPDL==777
PDL:	BLOCK NPDL		;Main stack
LPNPDL==100
LPPDL:	BLOCK LPNPDL		;Stack for finding forwarding loops

	.ENDPS
; Paged data areas

; Format of binary file documented here

	.PSECT BINDAT,100000	;MAILING-LISTS.BIN area

 BINADR==.			;Address where binary file starts
 BINPAG==<BINADR/1000>		;Page where binary file starts
BINFID:	BLOCK 1			;SIXBIT /MMLBX/
WRTTIM:	BLOCK 1			;Time of last write on text file
HSHMOD:	BLOCK 1			;Hash modulus
 BINHLN==.-BINADR		;Binary file header length
 HSHMD0==^D15013		;Magic prime number for hash modulus
 HSHFRE==1000			;Space at end of hash table
 HSHLEN==<<HSHMD0+BINHLN>!777>+HSHFRE-BINHLN ;Length of hash table
HSHTAB:	BLOCK HSHLEN		;The hash table itself
	BLOCK 1			;?? Is this still necessary ??
BINFRE:	BLOCK 100000-<.-BINADR>	;Binary file free storage
	.ENDPS

	.PSECT PAGDAT,400000
FNGADR:	BLOCK 1000		;Finger returned data address
 FNGPAG==<FNGADR/1000>		;Finger data page
TMPBUF:	BLOCK 20000		;Lots of space

STRSIZ==47777
STRBUF:	BLOCK <STRSIZ+1>/5
STRBF1:	BLOCK <STRSIZ+1>/5

	.ENDPS

	.PSECT FILDAT,500000

JFNPAG: BLOCK 100		;JFN to Page translations (firstpage,,lastpage)
JFNPTR: BLOCK 100		;JFN to byte pointer translation
FFP:	0			;First free page
FILADR=.+1000 & 777000		;Address of first file page
FILPAG=FILADR/1000		;First file page
MAXJFN=77

;Note: Files that are mapped into this PSECT are assumed to be opened
; and closed in stack order... (Last opened= first closed)
	.ENDPS
; Start of program

	.PSECT CODE,25000

EVEC:	JRST GO			;Start
	JRST GO			;Reenter
	BYTE(3)VWHO(9)VMAJOR(6)VMINOR(18)VMMLBX ;Version
	JRST MMLGO		;MMAILR entry (expansion)
	JRST MMGO		;Existance check entry (MM, etc.)
EVECL==.-EVEC

GO:	JSP A,INIT		;Map in database
	CALL UREAD		;Get address from user
	CALL CHECK		;Check for existence
	 JRST [	HRROI A,[ASCIZ/No such address/]
		JRST ERROR]	;+1 Non-ex
	 JRST [	TMSG <Local user, mail is not forwarded>
		JRST DONE]	;+2 Local user
	 JRST [	TMSG <Local file, mail is not forwarded>
		JRST DONE]	;+3 Local file
	 SKIPA			;+4 FINGER data
	  CALL EXPAND		;+5 Expand the address fully
	MOVEI J,EXPLST
	DO.
	  SKIPN E,(J)
	   JRST DONE
	  HRRO A,E
	  PSOUT%
	  IFXN. E,.LHALF
	    MOVEI A,"@"
	    PBOUT%
	    HLRO A,E
	    PSOUT%
	  ENDIF.
	  TMSG <
>
	  AOJA J,TOP.
	ENDDO.

MMGO:	JSP A,INIT
	SETOM HAVSUP		;Have a superior
	CALL CHECK
	 JRST [	SETZM SUCCES	;+1 No such user
		JRST DONE]
	 JRST [	MOVEI A,1
		MOVEM A,SUCCES	;+2 Local user, no forwarding
		JRST DONE]
	 JRST [	MOVEI A,2
		MOVEM A,SUCCES	;+3 Local file, no forwarding
		JRST DONE]
	 NOP			;+4 Forward address from FINGER database
	MOVEI A,3		;+5 Mailing list
	MOVEM A,SUCCES		;Has a mailing list entry
	JRST DONE		;And leave
MMLGO:	JSP A,INIT
	SETOM HAVSUP		;Have a superior
	SETOM MAISUP		;Flag mailer is superior
	CALL CHECK
	 JRST [	SETZM EXPLST	;+1 Not found, no expansion
		SETZM SUCCES
		JRST DONE]
	 NOP			;+2 Local user
	 JRST [	MOVSI A,ADDRES	;+3 Local file
		MOVEM A,EXPLST
		SETZM EXPLST+1
		MOVEI A,2
		MOVEM A,SUCCES
		JRST DONE]
	 JRST [ MOVEI A,3	;+4 Forward address from FINGER database
		MOVEM A,SUCCES
		JRST DONE]
	MOVEI A,3		;+5 Expanded address
	MOVEM A,SUCCES
	CALL EXPAND
	JRST DONE
;;; Common initialization
;;; Call: JSP A,INIT

INIT:	MOVE P,[IOWD NPDL,PDL]	;Init stack
	PUSH P,A		;Save return address
	SKIPL RUNP		;Have we been run before?
	IFSKP.
	  RESET%		;Once only - reset all I/O
	  SETO A,		;Get our names
	  MOVE B,[-2,,PRGNAM]
	  MOVEI C,.JISNM
	  GETJI%
	   NOP			;Shouldn't happen
	  SETZM BINJFN		;No binary JFN yet
	  SETZM FNGFRK		;No FINGER fork just yet
	ENDIF.
	SETZM FFP		;No filepages allocated yet
	SETZM HSTBIX		;Zero index
	SETZM HSTBFL		;Cache not full
	MOVEI A,HSTBSS		;Initial string sace pointer
	MOVEM A,HSTBFR
	MOVEI A,NHSTCH		;Set up TBLUK% table
	MOVEM A,HOSTTB
	SETZM HOSTTB+1
	MOVE A,[HOSTTB+1,,HOSTTB+2]
	BLT A,HOSTTB+NHSTCH-1
	SETZB F,HAVSUP		;No flags, no superior known as yet
	SETZM MAISUP		;Mailer not known as superior yet
	SETZM SUCES1		;No auxillary return value yet
	SETZM WHEEL		;Flag not a wheel just yet
	MOVX A,.FHSLF		;Enable all privs
	RPCAP%
	IOR C,B
	EPCAP%
	RPCAP%			;Watch for evil ACJ
	TXNE C,SC%WHL!SC%OPR	;Note if we are an enabled wheel
	 SETOM WHEEL
	MOVX A,GJ%SHT!GJ%OLD!GJ%PHY ;Physical only, short form, old file
	HRROI B,[ASCIZ/MAIL:MAILING-LISTS.TXT/]
	GTJFN%
	IFJER.
	  HRROI B,[ASCIZ/Cannot find mailing list file/]
	  JRST JSYERR
	ENDIF.
	MOVEM A,MLSJFN		;Save text JFN
	MOVE B,[1,,.FBWRT]	;Get time of last write
	MOVEI C,T
	GTFDB%
	HRLI A,.FBPRT		;Try to ensure right protection
	MOVX B,.RHALF
	MOVX C,777752
	SKIPE WHEEL		;If enabled
	 CHFDB%
	HRROI A,MLSBNM		;Set up binary file name
	HRRZ B,MLSJFN		;JFN of text file
	MOVE C,[110000,,1]	;Device and directory
	JFNS%
	HRROI B,[ASCIZ/MAILING-LISTS.BIN;P777752/]
	SETZ C,
	SOUT%			;Append our filename to it
;;; Map in binary file and see if we can use it

	DO.
	  SKIPE A,BINJFN	;Have a binary file?
	  IFSKP.
	    TXO F,FL%CMP	;No, must compile unless can find one
	    SETZM CMPPDP	;Don't have compiling stack yet
	    CALL MAPBIN		;Map in binary file
	     EXIT.		;Failed completely, must compile
	  ENDIF.
	  SKIPN WHEEL		;Have a binary file, only wheels may recompile
	  IFSKP.
	    SKIPE HAVSUP	;Have a superior?
REPEAT 0,<
;;;  Fall-back code to use if it turns out that too many problems occur
;;; with multiple MMailbox processes trying to recompile the binary file
	  ANSKP.		;No superior, allow recompile
>;REPEAT 0
REPEAT 1,<
;;;  Here to allow recompiling of the binary file only if MMailbox is run
;;; manually by a Wheel or if invoked by MMailr
	     SKIPE MAISUP	;Yes, is MMailr the superior?
	  ANNSK.		;No superior or MMailr is the superior
>;REPEAT 1
	    MOVE B,[1,,.FBWRT]	;Get time of last write of binary file
	    MOVEI C,D		; into D
	    GTFDB%
	    CAML D,T		;Binary newer than text?
	     CAME T,WRTTIM	;Yes, does its WRTTIM match write time of text?
	  ANNSK.
	    CALL UMPBIN		;Binary file out of date, toss it
	    JXE F,FL%CMP,TOP.	;If didn't just map it in, try again
	  ELSE.
	    TXZ F,FL%CMP	;Not compiling any more
	    MOVE A,MLSJFN	;Nothing more to do, flush the text JFN
	    RLJFN%
	     NOP
	    RET			;Return, ready to go
	  ENDIF.
	ENDDO.
;; Compile new binary file
	MOVEM P,CMPPDP		;Save compiling stack
	MOVE A,MLSJFN		;Open text file
	CALL OPNTXT
	IFNSK.
	  HRROI B,[ASCIZ/Cannot open mailing list file/]
	  JRST JSYERR
	ENDIF.

;; Parse new file
	MOVE A,MLSJFN
	MOVEI N,BINFRE		;Where to put text
	MOVEI O,TMPBUF		;Where to expand addresses
	MOVEM T,WRTTIM		;Save new update time
	MOVE T,[SIXBIT/MMLBX/]
	MOVEM T,BINFID
	MOVEI T,HSHMD0		;Initialize hash modulus
	MOVEM T,HSHMOD
	DO.
	  CALL FILTYI
	  IFL. B
	    HRROI B,[ASCIZ/EOF in file before formfeed/]
	    JRST IERROR
	  ENDIF.
	  CAIE B,.CHFFD
	   LOOP.
	ENDDO.
	DO.			;Read name of mailing list
	  CALL MREAD		;Get a token
	  JUMPL B,ENDLP.	;EOF, done with file
	  CAIN B,"="
	  IFSKP.
	    HRROI B,[ASCIZ/Bad delimiter, not =/]
	    JRST IERROR
	  ENDIF.
	  MOVEI T,STRBUF	;Token name
	  CALL HSHLUK		;Find hash table index
	  IFSKP.
	    HRROI B,[ASCIZ/Duplicate mailing list name/]
	    JRST IERROR
	  ENDIF.
	  HRLM O,(I)		;Save first address
	  HRRM N,(I)		;,,name
	  CALL CPYSTR		;Put in copy of name
	  DO.			;Read component addresses
	    CALL MREAD		;Read entry name
	    PUSH P,B		;Save delimiter
	    CALL CANADR		;Make site,,name
	    MOVEM E,(O)
	    ADDI O,1
	    POP P,B
	    CAIE B,.CHCRT	;End of line
	     JUMPGE B,TOP.	;Or end of file?
	  ENDDO.
	  SETZM (O)
	  ADDI O,1
	  JUMPGE B,TOP.		;Back for more addresses
	ENDDO.
	MOVE O,N		;Put all addresses in file now

;; Expand all addresses just within file
	TXZ F,FL%ADI!FL%PRV	;Don't bother with indirect files
	MOVE W,[IOWD LPNPDL,LPPDL] ;Init forwarding loop stack
	MOVE I,[-HSHLEN,,HSHTAB]
	DO.
	  SKIPN (I)		;Is there an entry here?
	  IFSKP.
	    HRRZ E,(I)		;Get this address
	    PUSH W,E		;First one to check
	    HLRZ J,(I)		;Yes, get start of addresses
	    HRLM O,(I)		;Save relocated list pointer
	    DO.
	      SKIPN E,(J)	;An address there?
	      IFSKP.
		CALL CKLOOP	;Looping on this address?
		 JRST EXPLPX	;Yes
		PUSH W,E	;No, save the address being expanded
		CALL EXPAD0
		ADJSP W,-1	;Reduce loop stack
		AOJA J,TOP.
	      ENDIF.
	    ENDDO.
	    SETZM (O)		;Clear last entry
	    ADDI O,1
	    ADJSP W,-1		;Pop first address
	  ENDIF.
	  AOBJN I,TOP.
	ENDDO.
	MOVE B,[1,,.FBGEN]	;While we're here, get generation #
	MOVEI C,C		;Into C
	GTFDB%
	CALL CLSTXT		;Close text file
	 NOP
	SKIPN WHEEL		;Can we write new version?
	 RET
	MOVX A,GJ%FOU!GJ%SHT!GJ%PHY
	HLR A,C			;Try to keep generation #'s parallel
	HRROI B,MLSBNM
	GTJFN%
	IFJER.
	  HRROI B,[ASCIZ/Cannot find mailing list binary file/]
	  JRST JSYERR
	ENDIF.
	PUSH P,A
	MOVEI B,OF%WR
	OPENF%
	IFJER.
	  POP P,A		;Can't update binary file, no-op it
	  RLJFN%
	   NOP
	  RET
	ENDIF.
	POP P,A
	HRLZ B,A
	MOVE A,[.FHSLF,,BINPAG]
	MOVEI C,777-BINADR(O)
	LSH C,-<^D9>
	TXO C,PM%CNT!PM%RD!PM%WR!PM%CPY
	PMAP%
	HLRZ A,B
	CLOSF%
	 NOP
	TXZ F,FL%CMP		;No longer compiling binary file
	SETZM CMPPDP		;Don't have compiling stack any more
	CALL MAPBIN		;Map it back in for read now
	 JRST ERROR
	RET

;;;Bad input file error handler
IERROR:	PUSH P,A		;Save JFN
	HRROI A,STRBF1		;Place for error string
	SETZ C,
	SOUT%			;Print the error string
	HRROI B,[ASCIZ/ while parsing /]
	SOUT%
	POP P,B			;Restore JFN
	JFNS%
	JRST IERR1
;;; Canonicalize address
;;; Entry: STRBUF/ address from file
;;; Call: CALL CANADR
;;; Returns: +1
;;;	    E/ host name,,user name
;;; host name of FILHST is special, means destination is indirect file
FILHST==777777
CANADR:	SAVEAC <A,B,C>
	STKVAR <HSTPTR>
	HRRZ E,N		;Save start of name (will be copied here)
	MOVE A,[POINT 7,STRBUF]
	SETZ B,			;Where host pointer will be if any
	DO.
	  ILDB C,A
	  IFN. C
	    CAIN C,"@"
	     MOVE B,A		;Save pointer to last @
	    LOOP.
	  ENDIF.
	ENDDO.
	IFE. B			;If no host name, copy string and return
	  SAVEAC <T,TT>
	  MOVEI T,STRBUF	;Is this name in hash table?
	  CALL HSHLUK		;Well?
	   JRST CPYSTR		;No, just copy it then
	  HRRZ E,(I)		;Yes, use that value
	  RET
	ENDIF.
	CAME B,[POINT 7,STRBUF,6] ;Was the @ the first character?
	IFSKP.
	  HRLI E,FILHST		;Yes, local address indirect file
	  CALL CPYSTR
	ELSE.
	  SETZ C,		;Foreign address
	  DPB C,B		;Replace @ with null
	  CALL CPYSTR		;Copy the name
	  MOVEM B,HSTPTR
	  MOVE A,HSTPTR		;Get pointer to host
	  SETO C,		;Try all protocols
	  CALL GETPRO		;Look up host name
	  IFSKP.
	    MOVEM B,HSTADR	;Save host address returned
	    HRROI A,HSTTMP	;Store local name in scratch area
	    SETO B,		;Local host address for this protocol
	    CALL MYADDR		;Get local host address for this protocol
	    IFSKP.
	      CAMN B,HSTADR	;Is this our local host?
	       RET		;Yes, don't need host name
	    ENDIF.
	  ENDIF.
	  HRL E,N
	  MOVE A,HSTPTR		;Start of host name
	  HRLI N,(<POINT 7,0>)
	  DO.
	    ILDB B,A
	    IDPB B,N
	    JUMPN B,TOP.
	  ENDDO.
	  MOVEI N,1(N)		;Update free pointer
	ENDIF.
	RET
;;; Expand address from index in I
EXPAND:	MOVEI N,TMPBUF		;Where to put any strings we make
	MOVEI O,EXPLST		;Where expansion goes
	MOVE W,[IOWD LPNPDL,LPPDL] ;Init forwarding loop stack
	TXO F,FL%ADI!FL%PRV	;Allow file indirection
EXPAN0:	SAVEAC <E,J>
	HLRZ J,(I)		;Get start of addresses
	DO.
	  SKIPN E,(J)		;An address there?
	  IFSKP.
	    CALL CKLOOP		;Looping on this address?
	     JRST EXPLPX	;Yes
	    PUSH W,E		;No, save the address being expanded
	    CALL EXPAD0
	    ADJSP W,-1		;Reduce loop stack
	    AOJA J,TOP.
	  ENDIF.
	ENDDO.
	SETZM (O)		;Clear last entry
	RET
;;; Expand a single address in E into list accumulating in O
;;; No indirect file handling.  EXPADX allows indirect files
EXPADR:	TXZ F,FL%ADI!FL%PRV	;Don't allow indirect files here
EXPADX:	MOVE W,[IOWD LPNPDL,LPPDL] ;Init forwarding loop stack
EXPAD0:	IFXE. E,.LHALF		;Is there a host?
	  SAVEAC <T,I>
	  MOVE T,E
	  CALL HSHLUK		;Look it up
	  IFSKP.
	    CALLRET EXPAN0	;No, recurse
	  ENDIF.
	  MOVEM E,(O)		;No expansion
	  AOJA O,R		;Increment O and return
	ENDIF.
	IFXE. F,FL%ADI
	  MOVEM E,(O)		;No expansion
	  AOJA O,R		;increment O and return
	ENDIF.
	TLC E,FILHST
	TLCN E,FILHST
	IFSKP.
	  MOVEM E,(O)		;No expansion
	  AOJA O,R		; increment O and return
	ENDIF.

;; Read indirect file
	SAVEAC <A,B,T,E>	;Save current state
	STKVAR <INDJFN>		;Indirect file JFN

;;; This code is necesary to fix a security bug.  A malicious user could use
;;;mailer to divulge parts of a protected file by queueing a message
;;;referring to a protected file on the system.  The mailer's error report
;;;may have useful information about the contents of the protected file in it.
;;; This requires that all indirect files referenced as such through the
;;;mailsystem (as opposed to directly via MM) must be publicly readable.
;;;However, indirect files referenced through MAILING.LISTS do not have to
;;;be (note however that a protected indirect file may cause problems for an
;;;unprivileged invocation of MMailbox).
	IFXE. F,FL%PRV		;Agent of MAILING.LISTS?
	  SKIPE MAISUP		;No, is mailer our superior?
	   SKIPN WHEEL		;Do we have capabilities enabled?
	ANSKP.
	  PUSH P,C		;In case necessary
	  MOVEI A,.FHSLF	;Get current capabilities
	  RPCAP%
	  TXZE C,SC%WHL!SC%OPR	;Disable wheel/operator capabilities
	   EPCAP%
	  POP P,C
	ENDIF.
	HRRZ B,E
	HRLI B,(<POINT 7,0,6>)
	MOVX A,GJ%SHT!GJ%OLD!GJ%PHY!GJ%ACC
	GTJFN%
	IFJER.
	  AOS SUCES1		;Note hard error
	  HRROI B,[ASCIZ/Cannot find indirect file/]
	  JRST JSYERR
	ENDIF.
	MOVEM A,INDJFN
	CALL OPNTXT
	IFNSK.
	  MOVE A,INDJFN		;Flush the JFN we got
	  RLJFN%
	   NOP
	  HRROI B,[ASCIZ/Cannot open indirect file/]
	  JRST JSYERR
	ENDIF.
	IFXE. F,FL%PRV		;Did we turn our privileges off?
	  SKIPE MAISUP		;Yes, is mailer our superior?
	   SKIPN WHEEL		;Need to restore capabilities?
	ANSKP.
	  PUSH P,C		;In case necessary
	  MOVX A,.FHSLF		;Retrieve capabilities
	  RPCAP%
	  IOR C,B		;Enable all capabilities again
	  EPCAP%
	  POP P,C
	  MOVE A,INDJFN		;Restore JFN
	ENDIF.
	DO.
	  CALL FREAD		;Read file address
	  PUSH P,B
	  LDB B,[POINT 7,STRBUF,6] ;Get first byte of address
	  IFN. B		;If null, no address to expand
	    CALL CANADR		;Canonicalize
	    CALL CKLOOP		;Looping on this address?
	     JRST EXPLPX	;Yes
	    PUSH W,E		;No, save the address being expanded
	    CALL EXPAD0		;Expand this one
	    ADJSP W,-1		;Reduce loop stack
	  ENDIF.
	  POP P,B
	  JUMPGE B,TOP.
	ENDDO.
	MOVE A,INDJFN		;Indirect file JFN
	CALL CLSTXT		;Close off file
	IFNSK.
	  HRROI A,STRBF1	;Build error string in STRBF1
	  SETO B,		;Timestamp
	  SETZ C,
	  ODTIM%
	   ERJMP .+1
	  HRROI B,[ASCIZ/MMailbox: Cannot close indirect file "/]
	  SETZ C,
	  SOUT%
	  MOVE B,INDJFN		;JFN that lost
	  JFNS%
	  HRROI B,[ASCIZ/" - /]
	  SOUT%
	  HRLOI B,.FHSLF
	  ERSTR%
	   NOP
	   NOP 
	  HRROI A,STRBF1
	  PSOUT%
; For now don't consider this an error
	ENDIF.
	RET

; Here to bomb out on expansion loop
EXPLPX:	HRROI A,[ASCIZ/Loop while expanding address/]
	JRST ERROR
; Routine to check on expansion loop
; Entry:   e = address to be checked
;	   w = stack ptr for previous addresses in expansion path
; Call:    CALL CKLOOP
; Return:  +1, Loop detected
;	   +2, No loop

CKLOOP:	SAVEAC <A>		;Save working AC
	MOVE A,[IOWD LPNPDL,LPPDL] ;Start of stack
	DO.
	  CAMN A,W		;End yet?
	   RETSKP		;Yes, no loop
	  AOBJP A,CKLPX		;Screw-up if turns positive!
	  CAME E,0(A)		;Already expanded this address?
	   LOOP.		;No
	ENDDO.
	RET			;Yes, expansion loop

; Here on internal screw-up
CKLPX:	HRROI A,[ASCIZ/Address expansion screwup/]
	JRST ERROR
;;; Check for mailing list
;;; ADDRESS/ user supplied address
;;; CALL CHECK
;;; +1: Not found
;;; +2: Local user
;;; +3:	Local file
;;; +4: Have found forward address in FINGER database, returning list
;;; +5: Mailing list, I will have hash table index

CHECK:	TXZ F,FL%RLY		;Initially no local relaying done
	DO.
	  MOVE A,[POINT 7,ADDRES,6] ;See if "%" style relay address
	  SETZ D,		;Originally no "last" byte
	  DO.
	    ILDB C,A		;Check byte
	    CAIE C,"@"		;This may be useful for really stupid composers
	     CAIN C,"%"		;Possible route delimiter?
	      MOVE D,A		;Remember destination byte pointer
	    JUMPN C,TOP.	;Loop for further bytes
	  ENDDO.
	  JUMPE D,ENDLP.	;Any "%" seen?
	  MOVE A,D		;Yes, get pointer to string to check
	  SETO C,		;Try all protocols
	  CALL GETPRO		;Validate host name
	   RET			;No such host, address fails
	  MOVEM B,HSTADR	;Save host address returned
	  HRROI A,HSTTMP	;Store local name in scratch area
	  SETO B,		;Local host address for this protocol
	  CALL MYADDR		;Get local host address for this protocol
	  IFSKP.
	    CAME B,HSTADR	;Is this our local host?
	  ANSKP.
	    SETZ C,		;Yes, tie off address
	    DPB C,D		;Edit as appropriate
	    TXO F,FL%RLY	;Local, flag must simulate FINGER return
	    LOOP.		;Now recurse
	  ENDIF.
	  MOVEI C,"@"		;Not local host, change to "@"
	  DPB C,D		;Edit address
	  DMOVE A,[POINT 7,STRBUF ;Copy string where FNGSMX wants it
		   POINT 7,ADDRES]
	  CALL MOVSTR
	  JRST FNGSIM
	ENDDO.
	MOVEI T,ADDRES		;Address of user string
	CALL HSHLUK		;Look in hash table
	 JRST CHKUSR		;Failed, try other options
CPOP4J:	AOS (P)			;+5 mailing list
CPOP3J:	AOS (P)			;+4
CPOP2J:	AOS (P)			;+3
CPOP1J:	AOS (P)			;+2
	RET			;+1
CHKUSR:	LDB A,[POINT 7,ADDRES,6] ;Get first character of name
	CAIE A,"*"		;Allow filespec
	IFSKP.
	  IFXN. F,FL%RLY	;Was it relayed?
	    DMOVE A,[POINT 7,STRBUF ;Yes, must simulate FINGER then
		     POINT 7,ADDRES]
	    CALL MOVSTR
	    JRST FNGSIM
	  ENDIF.
	  MOVX A,GJ%SHT!GJ%OLD!GJ%PHY ;Else see if file exists
	  MOVE B,[POINT 7,ADDRES,6]
	  GTJFN%
	   ERJMP R		;No, barf address
	  RLJFN%		;Flush the JFN
	   ERJMP .+1
	  JRST CPOP2J		;And return +3 filespec
	ENDIF.
	CAIE A,"@"		;Allow indirect
	IFSKP.
	  DMOVE A,[POINT 7,STRBUF ;Copy string where FNGSMX wants it
		   POINT 7,ADDRES]
	  CALL MOVSTR
	  JRST FNGSMX		;Return it
	ENDIF.
	MOVX A,RC%EMO		;A.ne.B see if user name, require exact match
	LDB B,[POINT 7,ADDRES,6] ;Get first character of name
	CAIN B,"&"		;Allow special syntax meaning local user
	 SKIPA B,[POINT 7,ADDRES,6] ;It was, so slide over by 1
	  MOVE B,[POINT 7,ADDRES] ;Proposed user name string
	MOVE C,B		;See if null string
	ILDB C,C		;Get first byte in string
	JUMPE C,R		;If null, punt it completely
	RCUSR%			;Parse it
	 ERJMP R		;If garbage characters, punt it completely
	IFXE. A,RC%NOM!RC%AMB	;Was it a user name?
	  IFXN. F,FL%RLY	;Yes, was it relayed?
	    DMOVE A,[POINT 7,STRBUF ;Yes, must simulate FINGER then
		     POINT 7,ADDRES]
	    CALL MOVSTR
	    JRST FNGSIM
	  ENDIF.
	  RETSKP		;No, local user with no forwarding
	ENDIF.
	MOVE A,[POINT 7,ADDRES]	;Check to see if BUG-MM mail
	MOVE B,[POINT 7,[ASCIZ/BUG-MM/]]
	CALL CMPSTR		;Are the strings equal?
	IFNSK.
	  MOVE A,[POINT 7,STRBUF] ;Buffer to use
	  MOVEI B,[ASCIZ/Bug-MM@SIMTEL20.ARPA/] ;Default BUG-MM host
	  CALL MOVSTR		;Copy the string
	  JRST FNGSIM		;Simulate data returned from FINGER
	ENDIF.
	MOVE A,[POINT 7,ADDRES]	;Check to see if SYSTEM mail
	MOVE B,[POINT 7,[ASCIZ/SYSTEM/]]
	CALL CMPSTR		;Are the strings equal?
	 RETSKP			;+2 treat SYSTEM as local user

;;;Total non-match, see if FINGER will return any information on this string
	SKIPLE A,FNGFRK		;Have FINGER yet?
	 JRST HAVFNG		;Yes, skip FINGER loading
	JUMPL A,NOFING		;If FINGER fork negative, don't use it ever
	MOVX A,GJ%OLD!GJ%SHT	;Look up FINGER
	HRROI B,[ASCIZ/SYS:FINGER.EXE/]
	GTJFN%
	 ERJMP NOFING		;FINGER not present
	PUSH P,A		;Save JFN
	MOVX A,CR%CAP		;Create a new fork
	CFORK%
	IFJER.
	  POP P,A		;Can't get fork, punt
	  RLJFN%		;Flush the JFN
	   NOP
	  JRST NOFING
	ENDIF.
	MOVEM A,FNGFRK		;Save fork handle
	POP P,A			;Get JFN back
	HRL A,FNGFRK		;Fork handle,,JFN
	GET%
	MOVE A,[.FHSLF,,FNGPAG]	;Map FNGPAG of this fork
	HRLZ B,FNGFRK		;From page 777 of FINGER (well-known)
	HRRI B,777
	MOVX C,PM%RD!PM%WR!PM%PLD ;Read/write/preload
	PMAP%
HAVFNG:	MOVE A,[POINT 7,FNGADR]	;Have FINGER.  Where to put the string
	MOVEI B,ADDRES		;Source
	CALL MOVSTR		;Copy the string to FINGER
	MOVE A,FNGFRK		;Get back fork handle
	MOVEI B,3		;Start inferior at offset 3
	SFRKV%
	 ERJMP FNGERR		;FINGER doesn't support +3
	RFORK%			;Resume, in case it didn't get going
	WFORK%			;Sleep until fork is finished
	DMOVE A,PRGNAM		;Restore program name
	SETSN%
	 NOP			;No failure returns defined
	MOVE A,FNGFRK		;See if it finished okay
	RFSTS%
	HLRZ A,A
	CAIE A,.RFHLT		;Fork halted?
	 JRST FNGERR		;No, FINGER fork is sick
	SKIPN FNGADR+400	;Positive reply from FINGER?
	 JRST NOFING		;No
	MOVE A,[POINT 7,STRBUF]	;Where to put it
	MOVEI B,FNGADR+400	;FINGER returns username string here
	CALL MOVSTR		;Copy the strings
	MOVEI N,TMPBUF		;Where expansion goes
	CALL CANADR		;Make canonical address in E
	MOVEI O,EXPLST		;Where to put expression
	CALL EXPADR		;Expand net address
	SETZM (O)		;Tie off list
	JRST CPOP3J		;+4 data from FINGER
;;;Fatal error in FINGER fork; flush it...
FNGERR:	SETO A,			;Unmap shared page
	MOVE B,[.FHSLF,,FNGPAG]	;Mapped to FNGPAG
	SETZ C,
	PMAP%
	MOVE A,FNGFRK		;Get FINGER fork
	KFORK%			;Kill the fork
	SETOM FNGFRK		;Flag not to use FINGER again
;	JRST NOFING

;;;No FINGER or no match.  If a BUG-xxx, try for BUG-RANDOM-PROGRAM
NOFING:	HRROI A,[ASCIZ/BUG-/]	;Prefix for bug reports
	HRROI B,ADDRES		;User's string
	STCMP%
	IFXN. A,SC%SUB		;Is "BUG-" a subset of user's string?
	  MOVEI T,[ASCIZ/BUG-RANDOM-PROGRAM/] ;Yes, return BUG-RANDOM-PROGRAM
	  CALL HSHLUK
	   RET			;Not present, address fails utterly
	  JRST CPOP4J		;Return forwarded address
	ENDIF.
	HRROI A,[ASCIZ/HELP-/]	;Prefix for HELP
	HRROI B,ADDRES		;User's string
	STCMP%
	JXE A,SC%SUB,R		;Fail if not a substring
	MOVEI T,[ASCIZ/HELP-RANDOM-PROGRAM/]
	CALL HSHLUK		;Substring, return HELP-RANDOM-PROGRAM
	 RET			;Not present, address fails utterly
	JRST CPOP4J		;Return forwarded address

;;;Simulate return from FINGER, STRBUF/ address to return
FNGSIM:	TXZA F,FL%ADI		;Don't allow indirect files
FNGSMX:	 TXO F,FL%ADI		;This is an indirect file
	TXZ F,FL%PRV		;Don't override protections
	MOVEI N,TMPBUF		;Where expansion goes
	CALL CANADR		;Make canonical address in E
	MOVEI O,EXPLST		;Where to put expression
	CALL EXPADX		;Expand net address
	SETZM (O)		;Tie off list
	JRST CPOP3J		;+4 simulate data from FINGER
;;; Lookup string in hash table
;;; T/ address of string
;;; Returns +1, not found
;;; +2, found
;;; in either case, I has index to hash table
HSHLUK:	SAVEAC <A,B>
	STKVAR <HSHSTR,HSHIDX>
	HRLI T,(<POINT 7,0>)
	MOVEM T,HSHSTR		;Save string pointer
	CALL HASH		;Hash string into number
	MOVEM TT,HSHIDX		;Save first index
	MOVE T,HSHIDX
	DO.
	  IDIV T,HSHMOD		;Divide by modulus
	  SKIPN A,HSHTAB(TT)	;Look for entry here
	   EXIT.		;Not found, return
	  HRLI A,(<POINT 7,0>)
	  MOVE B,HSHSTR		;Given string
	  CALL CMPSTR		;Compare strings
	  IFSKP.
	    AOS T,HSHIDX
	    LOOP.
	  ENDIF.
	  AOS (P)		;Set success return
	ENDDO.
	MOVEI I,HSHTAB(TT)	;Return absolute pointer
	RET

	ENDSV.

;;; Hash string in T until null
HASH:	SAVEAC <C>
	SETZ TT,
	DO.
	  ILDB C,T
	  JUMPE C,ENDLP.
	  LSH TT,7
	  TRZ C,40		;Case independent
	  XORI TT,(C)
	  LOOP.
	ENDDO.
	TLC TT,(TT)		;Make positive (18-bits)
	HLRZ TT,TT
	RET
;;; Compare strings in A and B
;;; +1 same, +2 different
CMPSTR:	SAVEAC <C,D>
	DO.
	  ILDB C,A
	  CAIL C,"a"
	   CAILE C,"z"
	    CAIA
	     SUBI C,"a"-"A"
	  ILDB D,B
	  CAIL D,"a"
	   CAILE D,"z"
	    CAIA
	     SUBI D,"a"-"A"
	  CAME C,D
	  IFSKP.
	    JUMPN C,TOP.	;More to do
	    RET			;Strings match
	  ENDIF.
	ENDDO.
	RETSKP			;Strings don't match

;;; Copy string from STRBUF
;;;        N/ pointer to string free space
CPYSTR:	SAVEAC <A,B>
	MOVE A,[POINT 7,STRBUF]
CPYST1:	HRLI N,(<POINT 7,0>)
	DO.
	  ILDB B,A
	  IDPB B,N
	  JUMPN B,TOP.
	ENDDO.
	MOVEI N,1(N)		;Update free pointer
	RET

;;; Routine to move an ASCIZ string from B to A
; Entry:   a = destination string ptr
;	   b = source buffer address
; Call:    CALL MOVSTR
; Return:  +1
MOVSTR:	TXCE B,.LHALF		;Source str ptr supplied?
	 TXCN B,.LHALF
	  HRLI B,(<POINT 7,0>)	;No, make it a valid str ptr
	SAVEAC <C>
	DO.
	  ILDB C,B
	  JUMPE C,ENDLP.	;Quit on null
	  IDPB C,A
	  LOOP.
	ENDDO.

; Here to finish ASCIZ string
	PUSH P,A		;Save dest ptr so can continue string
	IDPB C,A
	POP P,A
	RET
;;; Cache the host names used, so as to avoid the full overhead of
;;; a GTHST% or GTDOM% every time we need to look up a host.

; Cache the lookup of our local address for each protocol, A/ pointer
;to string, C/ protocol ID

MYADDR:	STKVAR <HSTPTR>
	MOVEM A,HSTPTR
	SETZ A,
	DO.
	  CAME C,LCLPRO(A)	;This protocol?
	  IFSKP.
	    MOVE B,LCLADR(A)	;Get address
	    MOVE A,HSTPTR	;Restore string pointer
	    RETSKP
	  ENDIF.
	  SKIPE LCLPRO(A)	;End of current list, and not found yet?
	  IFSKP.
	    MOVEM C,LCLPRO(A)
	    EXCH A,HSTPTR	;Get back string pointer
	    CALL $GTNAM		;Get local host address for this protocol
	     RET		;Return failure
	    EXCH A,HSTPTR
	    MOVEM B,LCLADR(A)	;Save this address
	    EXCH A,HSTPTR
	    RETSKP
	  ENDIF.
	  CAIGE A,NLOCAL-1	;End of list?
	   AOJA A,TOP.
	ENDDO.
	MOVE A,HSTPTR		;Out of room
	CALLRET $GTNAM		;Just chain to normal routine...

	ENDSV.
GETPRO:	SAVEAC <N>
	STKVAR <HSTPTR,HSTPRO,HSTENT>
	MOVEM A,HSTPTR
	MOVEM C,HSTPRO
	MOVEI A,HOSTTB		;Host TBLUK table
	MOVE B,HSTPTR		;Get string
	TBLUK%
	IFXN. B,TL%EXM		;Exact match?
	  HRRZ A,(A)		;Get arguments
	  MOVE B,HSTBAD(A)	;Get address
	  MOVE C,HSTBPR(A)	;Get protocol
	  MOVE A,HSTPTR		;Return pointer
	  RETSKP
	ENDIF.
	MOVE A,HSTPTR		;Get back args to $GTPRO
	MOVE C,HSTPRO
	CALL $GTPRO
	 RET			;Return failure
	SKIPE HSTBFL		;Cache full?
	IFSKP.
	  AOS A,HSTBIX		;No, increment and fetch index
	  MOVEM B,HSTBAD(A)	;Save address
	  MOVEM C,HSTBPR(A)	; and protocol
	  HRLZ B,HSTBFR		;Where the string will go
	  HRR B,A		;Index to info for that host
	  MOVEM B,HSTENT
	  MOVE N,HSTBFR		;Get free space pointer
	  MOVE A,HSTPTR		;Get string pointer
	  CALL CPYST1		;Copy the string
	  MOVEM N,HSTBFR	;Save new free space pointer
	  CAILE N,HSTBND	;Out of free space?
	   SETOM HSTBFL		;Set full flag...
	  MOVEI A,HOSTTB	;Host TBLUK% table
	  MOVE B,HSTENT
	  TBADD%		;Add it to the table
	..TAGF (ERJMP,)		;I sure wish ANNJE. existed!
	  MOVE C,HSTBIX
	  MOVE A,HSTPTR
	  MOVE B,HSTBAD(C)	;Get address again
	  MOVE C,HSTBPR(C)	;Get protocol again
	ELSE.
	  SETOM HSTBFL		;set failure flag
	ENDIF.
	RETSKP

	ENDSV.
;;; File reading routines
;;; Entry: A/ jfn
;;; Call: CALL MREAD		;Read from MAILING-LISTS.TXT
;;;       CALL FREAD		;Read from an @ file
;;; Return: +1
;;;	   B/ delimiter character

;;; Read a token from MAILING-LISTS.TXT, terminated by space or NL or tab
MREAD:	TXOA F,FL%MRD		;Flag reading from MAILING-LISTS.TXT
FREAD:	 TXZ F,FL%MRD		;Flag reading from user file
	SAVEAC <C,D>
REDTOP:	MOVE C,[POINT 7,STRBUF]	;Where to put it
	MOVEM C,ENDPOS		;Mark beginning of trailing spaces
	MOVEI D,STRSIZ		;Maximum size
	CALL WITTYI		;Flush initial whitespace
	SKIPA			;Already have first character
REDLUP:	 CALL FILTYI		;Get next character from file
REDLP1:	JUMPL B,REDRET		;Eof always terminates
	CAIN B,.CHCRT		;EOL terminates and flushes
	 JRST SKPWHT
	JUMPE B,SKPWHT		;Null terminates and flushes
	CAIN B,"-"              ;Do funny thing with dash
	 JRST RDDASH
	CAIN B,QUOTE
	 JRST REDQOT
	JXN F,FL%MRD,REDCKM
	CAIE B," "		;Tack on spaces for now to be amputated later
	 CAIN B,.CHTAB
	  JRST REDSPA
	CAIN B,","
	 JRST REDRET
	CAIE B,":"
	 JRST REDCHR
	LDB B,[POINT 7,STRBUF,6]
	CAIE B,"*"		;Unless a filespec,
	 JRST REDTOP		;Ignore atoms terminated by :
	MOVEI B,":"		;Otherwise the colon really does go in
REDCHR:	SOJL D,REDTLG
	IDPB B,C
	MOVEM C,ENDPOS
	JRST REDLUP

REDTLG:	HRROI B,[ASCIZ/Atom too long/]
	JRST IERROR

REDRET:	SETZ D,
	IDPB D,ENDPOS
	RET

REDCKM:	CAIE B," "              ;Space terminates and flushes trailing space
	 CAIN B,.CHTAB
	  JRST SKPWHT
	CAIN B,"="
	 JRST REDRET
	JRST REDCHR

REDQOT:	CALL FILTYI		;Saw quote.  Quotes are not included in buffer
	IFL. B
	  HRROI B,[ASCIZ/EOF in the middle of a string/]
	  JRST IERROR
	ENDIF.
	CAIE B,QUOTE		;Second quote?
	IFSKP.
	  CALL FILTYI		;Peek at next character
	  CAIN B,QUOTE		;Was it a doubled quote?
	ANSKP.			;Yes, insert single quote in string
	  MOVEM C,ENDPOS	;No, end of quoted string
	  JRST REDLP1		;Enter loop with next character in B
	ENDIF.
	SOJL D,REDTLG		;No, insert quoted character into buffer
	IDPB B,C
	JRST REDQOT

;;;Skip trailing whitespace, enter SKPWHT with terminator in B
SKPWHT:	MOVE D,B		;Keep track of whether we get a CR
	DO.
	  CALL FILTYI		;Get a byte
	  CAIE B," "		;Space?
	   CAIN B,.CHTAB	;Or TAB?
	    LOOP.		;Yes, skip to next
	  JUMPE B,TOP.		;Skip nulls
	  DO.
	    CAIN B,.CHCRT	;Remember if we get a CR
	     JRST SKPWHT
	    CAIE B,"-"		;Dash?
	    IFSKP.
	      CALL REDDS1	;Followed by CR is no-op, else is good char 
	    ANSKP.
	      MOVE B,D
	      JRST SKPWHT
	    ENDIF.
	    JUMPL B,REDRET	;Return EOF
	    CAIE B,";"		;Comment?
	    IFSKP.
	      CALL REDCM1	;Yes, flush and check terminator
	      LOOP.
	    ENDIF.
	    CAIN B,"!"		;Inline comment?
	    IFSKP.
	      CALL BCKTXT	;Back up over character
	      MOVE B,D		;And return saved CR or terminator
	      JRST REDRET
	    ENDIF.
	    CALL REDXC1		;Yes, flush
	    CAIE B,"!"		;Ended on matching excl?
	     LOOP.		;No, check terminator
	  ENDDO.
	  LOOP.			;Yes, get next character and continue
	ENDDO.

REDSPA:	CALL WITTYI		;Yes, eat white space
	JRST REDLP1		;Go look at the next character

RDDASH:	CALL REDDS1             ;On dash, check for EOL
	 JRST REDLUP
	JRST REDCHR

;;; Skip leading whitespace
WITTYI:	CALL FILTYI
	JUMPE B,WITTYI		;Ignore nulls
	CAIN B,.CHTAB
	 JRST WITTYI
	CAIE B," "
	 CAIN B,.CHCRT
	  JRST WITTYI
	CAIN B,";"
	 JRST REDCMT		;Read a comment
	JXN F,FL%MRD,R
	CAIN B,"!"
	 JRST REDXCL
	CAIN B,"-"
	 JRST REDDSH		;Check for - crlf
	RET

REDCMT:	CALL REDCM1		;Read the comment
	JRST WITTYI		;And skip more leading whitespce

;;;Read and drop until EOL
REDCM1:	CALL FILTYI
	JUMPL B,R
	CAIE B,.CHCRT
	 JRST REDCM1
	RET

REDXCL:	CALL REDXC1		;Read the comment
	JRST WITTYI		;And skip more leading whitespce

REDXC1:	CALL FILTYI		;Saw an !, read to matching ! or newline
	JUMPL B,R
	CAIE B,"!"
	 CAIN B,.CHCRT
	  RET
	JRST REDXC1

REDDSH:	CALL REDDS1
	 JRST WITTYI
	RET

REDDS1:	CALL FILTYI
	JUMPL B,REDDS2
	CAIN B,.CHCRT
	 RET
	CALL BCKTXT
REDDS2:	MOVEI B,"-"
	JRST CPOP1J

;;; Read a single character, returns -1 at EOF, Just 15 for CRLF
FILTYI: CAIG A,MAXJFN		;Reasonable JFN?
	IFSKP.
	  HRROI A,[ASCIZ/Illegal JFN at FILTYI/]
	  JRST ERROR
	ENDIF.
	ILDB B,JFNPTR(A)	;Get a byte
	CAIE B,.CHCRT
	IFSKP.
	  ILDB B,JFNPTR(A)	;Get a byte
	  CAIE B,.CHLFD		;Treat stray CR's as CRLF
	   CALL BCKTXT
	  MOVX B,.CHCRT
	ENDIF.
	CAIN B,.CHLFD		;Stray LF = CR
	 MOVX B,.CHCRT
	SKIPN B			;Assume a NUL only at EOF...
	 SETO B,
	RET

BCKTXT: CAIG A,MAXJFN		;Reasonable JFN?
	IFSKP.
	  HRROI A,[ASCIZ/Illegal JFN at BCKTXT/]
	  JRST ERROR
	ENDIF.
	SAVEAC <B>
	SETO B,
	ADJBP B,JFNPTR(A)	;Back up the pointer
	MOVEM B,JFNPTR(A)
	RET

OPNTXT: CAIG A,MAXJFN		;Reasonable JFN?
	IFSKP.
	  HRROI A,[ASCIZ/Illegal JFN at OPNTXT/]
	  JRST ERROR
	ENDIF.
	MOVEI B,FILPAG
	SKIPN FFP		;Initialized yet?
	 MOVEM B,FFP		;No, do so...
	MOVX B,<<FLD ^D7,OF%BSZ>!OF%RD>
	OPENF%
	 ERJMP R
	PUSH P,A
	SIZEF%			;Get file size (in C)
	 SETZB B,C
	MOVE B,FFP		;First page to use
	HRLZM B,JFNPAG(A)
	ADDM C,FFP		;Update first free page
	HRLI B,.FHSLF		;.FHSLF,,FFP as destination
	HRLZ A,A		;Put JFN,,0 as source address
	TXO C,PM%CNT!PM%RD!PM%PLD!PM%CPY
	PMAP%			;Map the pages
	IFJER.
	  POP P,A
	  HRROI A,[ASCIZ/Indirect file PMAP failed/]
	  JRST ERROR
	ENDIF.
	POP P,A			;get JFN back
	MOVE B,FFP
	SUBI B,1
	HRRM B,JFNPAG(A)	;Save last page used by thiss file
	HLRZ B,JFNPAG(A)	;First page
	LSH B,^D9		;Address
	HRLI B,(POINT 7)	;Make a byte pointer
	MOVEM B,JFNPTR(A)
	RETSKP

CLSTXT:	SAVEAC <C>
	CAIG A,MAXJFN		;Reasonable JFN?
	IFSKP.
	  HRROI A,[ASCIZ/Illegal JFN at OPNTXT/]
	  JRST ERROR
	ENDIF.
	PUSH P,A
	HLR B,JFNPAG(A)		;Starting page
	HRR C,JFNPAG(A)		;Ending page
	SUB C,B
	ADDI C,1		;Number of pages
	MOVE A,FFP
	SUB A,C
	MOVEM A,FFP		;save updated first free page.
	SETO A,
	HRLI B,.FHSLF
	PMAP%
	POP P,A
	CLOSF%
	 ERJMP R
	RETSKP
;;; Map binary file
MAPBIN:	CALL UMPBIN		;Toss out what might have been there before
	MOVX A,GJ%OLD!GJ%SHT!GJ%PHY ;Try to get binary file
	HRROI B,MLSBNM
	GTJFN%
	IFJER.
	  HRROI A,[ASCIZ/Cannot find mailing list binary file/]
	  RET
	ENDIF.
	PUSH P,A		;Save JFN
	MOVEI B,OF%RD		;Now try to open it
	OPENF%
	IFJER.
	  POP P,A		;Get back JFN
	  RLJFN%		;Flush it
	   NOP
	  HRROI A,[ASCIZ/Cannot open mailing list binary file/]
	  RET
	ENDIF.
	POP P,BINJFN		;Set BINJFN now that it's open
	MOVE B,[1,,.FBBYV]
	MOVEI C,BINSIZ
	GTFDB%
	HRLZ A,A
	MOVE B,[.FHSLF,,BINPAG]
	HRRZ C,BINSIZ
	HRLI C,(PM%CNT!PM%RD!PM%CPY)
	PMAP%
	MOVE A,BINFID
	CAMN A,[SIXBIT/MMLBX/]
	IFSKP.
	  CALL UMPBIN		;Unmap the sucker
	  HRROI A,[ASCIZ/Bad format binary file/]
	  RET
	ENDIF.
	MOVE A,BINJFN		;Return with JFN in A
	RETSKP

;; Unmap old binary
UMPBIN:	SETO A,			;Unmap old binary
	MOVE B,[.FHSLF,,BINPAG]
	HRRZ C,BINSIZ
	TXO C,PM%CNT
	PMAP%
	SKIPE A,BINJFN
	 CLOSF%
	  NOP
	SETZM BINJFN
	RET
;;; Read an address from the user into ADDRES
UREAD:	TMSG <Address: >
	HRROI A,ADDRES
	MOVE B,[RD%CRF+500]
	HRROI C,[ASCIZ/Address: /]
	RDTTY%
	IFJER.
	  HRROI B,[ASCIZ/RDTTY% failed/]
	  JRST JSYERR
	ENDIF.
	SETZ C,
	DPB C,A
	RET

; Here on JSYS error
JSYERR:	HRROI A,STRBF1		;Place for error string
	SETZ C,
	SOUT%
	HRROI B,[ASCIZ/ - /]
	SOUT%
	HRLOI B,.FHSLF
	ERSTR%
	 NOP
	 NOP 
IERR1:	HRROI A,STRBF1

;;; General error handler
ERROR:	MOVEM A,SUCCES		;Failure, give error message
	SKIPE HAVSUP
	IFSKP.
	  ESOUT%
	  TMSG <
>
	ENDIF.
	TXZN F,FL%CMP		;Compiling binary file?
	IFSKP.
	  MOVE P,CMPPDP		;Yes, restore stack as of compiling
	  CALL MAPBIN		;Map binary file back in
	   JRST DONE		;Can't, give up!
	  TMSG <[Using previous version of database]
>
	  RET			;A-okay
	ENDIF.
DONE:	HALTF%
	JRST GO

...LIT:	LIT

END <EVECL,,EVEC>
; Local Modes:
; Mode: MACRO
; Comment Start:;
; Comment Begin:;
; End: