Google
 

Trailing-Edge - PDP-10 Archives - bb-jr93g-bb - 7,6/ap018/ms.x18
There are 9 other files named ms.x18 in the archive. Click here to see a list.
;This software is furnished under a license and may only be used
;  or copied in accordance with the terms of such license.
;
;Copyright (C) 1979,1980,1981,1982 by Digital Equipment Corporation
;	       1983,1984,1985,1986    Maynard, Massachusetts, USA


	TITLE MS - Message System for TOPS10 and TOPS20

	SEARCH GLXMAC,MSUNV,MACSYM
TOPS20<	SEARCH MONSYM>
	PROLOG (MS)

	CPYRYT
	MSINIT

	.DIRECTIVE FLBLST
	SALL

IFNDEF MHACK,<MHACK==0>		; BLISS hack switch

;Define globals

	GLOBS			; Storage
	GLOBRS			; Routines

;Globals

	INTERNAL CKEXIT, CKXIT0, CKXRTN, CMDRES, GO, GO1, SUBJEC
	INTERNAL CHECKT,CHKDEL
TOPS20<	INTERNAL CHECKM>
	INTERNAL DELMSG,FNDCC,FNDDAT,FNDFRM,FNDMID,FNDREF,FNDRRR,FNDSDT
;**;[3096] Change 1 line at INTERNAL FNDSND,...		Ned	12-Aug-87
	INTERNAL FNDSND,FNDSUB,FNDTO,RECEN0,FSCPKL

	INTERNAL CRFPRT,ENQBLK,FILPGM,FILPGS,FILSIZ,FILWRT,FLAGS2,NETFLG
	INTERNAL HLPTXT,IDXNUM,IDXSAV,LASTRD,MBOT,MSGFAD,MSGIFJ,MSGJF2
	INTERNAL MSGPAG,MSGSSQ,MTOP,OUTFOB,WBOT,WTOP,WWIDH
	INTERNAL MINWSZ,SCRLFL,BLKTIM
	INTERNAL ABREL,ADRLST,AUTCMT,AUTEXP,BLKTIM
	INTERNAL CMDTAB,CRFDEV
	INTERNAL CRFDIR,CRFPRT,DEFCC,DFCMTB,EXPRNC,F%NO,FILPGS,FILSIZ
	INTERNAL FLAGS2,GTFLAG,HD%KWD,HDITAB,HDTNAM,HLPTAB,HLPTXT
	INTERNAL INIFOB,INIIFN,KWDLST,LIBVER
	INTERNAL MOVDSP,MOVHDO,MVALST,NDELET,NFLAGD,OHSNMX,PERSON,RCMDTB
	INTERNAL REDNOV,REDPTR,REPADD
	INTERNAL SCMDTB,SCRBTM,SCRLFL,SCRREG,SCRRGR,SENNOV,SENPTR,SETNOV
	INTERNAL SETPTR,SHCMTB,STCMT0,STCMTB,STRBSZ,SVMFOB,TOPNOV,TOPPTR
	INTERNAL TYPE1,UNSEEN,V52FLG
	INTERNAL AUTEXP,CHECKS,CMDLUP,CRFPRT,FLAGS2,HDITAB
	INTERNAL IB,INIRET,LINEW,OKTINT,PDL,PIDBLK,PIDMS
	INTERNAL SAVMOD,TAKPDL,TAKPTR,ZEREND,ZERMEM
	INTERNAL CRFDEV, CRFDIR, INIP, INIRET, LFCNT, INIPDL
	INTERNAL MYDIR, MYDIRS, PERSON, MSGSSQ, MSGSQE
	INTERNAL RPRHNP,FRMLCL,TTXTIB,FSJFN,MYHSPT,MYHDPT,CRFDTB

TOPS10<	INTERNAL MYPPN, FILBLK, SENBLK,CRFFDH
	INTERNAL OBUF,INTBLK,INTF,MYPPN,SAVPSZ,SENBLK,TTYUDX
	INTERNAL FILCRV,FILOPB,LKB,MSGA1,MSGSTR,MSIOWD,PBLOCK,MSGFD
	INTERNAL ATTBLK,LOKAPP >

TOPS20<	INTERNAL CHNSIZ,CHNTAB,GTJFN2,IDXFIL,INTP,INTPDL,JF2BUF >

;Global routines defined elsewhere

;MSFIL.MAC
	EXTERNAL CHECK0,CLOSEF,EXPUNG,GET1,GETFIL,GETHLP,GETLPT
TOPS20< EXTERNAL CLSJF2,GETJF2,SIZFIL>
	EXTERNAL GETNEW,GETOUT,GETPRS,LPTMSG,MOVMSG,PARSEF,PUTMSG
	EXTERNAL REMAP,SETREF,SETSFL,SHRAGN
TOPS20<	EXTERNAL UNMAPF>
	EXTERNAL UPDBIT,XCLENQ

;MSMCMD.MAC
	EXTERNAL BLANK0,.BBORD,.BLANK,.CREAT,.ECHO,.FINIS,.HELP
TOPS20<	EXTERNAL .LOGOU,.MAILR>
	EXTERNAL .PUSH,.QUINI,.QUIT,.SET,.SHADL,.SHDEF,.SHHDI
	EXTERNAL .SHINT,.SHOW,.SHSYN,.STATU,.STAUF,.STAUT,.STBFD
	EXTERNAL .STCDI,.STCLZ,.STCNC,.STCND,.DAYTI
	EXTERNAL .STCPR,.STDCC,.STDFT,.STDLC,.STEXP
	EXTERNAL .STFDI
	EXTERNAL .STHLP,.STHPR
	EXTERNAL .STINC,.STLGD,.STNO,.STOHS,.STPNM,.STRAD,.STRPA,.STRPS
	EXTERNAL .STSPH,.STSUM,.STVID,.STWSZ,.TAKE,.VERSI,MSGOD0,.MSGOD

;MSCNFG.MAC
TOPS20<	EXTERNAL CTLCIN,TMRINT>
	EXTERNAL CTCLOK,CTCOK,KWDREL,MSINI
	EXTERNAL SUMMRY,TTINI

;MSDLVR.MAC
	EXTERNAL DELIVR, SAVMSG, SAVDRF

;MSDSPL.MAC
	EXTERNAL TYPBOD, TYPHDR, TYPLIT, TYPMHD

;MSGUSR.MAC
TOPS20<	EXTERNAL CHKPBX>

;MSHTAB.MAC
	EXTERNAL NAMINI, HOSTAB, HSTINI
;MSTXT.MAC
TOPS10< EXTERNAL CTX >
TOPS20<	EXTERNAL .EDITO >
	EXTERNAL GETTXT, .ERST0, .EDTXT
	EXTERNAL TXTCHR, TXTCPT, TXTPUT

;MSUTL.MAC
	EXTERNAL ALCFOB, ALCSB, CFIELD, CLRCTO, CLRFIB, CMDERR, CMDER1
	EXTERNAL CMDINI, COMPAC, COUNTS, CPYATM, CRIF, CRLF, DPROMP
TOPS10<	EXTERNAL ECHOON >
;**;[3096] Change 1 line at EXTERNAL EXPAND,...		Ned	12-Aug-87
	EXTERNAL EXPAND, FSCOPY, FSPEC, FSPEC0, GETUSR, UNGGNU, FENTRM
	EXTERNAL MOVST0, MOVST1, MOVST2, MOVSTR
	EXTERNAL RELFOB, RELSB, REPARS
	EXTERNAL RFIELD, RFLDE, RSTPSZ
TOPS20<	EXTERNAL RUNFIL, RUNFL0, RUNFL2 >
	EXTERNAL SETIOJ, SETPSZ, SSEARC, TBADDS, TBOUT, TNOUT, TSOUT
	EXTERNAL TXTOUT, UPDTOR
	EXTERNAL RDELAY
TOPS10<	EXTERNAL XDATI >

;MSUUO.MAC
	EXTERNAL UUOH

;Global data items defined elsewhere

;MSGUSR.MAC
TOPS10<	EXTERNAL KILLST >

;MSHTAB.MAC
	EXTERNAL VALID8

;MSUTL.MAC
	EXTERNAL ATMBUF, CJFNBK, CMDBUF, CMDACS
	EXTERNAL LSCERR, REPAR0, REPARA, SBK
;MSCNFG
	EXTERNAL RJ.FLG, RJ.VMA, RJ.AMA

 SUBTTL Impure storage

	IMPUR0

ZERMEM:				; Begin clear here
MSQBOT:	BLOCK 1			; Sequence frame bottom
MSQTOP:	BLOCK 1			; And Top
MMPPG:	BLOCK 1			; Index file page number
INIP:	BLOCK 1			; Saved P during init file
INIRET:	BLOCK 1			; Where to go when init file exhausted
INIPDL:	BLOCK 40		; Saved stack during init file
OKTINT:	BLOCK 1			; Is it ok for timer to interrupt now?
V52FLG:	BLOCK 1			; We are on a vt52
GTFLAG:	BLOCK 1			; "No messages in file" message flag
LSTCHR:	BLOCK 1			; Place to stash last char typed
CPYJFN:	BLOCK 1			; JFN for MAIL.CPY
FSJFN::	BLOCK 1			; Temporary JFN storage
FLAGS2:	BLOCK 1			; Second flags word
;**;[3096] Delete (move) one line at FLAGS2: + 1	Ned	12-Aug-87
TOPS20<
MSGJF2:	EXP 0 			; JFN to open for write
GTJFN2:	EXP 0			; READ/WRITE JFN for GET command
CHNSIZ:	EXP 0>			; File has changed size flag
;**;[3096] Delete (move) 2 lines at CHNSIZ: + 1		Ned	12-Aug-87
MSGIDP:	BLOCK 1			; Its size in pages
MSGSQL:	BLOCK 1			; Sequence buffer size in pages

   TOPS10<
MSGSTR:	BLOCK 1			; Structure for message file
LKB:	BLOCK .RBTIM+1		; Extended LOOKUP/ENTER block
PBLOCK:	BLOCK 10		; Path block
FILOPB:	BLOCK .FOPPN+1		; FILOP. block
SAVPSZ:	BLOCK 1			; Saved TTY page size
MYPPN:	BLOCK 1
OBUF:	BLOCK 3			; Output buffer headers
FILBLK:	BLOCK .FOPPN+1		; FILOP block for queued network mail
SENBLK:	BLOCK 10		;
ASCNOD:	BLOCK 5			; Storage for ASCII8 node name
LOKAPP:	BLOCK	1		;Level counter for getting append lock
   >;End TOPS10

OUTIFN:	BLOCK 1			; Output file IFN
OUTFOB:	BLOCK 2			; Output file FOB size and length
SAVMOD:	BLOCK 5			; Normal tty modes
LASTM:	BLOCK 1			; Number of messages in current file
FILPGM:	BLOCK 1			; Number of mapped pages for reading
FILPGS:	BLOCK 1			; Size of the file in pages
FILSIZ:	BLOCK 1			; Size of the file (bytes)
FILCRV:	BLOCK 1			; Creation date
FILWRT:	BLOCK 1			; Write date
LASTRD:	BLOCK 1			; Last read date of file
UNSEEN:	BLOCK 1			; Number of unseen messages
NDELET:	BLOCK 1			; Number of deleted messages
NFLAGD:	BLOCK 1			; Number of flagged messages
LASTN:	BLOCK 1			; Saved last number for pluralizing
DOMSG:	BLOCK 2			; Dispatch to process next message
HLPTXT:	BLOCK 1			; Pointer to text from help file
PSIPC:	BLOCK 1			; Saved pc from psi routine (level 3)
ILIPC:	BLOCK 1			; Saved pc from psi routine (level 2)
CTLCPC:	BLOCK 1			; Saved pc from psi routine (level 1)
TOPTRS:	BLOCK 1			; CC,,TO list pointers
RPRHNP:	BLOCK 1			; REPAIR flag
TRYSND:	BLOCK 1			; Use SENDER in REPLY (no FROM/REPLY-TO)
FRMLCL:	BLOCK 1			; From MSLCL or from SAVE OUTGOING-MESSAGES
DEFCC:	BLOCK 1			; Ptr to default cc list
NAMTAB:	BLOCK 1			; (Pointer to) name table
FRENAM:	BLOCK 1			; Pointer to free space for names
SV.TOP:	BLOCK 1			; Saved TOPTRS (for reparsing address lists)
SV.FNM:	BLOCK 1			; Saved FRENAM (ditto)
SV.NTB:	BLOCK 1			; Saved NAMTAB (ditto)
MOVDSP:	BLOCK 1			; Dispatch for typing or setting to, etc
REPLIN:	BLOCK ^D50		; Reply lines (In-reply-to and Reference)
SAVEL:	BLOCK 1			; Saved L (msg sequence pointer)
TTYUDX:	BLOCK 1			; Terminal UDX
LINEW:	BLOCK 1			; Terminal line width
REDLVL:	BLOCK 1			; Recursive read level depth
FILCOL:	BLOCK 1			; Fill column for auto-fill mode
TAKPDL:	BLOCK TAKPTN		; Stack for take file IFNs and FOBs
TAKPTR:	BLOCK 1			; Stack pointer
SVMFOB:	BLOCK 2			; Saved messages FOB size and address
SVMIFN:	BLOCK 1			; Saved messages IFN
INIIFN:	BLOCK 1			; IFN of init file being created
INIFOB:	BLOCK 2			; FOB size and addr of init file being created
SUBJEC:	BLOCK 1			; Subject field
AUTEXP:	BLOCK 1			; Magic number which controls auto-expunges
SVABLK:	BLOCK 1			; Saved A-block for GETUSR
UPDPTR:	BLOCK 1			; Updated byte pointer returned by TORs
UPDX:	BLOCK 1			; Updated X (horizontal position) for TORs
OBPTR:	BLOCK 1			; Output byte pointer (partly replaces AC O)
MSGID0:	BLOCK 1			; Date/time to compose msg id with
MSGID1:	BLOCK 1			; Job number for same
MSGID2:	BLOCK 1			; PPN or usernumber for same
MSGID3:	BLOCK 1			; Runtime in msec. for same
LDEPTH:	BLOCK 1			; Address list depth
WTOP::	BLOCK 1			; File window top address
WBOT::	BLOCK 1			; File window bottom address
MSGPGS:	BLOCK 1			; Pages allocated for message file
CNCLHD:	BLOCK 1			; Pointer to TBLUK table of suppressed headers
SCRREG:	BLOCK 1			; Ptr to routine to set scroll region
SCRBTM:	BLOCK 1			; Ptr to routine to undo scroll region and
				;  go to bottom line of screen
SCRRGR:	BLOCK 1			; Ptr to routine to do the reverse
BLKTIM:	BLOCK 1			; Universal date/time before which clear-screen
				;  not allowed (error message would vanish)
LFCNT:	BLOCK 1			; Line feed counter
MINWSZ:	BLOCK 1			; Minimum text window size
SCRLFL:	BLOCK 1			; Screen parameters need resetting flag
ABLHED:	BLOCK 1			; OWN storage for ADRLST reparse code
LCNT:	BLOCK 1			; Number of msgs in current message sequence
TOPPTR:	BLOCK 1			; Pointer to command table, top level
REDPTR:	BLOCK 1			; Pointer to command table, read level
SENPTR:	BLOCK 1			; Pointer to command table, send level
SETPTR:	BLOCK 1			; Pointer to command table, set commands
EXPRNC:	BLOCK 1			; Experience level, controls preceding 4 vars

   TOPS10<
MSIOWD:	BLOCK 2			; IOWD command list
MSGFD:	BLOCK FDXSIZ		; FD for message file
   >;End TOPS10
HLPTAB:	BLOCK 1			; Pointer to help topic table
HDITAB:	BLOCK 1			; Pointer to header-item table
KWDTBL:	BLOCK 1			; Pointer to alias/address list table
REPADD:	BLOCK 1			; Pointer to A-block list for reply-address
PERSON:	BLOCK 1			; Ptr to personal-name string
CLZTXT:	BLOCK 1			; Ptr to S-block for closing text

FLG:	BLOCK 1			; Headers Flags
HDIO:	BLOCK 1			;
HDI1:	BLOCK	1
FLAGS:	BLOCK	1
TENT1:	BLOCK 1
HBLKP:	BLOCK 1

OHSNMX==^D32			; Max no. headers to exclusively show
OHSN:	BLOCK 1			; Number of only-shown headers
OHSPTR:	BLOCK OHSNMX		; length of hdr name,,word addr of name string

;.JBINT block for trapping ctrl-C on TOPS10

   TOPS10<
INTF:	BLOCK 1			; -1 means interrupts not in progress
INTBLK:	BLOCK 3
   >;End TOPS10

CRFDEV:	BLOCK 2			; Created-files device
TOPS10<
CRFFDH:	BLOCK	FDMSIZ-1	;DUMMY HEADER FOR TOPS-10 PATH TYPEOUT
				;MUST BE JUST BEFORE CRFDIR
>
CRFDIR:	BLOCK 10		; Created-files directory
CRFPRT:	BLOCK 2			; Created-files protection
ZEREND:	0			; End of clear
UUOACS:	BLOCK 20		; Ac's during LUUO call
INTACS:	BLOCK 20		; During timer interrupt routines

PDL:	BLOCK NPDL		; Pushdown list

TOPS20<
INTP:	BLOCK 1			; Saved P during interrupt
INTPDL:	BLOCK NPDL		; Interrupt pushdown list
>;END TOPS20

STRBSZ==40
SAVF:	BLOCK 1
;**;[3096] Delete (move) 2 lines at SAVF: + 1	Ned	12-Aug-87

TOPS20<
IDXFIL: BLOCK ^D40		; Place to keep index file name
>;END TOPS20

IDXNUM:	BLOCK	1		; TEMP for GTMIND
IDXSAV:	BLOCK	3		; TEMP for GTMIND
;**;[3096] Delete (move) 3 lines at IDXSAV: + 1		Ned	12-Aug-87
STRBUF:	BLOCK STRBSZ		; Temporary string space
TOPS20<
JF2BUF:	BLOCK STRBSZ		; Temporary string space for GET command
MYSTR::	BLOCK 2			; Keep STR: here
>
MYDIR:	BLOCK 1			; Login directory
MYDIRS:	BLOCK 10		; ASCII of login directory
LIBVER:	BLOCK 1			; Place to keep GLXLIB Version number
TRANFG:	BLOCK 1			; Flags from last nodename done by TRANSH

;**;[3096] Delete (move) 13 lines at ATTBLK: - 1	Ned	12-Aug-87

SAB::	BLOCK 5			; SEND ARGUMENT BLOCK

Z.DRFB:!			;BEGINING OF BLOCK TO ZERO
DRFFOB:	BLOCK	FOB.MZ		;FOB OF DRF FILE
DRFFD:	BLOCK	FDXSIZ		;FD BLOCK OF DRF FILE
Z.DRFE:!			;END OF BLOCK TO ZERO

SUBTTL Impure storage inited nonzero

   TOPS10<
	IMPUR0
	..NZLO==:.		; Lowseg origin of nonzero-inited stuff
	PURE			; Make pure copy
	..NZHO==:.		; At this address
	PHASE ..NZLO		; But make like in low seg
	..NZT==:.		; For computing length of this stuff
   >;End TOPS10

CPYRIT::ASCIZ /COPYRIGHT (C) DIGITAL EQUIPMENT CORPORATION 1982,1983,1984/

;**;[3096] Insert (move to) 31 lines at CPYRIT:		Ned	12-Aug-87
TOPS10	<
ATTBLK:	EXP	3		;[3096][CJA] GALAXY file attributes (3 words total)
	XWD	1,.FIPRO	;[3096][CJA] Might specify protection
	EXP	CRFPRT		;[3096][CJA] Address of default file protection
>

NETFLG:	EXP	RJ.FLG		;[3096]
PIDBLK::PB.MNS,,0		;[3096] LENGTH OF THE PID BLOCK
	EXP 0			;[3096] PID (FILLED IN BY GLXLIB)
	IP.RSE			;[3096] RETURN TO CALLER IF SEND FAILS
	EXP 0			;[3096] NO INTERRUPT FOR IPCF
	EXP 0			;[3096] DON'T SET IPCF RECEIVE/SEND QUOTAS

MSGIFJ:	EXP -1			;[3096]
MSGFAD::EXP MSGA1               ;[3096] Address of beginning of message file
MSGPAG:	EXP MSGA1/1000		;[3096] Page of beginning of message file

MBOT:	EXP 0			;[3096] First message number in index section
MTOP:	EXP 777			;[3096] Last message number in index section
MSGJFN:	EXP -1 			;[3096] JFN for current message file

TOPS10<
MSGJF2: EXP -1 >		;[3096] JFN to open for write

FSCPKL:	0			;[3096] a LSHC A,<n> for SHIFT-IN goes here
	LSH A,1			;[3096]
	MOVEM A,(C)		;[3096] Address of dest stored in RH here
	0			;[3096] a LSHC A,<n> for SHIFT-OUT goes here
	MOVE B,(C)		;[3096] Address of source stored in RH here
	AOBJN C,FSCPKL		;[3096]
	JRST @FENTRM(D)		;[3096]

;PIDS THAT ARE NEEDED

PIDMS::	0			; PID of MS
PIDMX::	0			; PID of MX

MYHSPT:	POINT 7,MYHNAM,6
MYHNAM:	ASCIZ /@/
	BLOCK 17		; ARPANET host name

MYHDPT:	POINT 7,MYHDEC,6
MYHDEC:	ASCIZ /@/
	BLOCK 17		; DECNET host name

;TEXTI argument block

TTXTIB:	7			; .RDCWB - count
	RD%JFN			; .RDFLG - flags
	.PRIIN,,.PRIOU		; .RDIOJ - JFNs
TXTPTR:	0			; .RDDBP - destination byte pointer
TXTCNT:	0			; .RDDBC - destination byte count
	0			; .RDBFP - buffer pointer
	0			; .RDRTY - prompt string pointer
	TXTMS2			; .RDBRK - break table for text

; Texti break mask for user input

TXTMS2:
	110100001400		; ^B, ^E, ^K, ^Z, ESC
	000000000000
	000000000000
	000000000000

   TOPS20<
EDTGJB:	EXP GJ%OLD		; GTJFN block to default editor type to .EXE
	XWD .NULIO,.NULIO
	-1,,[ASCIZ /SYS:/]
	0
	-1,,[ASCIZ /EDIT/]
	-1,,[ASCIZ /EXE/]
	EXP 0
	EXP 0
	EXP 0

;**;[3074][3075] Add 9 lines at EMXGJB:+0L	MDR	14-AUG-86
EMXGJB:	EXP GJ%OLD		;[3074][3075] GTJFN to use the right EMACS.EXE
	XWD .NULIO,.NULIO	;[3074][3075]
	-1,,[ASCIZ /SYS:/]	;[3074][3075]
	0			;[3074][3075]
	-1,,[ASCIZ /EMACS/]	;[3074][3075]
	-1,,[ASCIZ /EXE/]	;[3074][3075]
	EXP 0			;[3074][3075]
	EXP 0			;[3074][3075]
	EXP 0			;[3074][3075]
   >;End TOPS20

;ENQ block for expunge interlock
; CAUTION -- offsets assumed to be the same on TOPS10 and TOPS20

ENQBLK:	1,,6			; Number of locks,,length of block
	NQID			; Magic number
	0			; Bits,,JFN
	POINT 7,[ASCIZ /Mail expunge interlock/]	; Name of lock
	0			; Unused fields
	0

   TOPS10<
;ENQ block for append interlock -- TOPS10 only
; Needed since TOPS10 screws up if two simultaneous appenders

APPQID==23456			; Magic number defined
APPBLK:	1,,6			; Number of locks,,length of block
	APPQID			; Magic number
	0			; Bits, channel number
	POINT 7,[ASCIZ /Mail append interlock/]
	0
	0			; Unused fields
   >;End TOPS10


;Trailer added to end of queued mail and saved mail

TRAILR:	ASCIZ /   --------
/


; Interrupt storage

   TOPS20<
LEVTAB:	CTLCPC
	ILIPC
	PSIPC


CHNTAB:	1,,CTLCIN		; 0 - ctrl-C
	EXP 0			; 1
	EXP 0			; 2
	EXP 0			; 3
	EXP 0			; 4
	3,,TMRINT		; 5 - timer interrupt
	XLIST			; Nothing else
	REPEAT ^D30,<EXP 0>	;  ..
	LIST

   >;End TOPS20

   TOPS10<
	..NZT==.-..NZT		; Compute number of words in nonzero lowseg
	DEPHASE			; Back to normality please
	IMPUR0			; Allocate space for this stuff
	BLOCK ..NZT		;  ..
   >;End TOPS10
SUBTTL High segment -- sharable data

	PURE

;GLXLIB initialization block

IB:	EXP 0			; Default everything except interrupt vectors
TOPS10<	EXP IT.OCT!IB.NPF >	; Open controlling terminal, no pfh
TOPS20<	EXP 0 >
TOPS20<	LEVTAB,,CHNTAB >
TOPS10<	EXP 0 >
	EXP PIDBLK		; Address of the PID Block
	EXP 0
	SIXBIT /MS/		; Program name


;Help message for host name parsing

HSTHLP:	ASCIZ /host name/

SUBTTL Page allocation


DEFPAG HDRPAG,10		; Header of msg currently being composed
DEFPAG TCPAG,NTCPAG		; TO/CC lists
DEFPAG NAMTXT,10		; Name strings for above lists

TOPS10<WWIDHN=^D10>		; Default to ten pages for mail file window
TOPS20<WWIDHN=^D100>		; Or one hundred on TOPS-20

WWIDH: EXP WWIDHN		; Number of pages for the mail window
DEFPAG MSGA1,WWIDHN		; Window into the mail file
DEFPAG MSGIDX,MSGIDN		; Window into the index file (if needed)
MSGSQN==^D10			; Buffer for sequences
DEFPAG MSGSSQ,MSGSQN		; ...
MSGSQE==MSGSSQ+MSGSQN*1000-1


 SUBTTL Main program

GO:	MOVX F,F%FDIR		; Clear flags, but light Force-directory-look
GO0:	MOVE P,[IOWD NPDL,PDL]
	RESET
   TOPS20<
	MOVEI A,<<TOPPAG+777>/1000>*1000
	MOVEM A,.JBFF##		; Protect our pages from GLXMEM
   >
	CALL MSINI 		; Initialize everything
	TXNE F,F%AMOD		; Auto mod feature?
	JRST MSGOD0		; Yes - enuf init for now

GO3:				;See if command on line which invoked us
   TOPS20<
	SETZ A,
	RSCAN			; check for command
	 ERJMP GO2		; None
	JUMPE A,GO2		; If char count zero, no cmd
   >;End TOPS20
   TOPS10<
	RESCAN 1		; See if anything there
	 SKIPA			; Could be...
	  JRST GO2		; Nothing, skip all this
	MOVX A,.NULIO		; Turn off GLXLIB echoing so users
	HRRM A,SBK+.CMIOJ	;  don't see command twice
   >;End TOPS10
	HRROI A,[0]		; Dummy ^R pointer
	MOVEM A,SBK+.CMRTY
	MOVEI A,GO4+1		; For reparse on error
	MOVEM A,REPARA		;  fake out return addrs.
	MOVEI A,[FLDDB. .CMINI]	; Init COMND
	CALL RFIELD
	MOVEI A,[FLDDB. (.CMKEY,,<[2,,2
				     [ASCIZ /MAIL/],,0
				     [ASCIZ /MS/],,0]>)]
	CALL RFLDE		; See if program name
	 JRST GO2		; Clean up and try normal case
	MOVEI A,[FLDDB. .CMCFM]	; Maybe just MS<CR>
	CALL RFLDE
	 JRST [	TXO F,F%RSCN		;  mark as exec command
		PUSH P,[CMDRES]		; Dummy return in case EOF on cmd input
		MOVEM P,CMDACS+P	;  insure stack doesn't disappear
		JRST CMDLLP]		;  and try command parse

	; ..
	; ..

GO2:
   TOPS20<
	HRROI A,[0]		; Clear rescan
	RSCAN
	 ERJMP .+1
   >;End TOPS20
   TOPS10<
	MOVX A,.PRIOU		; Turn echoing back on
	HRRM A,SBK+.CMIOJ	;  ..
   >;End TOPS10
	SKIPG MSGJFN		; Already have message file?
	CALL GETFIL		; No, get and parse one
	SKIPG MSGJFN		; Have we found something?
	JRST CMDRES		; No - message already printed
	CALL RECENT		; Show data on recent messages
	TXNN F,F%NSUM		; "Set no type-initial-summary"?
	CALL SUMMRY		; No, type summary of the files contents
	JRST CMDRES		; Enter main loop

; Auto message of the day hack

GO1:	MOVX F,F%AMOD		; Set CONgs
	JRST GO0		;  and join common code

;Handle initial command error

GO4:	CALL CLRFIB		; Clear typeahead
	JRST CKEXIT		; Just quit
CMDRES::MOVE P,[IOWD NPDL,PDL]
	PUSH P,[CMDRES]		; Dummy return in case EOF on .PRIOU
	CALL CMDINI		; Init command parser
CMDLUP:	MOVE T,TAKPTR		; See if inside command file
	HRRZ A,(T)		; Get current COMND input IFN
	CAIE A,.PRIIN		; Command file or TTY?
	JRST CMDLP0		;  file...
	TXZE F,F%RSCN		; Exec command?
	JRST [	MOVE A,AUTEXP		; Yes, auto-expunge always?
		CAIE A,1		;  ..
		JRST .EXIT1		; No, just close file and quit
		SKIPLE MSGJFN		; If we have a message file,
		CALL EXPUNG		; Expunge it
		JRST .EXIT1]		; Now close file and quit
CMDLP0:	SKIPE INIIFN		; Creating init file?
	JRST [	PROMPT (MS Create-init>>)
		JRST CMDLLP]		; Yes, different prompt
	PROMPT (MS>)
	MOVX A,F2%NSV		; Reset "suppress save" bit
	ANDCAM A,FLAGS2		;  ..
	HRRZ A,(T)		; Get current COMND input IFN
	CAIE A,.PRIIN		; File or TTY?
	JRST CMDLLP		; File, DON'T call slow routines like CHECK0!
	CALL CHECK0		; Check for new messages
	 JRST CMDLLP		; None - go on
	CALL CHECKS		; Got some - print headers
	JRST CMDLUP		; Re-prompt
	; ..
	; ..

CMDLLP:	MOVE A,TOPPTR		; Get pointer to command table
	SKIPE INIIFN		; Creating defaults file (init file)?
	MOVEI A,[FLDDB. (.CMKEY,,CINTAB)]	; Yes, choose cmd subset
	SETOM OKTINT		; OK for timer interrupt here
	TXZ F,F%VBTY		; Default is not verbose-type
	CALL RFIELD		; Read command
	SETZM OKTINT		; No more though
	HRRZ B,(B)		; Get entry
	MOVE B,(B)		; addr of routine
	PUSH P,B		; Save it
	SKIPG MSGJFN		; Have message file?
	TXNN B,C%GETF		; No - need to get message file?
	 SKIPA			;  Already have it or dont't need it
	CALL GETFIL		; Yes - get it
	HRRZ A,0(P)		; Command dispatch address
	CALL (A)		; Do the command
	POP P,A			; Restore dispatch word
	HRRZS A			; Only check significant part
	CAIE A,.TAKE		; Take command?
	CAIN A,.CREAT		;  or create-init command?
	JRST CMDLUP		; Yes, don't put it into init file!
	CAIN A,.HELP		; Also don't put help into init file
	JRST CMDLUP		;  ..
	MOVE C,[POINT 7,CMDBUF]	; Point to cmd in case it needs writing
	SKIPE A,INIIFN		; Creating init file?
	JRST [	ILDB B,C		; Yes, get next byte
		JUMPE B,CMDLUP		; Done, fetch next cmd
		$CALL F%OBYT		; Write to init file
		JRST .-1]		; Repeat for all bytes in cmd
	TXZN F,F%ESND		; Want to send something?
	JRST CMDLUP		; No - keep going
	SETZM LSTCHR		; Yes - invoke sender
	CALL ERSAL1		; Erase all but text
	CALL SEND0
	JRST CMDLUP		; And return to command loop
 SUBTTL Command tables

;Caution -- the CMD1 macro generates a reference to a label formed by
; preceding the command name with a dot.  This does not work, however, for
; command names containing hyphens.  For these commands, the CMDT macro,
; which requires an explicit label, must be used.

; Top level commands

TOPNOV:	NOVN,,NOVN		; Novice-mode commands
TOPS10<	CMDT (\"32,.EXIT0,CM%INV) >;Blue toads like ctrl-Z
	CMDT (BBoard,.BBORD,CM%INV)
	CMDT (Delete,,,C%GETF)
	CMDT (Directory,.HEADE,,C%GETF)
	CMD1 (Ex,ENTXXT,CM%ABR!CM%INV)
ENTXXT:	CMDT (Exit)
	CMDT (Expunge,,,C%GETF)
	CMDT (File,,,C%GETF)
	CMDT (Headers,.HEADE,,C%GETF)
	CMDT (Help)
TOPS20<	CMDT (Net-mail,.MAILR,CM%INV) >
	CMDT (Print,.LIST,,C%GETF)
	CMDT (Read,,,C%GETF)
	CMDT (Send,,,C%GETF)
	CMDT (Set)
	CMDT (Summarize,.HEADE,CM%INV,C%GETF)
	CMDT (System-messages,.MSGOD)
	CMDT (Undelete,,,C%GETF)
NOVN==.-TOPNOV-1

CMDTAB:	NCMDS,,NCMDS
TOPS10<	CMDT (\"32,.EXIT0,CM%INV) >;Blue toads again
	CMDT (Answer,.REPLY,CM%INV,C%GETF)	; Synonym for Reply
;**;[3094]  Replace 2 lines with 3 at CMDTAB+3.		NED	12 May 87
	CMD1 (B,ENTBLK,CM%ABR!CM%INV)		;[3094] Blank not BBoard
	CMDT (BBoard,.BBORD,CM%INV)
ENTBLK:	CMDT (Blank)				;[3094]
	CMDT (Check,,,C%GETF)
	CMDT (Copy,.PUT,,C%GETF)
	CMDT (Create-init-file,.CREAT)
	CMD1 (D,ENTDEL,CM%ABR!CM%INV)
	CMDT (Daytime)
	CMDT (Define)
ENTDEL:	CMDT (Delete,,,C%GETF)
	CMDT (Directory,.HEADE,,C%GETF)
	CMDT (Echo,,CM%INV)
TOPS20<	CMDT (EMACS,.EDITO,CM%INV) >
	CMD1 (Ex,ENTXIT,CM%ABR!CM%INV)
ENTXIT:	CMDT (Exit)
	CMDT (Expunge,,,C%GETF)
	CMDT (File,,,C%GETF)
	CMDT (Flag,,,C%GETF)
	CMDT (Forward,,,C%GETF)
	CMDT (Get)
	CMD1 (H,ENTHDR,CM%ABR!CM%INV)
ENTHDR:	CMDT (Headers,.HEADE,,C%GETF)
	CMDT (Help)
	CMDT (List,,CM%INV,C%GETF)
	CMDT (Mark,,,C%GETF)
	CMDT (Move,,,C%GETF)
	CMD1 (N,ENTNXT,CM%ABR!CM%INV)
TOPS20<	CMDT (Net-mail,.MAILR,CM%INV) >
ENTNXT:	CMDT (Next,,,C%GETF)
	CMDT (Print,.LIST,,C%GETF)
	CMDT (Push)
	CMDT (Quit)
	CMD1 (R,ENTRED,CM%ABR!CM%INV)
ENTRED:	CMDT (Read,,,C%GETF)
	CMDT (Redistribute,,CM%INV,C%GETF)
	CMD1 (Rep,ENTRP1,CM%ABR!CM%INV)
	CMDT (Repair)
ENTRP1:	CMDT (Reply,,,C%GETF)
	CMDT (Retrieve)
	CMD1 (S,ENTSND,CM%ABR!CM%INV)
	CMD1 (Sa,ENTSAV,CM%ABR!CM%INV)
	CMD1 (Sav,ENTSAV,CM%ABR!CM%INV)
ENTSAV:	CMDT (Save,.SAVTL)	; Top-level save command
	CMDT (Save-outgoing-messages,.SAVMS,CM%INV)
ENTSND:	CMDT (Send)
	CMDT (Set)
	CMDT (Show)
	CMDT (Skim)
	CMDT (SSend,.XSEND,CM%INV)
	CMDT (Status,.STATU,CM%INV,C%GETF)
	CMDT (Summarize,.HEADE,CM%INV,C%GETF)
	CMDT (System-messages,.MSGOD)
	CMD1 (T,ENTTYP,CM%ABR!CM%INV)
	CMDT (Take)
ENTTYP:	CMDT (Type,,,C%GETF)
	CMDT (Undelete,,,C%GETF)
	CMDT (Unflag,,,C%GETF)
	CMDT (Unmark,,,C%GETF)
	CMDT (Verbose-type,,,C%GETF)
	CMDT (ZSend,.ZSEND,CM%INV)
NCMDS==.-CMDTAB-1
;Commands available in create-init mode

CINTAB:	NINCMD,,NINCMD
	CMDT (Blank)
	CMDT (Check,,,C%GETF)
	CMD1 (D,ENIDEL,CM%ABR!CM%INV)
	CMDT (Daytime)
	CMDT (Define)
ENIDEL:	CMDT (Delete,,,C%GETF)
	CMDT (Directory,.HEADE,,C%GETF)
	CMDT (Echo,,CM%INV)
TOPS20<	CMDT (EMACS,.EDITO,CM%INV) >
	CMDT (Expunge,,,C%GETF)
	CMDT (Finish)
	CMDT (Flag,,,C%GETF)
	CMDT (Get)
	CMDT (Headers,.HEADE,,C%GETF)
	CMDT (Mark,,,C%GETF)
	CMD1 (N,ENINXT,CM%ABR!CM%INV)
TOPS20<	CMDT (Net-mail,.MAILR,CM%INV) >
ENINXT:	CMDT (Next,,,C%GETF)
	CMDT (Print,.LIST,,C%GETF)
	CMDT (Push)
	CMDT (Quit,.QUINI)
	CMD1 (R,ENIRED,CM%ABR!CM%INV)
ENIRED:	CMDT (Read,,,C%GETF)
	CMD1 (Rep,ENIRP1,CM%ABR!CM%INV)
ENIRP1:	CMDT (Reply,,,C%GETF)
	CMDT (Save,.SAVTL)
	CMDT (Set)
	CMDT (Show)
	CMDT (Skim)
	CMDT (Status,.STATU,CM%INV,C%GETF)
	CMDT (Summarize,.HEADE,CM%INV,C%GETF)
	CMDT (System-messages,.MSGOD)
	CMD1 (T,ENITYP,CM%ABR!CM%INV)
	CMDT (Take)
ENITYP:	CMDT (Type,,,C%GETF)
	CMDT (Undelete,,,C%GETF)
	CMDT (Unflag,,,C%GETF)
	CMDT (Unmark,,,C%GETF)
	CMDT (Verbose-type,,,C%GETF)
NINCMD==.-CINTAB-1
; Read commands

REDNOV:	NRNOV,,NRNOV		; Novice-mode read-level commands
	CMD (Answer,.RRPL1,CM%INV)
	CMD (Delete)
	CMD (File)
	CMD (Flag)
	CMD (Forward)
	CMD (Help)
	CMD (Next,.RDNXT)
	CMD (Print,.LIST)
	CMD (Quit,.RQUIT)
	CMD (Reply,.RREPL)
	CMD (Set)
	CMD (Type,.RTYPE)
	CMD (Undelete)
	CMD (Unflag)
NRNOV==.-REDNOV-1

RCMDTB:	NRCMDS,,NRCMDS
TOPS10<	CMD (\"32,.REXIZ,CM%INV) > 	; Blue Demons again
	CMD (Answer,.RRPL1,CM%INV)
	CMD (Backup,.RBACK)		; Synonym for "previous"
	CMD (Blank)
	CMD (Copy,.PUT)
	CMD1 (D,ENTRDL,CM%ABR!CM%INV)
	CMD (Daytime)
	CMD (Define)
ENTRDL:	CMD (Delete)
	CMD (Directory,.RHEAD)
	CMD (Echo,,CM%INV)
TOPS20<	CMD (EMACS,.EDITO,CM%INV) >
	CMD (Exit,.REXIT)
	CMD (File)
	CMD (Flag)
	CMD (Forward)
	CMD1 (H,ENTRHD,CM%ABR!CM%INV)
ENTRHD:	CMD (Headers,.RHEAD)
	CMD (Help)
	CMD (List,,CM%INV)
	CMD (Mark)
	CMD (Move)
TOPS20<	CMD (Net-mail,.MAILR,CM%INV) >
	CMD (Next,.RDNXT)
	CMD (Previous,.RPREV,CM%INV)
	CMD (Print,.LIST)
	CMD (Push)
	CMD (Quit,.RQUIT)
	CMD1 (R,ENTRD0,CM%ABR!CM%INV) ; Abbreviation for READ
ENTRD0:	CMD (Read)
	CMD (Redistribute,,CM%INV)
	CMD1 (Rep,ENTREP,CM%ABR!CM%INV)
	CMD (Repair)
ENTREP:	CMD1 (Reply,.RREPL)
	CMD (Retrieve)
	CMD1 (S,ENTSNX,CM%ABR!CM%INV)
entsnx:	CMD (Send)
	CMD (Set)
	CMD (Show)
	CMD (Skim)
	CMD (SSend,.XSEND,CM%INV)
	CMD (Status,.STATU,CM%INV)
	CMD (Summarize,.RHEAD,CM%INV)
	CMD (Take)
	CMD (Type,.RTYPE)
	CMD (Undelete)
	CMD (Unflag)
	CMD (Unmark)
	CMD (Verbose-type,.RVBTY)
NRCMDS==.-RCMDTB-1
; Send (and reply) commands

SENNOV:	NSNOV,,NSNOV		; Novice-mode send-level commands
	CMD (cc)
	CMD (Display)
	CMD (Edit,.SEDIT)
	CMD (Erase)
	CMD (Help)
	CMD (Insert)
	CMD (Quit,.SQUIT)
	CMD (Remove,.UNTO)
	CMD (Return-receipt-requested,.RETUR) ;
	CMD (Send,.SSEND)
	CMD (Set)
	CMD (Subject)
	CMD (Text)
	CMD (To)
NSNOV==.-SENNOV-1

SCMDTB:	NSCMDS,,NSCMDS
TOPS10<	CMD (\"32,.EXIT0,CM%INV) >	; Blueness
	CMD (Blank)
	CMD (cc)
	CMD1 (D,ENTSDI,CM%ABR!CM%INV)
	CMD (Daytime)
	CMD (Define)
ENTSDI:	CMD (Display)
	CMD (Echo,,CM%INV)
	CMD (Edit,.SEDIT)
	CMD (Erase)
	CMD (Exit)
	CMD (Help)
	CMD (Include)
	CMD (Insert)
	CMD (Push)
	CMD (Quit,.SQUIT)
	CMD (Remove,.UNTO)
	CMD (Return-receipt-requested,.RETUR)
	CMD1 (S,ENTSSN,CM%ABR!CM%INV)
	CMD (Save,.SAVE)
ENTSSN:	CMD (Send,.SSEND)
	CMD (Set)
	CMD (Show)
	CMD (Status,.STATU,CM%INV)
	CMD (Subject)
	CMD (Take)
	CMD (Text)
	CMD (To)
	CMD (Type,.STYPE)
	CMD (Verbose-type,.VSTYP)
	CMD (ZSend,.ZSSND,CM%INV)
NSCMDS==.-SCMDTB-1

ECMDTB:	NECMDS,,NECMDS
	CMD All,.ERSAL
	CMD Cc,.ERSCC
	CMD Header-item,.ERSHD
	CMD Reply-information,.ERSDT
	CMD Subject,.ERSSB
	CMD Text,.ERSTX
	CMD To,.ERSTO
NECMDS==.-ECMDTB-1
DCMDTB:	NDCMDS,,NDCMDS
	CMD All,.DSALL
	CMD Cc,.DSCC
	CMD Subject,.DSSUB
	CMD Text,.DSTXT
	CMD To,.DSTO
NDCMDS==.-DCMDTB-1

EDCMTB:	NEDCMS,,NEDCMS
;	CMD All,.EDALL
;	CMD Cc,.EDCC
;	CMD Subject,.EDSUB
	CMD Text,.EDTXT
;	CMD To,.EDTO
NEDCMS==.-EDCMTB-1


RPCMTB:	NRPCMS,,NRPCMS		; REPLY commands
	CMD All,.REPAL
	CMD Sender-only,.REPTO
NRPCMS==.-RPCMTB-1


;Show commands

SHCMTB:	NSHCMT,,NSHCMT
	CMD (Address-lists,.SHADL)
	CMD (Aliases,.SHSYN)
	CMD (Daytime)
	CMD (Defaults,.SHDEF)
	CMD (Header-items,.SHHDI)
	CMD (Internal-information,.SHINT,CM%INV)
	CMD (Status,.STATU)
	CMD (Version)
NSHCMT==.-SHCMTB-1
;SET commands

SETNOV: 1,,1			; Novice-mode SET commands
	CMD (Experience-level,.STEXP)

STCMTB:	NSTCMD,,NSTCMD
	CMD (Auto-expunge,.STAUT)
	CMD (Auto-fill,.STAUF)
	CMD (Brief-address-list-display,.STBFD)
	CMD (Closing-text,.STCLZ)
	CMD (Concise-mode,.STCNC)
	CMD (Default,.STDFT)
	CMD (Directory-lookup-confirmation,.STDLC,cm%inv)
	CMD (Experience-level,.STEXP)
	CMD (Force-directory-lookup,.STFDI)
	CMD (Headers-on-printer-output,.STHLP,CM%INV)
	CMD (Headers-personal-name-only,.STHPR,CM%INV)
	CMD (Include-me-in-replies,.STINC)
TOPS20<	CMD (Logout-on-exit,.LOGOU) >
	CMD (No,.STNO)
	CMD (Only-headers-shown,.STOHS)
	CMD (Personal-name,.STPNM)
	CMD (Reply-address,.STRAD,CM%INV)
	CMD (Reply-to,.STRAD)	; Synonym
	CMD (Summary-on-printer-output,.STHLP)
	CMD (Summary-personal-name-only,.STHPR)
	CMD (Suppressed-headers,.STSPH)
	CMD (Text-scroll-region,.STWSZ)
	CMD (Type-initial-summary,.STSUM)
	CMD (Video-mode,.STVID)
NSTCMD==.-STCMTB-1

STCMT0:	NSTCM0,,NSTCM0		; SET commands which can be negated
	CMD (Auto-fill,.STAUF)
	CMD (Brief-address-list-display,.STBFD)
	CMD (Concise-mode,.STCNC)
	CMD (Directory-lookup-confirmation,.STDLC,cm%inv)
	CMD (Force-directory-lookup,.STFDI)
	CMD (Headers-on-printer-output,.STHLP,CM%INV)
	CMD (Headers-personal-name-only,.STHPR,CM%INV)
	CMD (Include-me-in-replies,.STINC)
	CMD (Personal-name,.STPNM)
	CMD (Reply-address,.STRAD,CM%INV)
	CMD (Reply-to,.STRAD)	; Synonym
	CMD (Summary-on-printer-output,.STHLP)
	CMD (Summary-personal-name-only,.STHPR)
	CMD (Suppressed-headers,.STSPH)
	CMD (Text-scroll-region,.STWSZ)
	CMD (Type-initial-summary,.STSUM)
	CMD (Video-mode,.STVID)
NSTCM0==.-STCMT0-1


;Set default

DFCMTB:	NDFCM0,,NDFCM0
	CMD Cc-list,.STDCC
	CMD Directory,.STCDI
	CMD Protection,.STCPR
	CMD Reply-to-all,.STRPA
	CMD Reply-to-sender-only,.STRPS
NDFCM0==.-DFCMTB-1
;Keyword table for set default directory

CRFDTB:	CRFDT0,,CRFDT0
	CMD Connected-directory,.STCND
	CMD Logged-in-directory,.STLGD
CRFDT0==.-CRFDTB-1

;Keyword table for set auto-expunge (on)

AUTCMT:	AUTCM0,,AUTCM0
	CMD Any-exit,1		; Magic numbers
	CMD Exit-command-only,2	; Default
	CMD Never,3
AUTCM0==.-AUTCMT-1


;Keyword table for define commands

DFNCTB:	DFNCT0,,DFNCT0
	CMD Address-list,.DEFSS
	CMD Alias,.DEFAS
	CMD Header-item,.DFHDI
DFNCT0==.-DFNCTB-1


;Keyword table for define header-item

HTYP0T:	HTYP00,,HTYP00
	CMD Optional,HD%OPT
	CMD Predefined,HD%PDF
	CMD Required,HD%RQD
HTYP00==.-HTYP0T-1

;Save command, top level

SVTLTB:	SVTLT0,,SVTLT0
	CMD (Outgoing-messages,.SAVMS)
SVTLT0==.-SVTLTB-1

;Save command, send level

SVCMTB:	SVCMT0,,SVCMT0
	CMD (Draft,.SAVDF)
	CMD (Outgoing-messages,.SAVMS)
SVCMT0==.-SVCMTB-1

;Retrieve commands

RETRCM:	RETRC0,,RETRC0
	CMD (Draft,.RESDF)
	CMD (Last-message,.RECOV)
RETRC0==.-RETRCM-1

;Insert commands

INSCTB:	INSCT0,,INSCT0
	CMD (File,.INSFI)
	CMD (Message,.INSMS)
INSCT0==.-INSCTB-1
; Headers of messages (SUMMARIZE command)

.RHEAD:	MOVEM F,SAVF
	JSP F,SAVMSQ		; Save message sequence context
	MOVE F,SAVF
	CALL DFSQTH		; Default to current
	CALL .HEAD0		; Call ordinary routine
	MOVEM F,SAVF
	JSP F,RESMSQ			; Restore context
	MOVE F,SAVF
	RET


.HEADE:	SAVMSX			; Save context if necessary
TOPS20<	SKIPG A,MSGJFN		; Does the mail file exist?
	JRST .HEAD2		; No, so do not check if file size changed
	CALL SIZFIL		; Yes, get latest infomation on file size
	SETOM CHNSIZ		; Error, assume file size has changed
	SKIPE CHNSIZ		; Has file size changed?
	CALL PARSEF		; Yes, do a total reparse
.HEAD2: >
	CALL DFSQNW		; Get sequence, default to new
	CALL .HEAD0		; Do the work
	RESMSX			; Restore context
	CALL SETREF		; Update last time mail file was read
	RET

.HEAD0:	SKIPN LCNT		; Any message at all?
	JRST [	WARN <No messages match this specification>
		RET]
HEADR1:	CALL NXTSEQ		; Get the next message in sequence
	 RET			; No more to do
	CALL TYPHDR		; Type its header
	JRST HEADR1


; Type messages

.VERBO:	TXO F,F%VBTY		; Set "verbose type" flag
.TYPE:	CALL DFSQTH
	SKIPN LCNT		; Any messages at all?
	JRST [	WARN <No messages match this specification>
		CALL SETREF	; Update the last time file was read
		RET]
TYPE1:	CALL NXTSEQ
	 JRST [	TXZ F,F%VBTY
		CALL SETREF	; Update the last time file was read
		RET]
	CALL CHKDEL		; See if deleted and type out warning
	 JFCL			; Unlike READ, type the message anyway.
	CALL TYPMSG
	JRST TYPE1
SUBTTL Routines to diddle various message flags

.FLAG:	SAVMSX			; Save context maybe
	MOVEI A,FLGMSG		; Flag messages
	MOVEI B,[ASCIZ / Flagged: /]
.FLAGX:	CALL SEQUEN
	RESMSX			; Restore context maybe
	RET

.UNFLA:	SAVMSX			; Save context maybe
	MOVEI A,UFLMSG		; Unflag messages
	MOVEI B,[ASCIZ / Unflagged: /]
	CALLRET .FLAGX		; Common exit

.UNMAR:	SAVMSX			; Save context maybe
	MOVEI A,UMKMSG		; Unmark message (make unseen)
	MOVEI B,[ASCIZ / Unmarked: /]
	CALLRET .FLAGX		; Common exit

.UNDEL:	SAVMSX			; Save context maybe
	MOVEI A,UNDMSG		; Undelete message
	MOVEI B,[ASCIZ / Undeleted: /]
	CALLRET .FLAGX		; Common exit

.MARK:	SAVMSX			; Save context maybe
	MOVEI A,MRKMSG		; Mark message (as seen)
	MOVEI B,[ASCIZ / Marked: /]
	CALLRET .FLAGX		; Common exit
.DELET:	SAVMSX			; Save context maybe
	MOVEI A,DELMSG		; Delete message
	MOVEI B,[ASCIZ / Deleted: /]
	CALLRET .FLAGX		; Common exit

FLGMSG:	MOVX A,M%ATTN		; Mark as attention needed
	MOVE C,[AOS NFLAGD]	; And increment number flagged
	JRST SETBIT

UFLMSG:	MOVX A,M%ATTN		; Mark as unflagged
	MOVE C,[SOS NFLAGD]	; Decrement number of messages flagged
	JRST CLRBIT

DELMSG:	MOVX A,M%DELE		; Mark as deleted
	MOVE C,[PUSHJ P,DELMS1]	; We will also mark as read
	JRST SETBIT
DELMS1:	AOS NDELET		; Keep counts up to date
	MOVX A,M%SEEN		; Was this message unread?
	TRNE A,(D)		;  before we deleted it?
	 JRST DELMS2		; No, do normal things.
	IORM A,MSGBTS(B)	; Mark it as read now.
	SOS UNSEEN		; One less unread message.
DELMS2:	MOVX A,M%DELE		; Restore our own bit
	RET			;  and return.

UNDMSG:	MOVX A,M%DELE		; Mark as undeleted
	MOVE C,[SOS NDELET]	; Keep counts up-to-date
	JRST CLRBIT

MRKMSG:	MOVX A,M%SEEN		; Mark as seen
	MOVE C,[SOS UNSEEN]	; One less new message
SETBIT:	GTMBL (M,B)		; Get ptr to message block
	MOVE D,MSGBTS(B)	; Get the message bits handy
	TRNE A,(D)		; Did we already have this bit set?
	 RET			; Yes, well we don't have much to do then
	XCT C			; Keep counts accurate
	IORM A,MSGBTS(B)	; Set it
	JRST UPDBIT		; Go update the message bits

UMKMSG:	MOVX A,M%SEEN		; Mark as unseen
	MOVE C,[AOS UNSEEN]	; One more new message
CLRBIT:	GTMBL (M,B)		; Get ptr to message block
	MOVE D,MSGBTS(B)	; Get the message bits handy
	TRNN A,(D)		; Did we already have this bit unset?
	 RET			; Yes, well we don't have much to do then
	XCT C			; Keep counts accurate
	ANDCAM A,MSGBTS(B)	; Unset the bit
	JRST UPDBIT		; Go update the message bits

;Here to perform some action on a sequence of messages
;Call:
;	A/ address of routine to munch message
;	B/ address of ASCIZ reassurance string

SEQUEN:	DMOVEM A,DOMSG		; Set up handler
	CALL DFSQTH		; Get sequence, default to current
SEQUE0:	MOVE A,LCNT		; Get count of msgs in this sequence
	CAIN A,1		; Is there only one?
	SKIPG REDLVL		;  and is this a READ or SKIM mode command?
	SKIPA			; No to either
	JRST SEQUE2		; Yes, no confirmations then
	CALL CRIF		; In case random error messages have happened
	MOVE A,DOMSG+1		; Type reassurance string
	HRLI A,(POINT 7,)
	$CALL KBFTOR		; Flush buffers, this might be slow
SEQUE1:	CALL NXTSEQ		; Next message spec'd
	 CALLRET PRTSQS		; No more, type end of them
	CALL @DOMSG		; Process the message
	CALL PRTSEQ		; Print out the numbers
	JRST SEQUE1
SEQUE2:	MOVX A,F2%NSQ		; No sequence flag
	IORM A,FLAGS2		;  ..
	CALL NXTSEQ		; Get next (only) message
	 JRST [	WARN <MS internal error:  SEQUE2>
		JRST SEQUE3]
	CALL @DOMSG		; Call handler
	CALL NXTSEQ		; Bug filter
	 JRST SEQUE3
	WARN <MS internal error:  LCNT and NXTSEQ don't agree>
SEQUE3:	MOVX A,F2%NSQ		; Clear no sequence flag
	ANDCAM A,FLAGS2
	RET
SUBTTL GET command - Get another message file

.GET:	NOISE (messages from file)
	TXZ F,F%F2		; Allow printing of file status
	TXZ F,F%RSCN		; Don't return to EXEC after reading file
   TOPS20<
	MOVX A,GJ%OLD		; Must exist
	MOVEM A,CJFNBK+.GJGEN
	HRROI A,[ASCIZ /POBOX:/]	; Default to PS:<logged-in-directory>
	MOVEM A,CJFNBK+.GJDEV
	HRROI A,MYDIRS
	MOVEM A,CJFNBK+.GJDIR
	HRROI A,[ASCIZ /MAIL/]
	MOVEM A,CJFNBK+.GJNAM
	HRROI A,[ASCIZ /TXT/]
	MOVEM A,CJFNBK+.GJEXT
   >;End TOPS20
   TOPS10<
	SETZM CJFNBK		; First zero the block
	MOVE A,[CJFNBK,,CJFNBK+1]
	BLT A,CJFNBK+CJFNLN-1
	MOVE A,[SIXBIT /MAIL/]
	MOVEM A,CJFNBK+.FDNAM
	MOVSI A,(SIXBIT /TXT/)
	MOVEM A,CJFNBK+.FDEXT
   >;End TOPS10
	MOVEI A,[FLDDB. .CMFIL]
	CALL CFIELD
	TXZ F,F%AMOD!F%MOD
	SETZM GTFLAG
 	JRST GET1		;GO DO THE WORK

.NEXT:	NOISE (message)
	CONFRM			; Confirm first
	SKIPG MSGJFN
	 JRST [	WARN (No current mail file)
		RET]
	CAME M,LASTM		; At last message?
	 AOJA M, [ CALL SETREF	  ; Update the last read of mail file
		   JRST TYPMSG ]   ; No, type the next one then
	CIETYP < Currently at end, message %M.
>
	RET

.EXIT:	NOISE (and update message file)
	CONFRM			; Confirm first
.EXIT0:	CALL CHECK0		; Any newly arrived mail?
	 SKIPA			; No, continue
	  JRST [CALL CLRFIB		; Clear typeahead - this is unexpected
		CALL CHECKS		; Type message
		JRST .+1]		; Go on
	MOVE A,AUTEXP		; Get auto-expunge magic number
	TXNN F,F%MOD		; SYSTEM messages?
	CAIN A,3		;  or never do auto-expunge?
	JRST .EXIT1		; Yes to either, don't try then
	SKIPLE MSGJFN		; If file exists,
	 CALL EXPUNG		;  then expunge first
.EXIT1:	SKIPG MSGJFN		; Still have file?
	JRST CKEXIT		; No, just quit
TOPS20<	CALL UNMAPF >		; Yes - unmap message file
	CALL CLOSEF		;  and flush JFN

CKEXIT:	CALL CKXRTN		; Exit and return if continued
	MOVE P,[IOWD NPDL,PDL]	; If continued, reset stack
	JRST GO3		;  and try a rescan (so KEEP CONTINUE wins)

CKXRTN:	TXNE F,F%MOD		; Never do implied EXPUNGE for system mail
	JRST CKXIT0		;  ..
	MOVE A,AUTEXP		; Get auto-expunge magic number
	CAIN A,1		; Do for any exit?
	JRST [	SKIPLE MSGJFN		; Yes, have a message file?
		CALL EXPUNG		; Yes, do it then
		JRST .+1]
CKXIT0:	SKIPE SCRLFL		; If scroll region in effect,
	JRST [	CALL @SCRRGR		; Undo scroll region stuff
		CALL @SCRBTM		; Get to bottom of screen
		SETZM SCRLFL		; Reset flag
		JRST .+1]
	$CALL K%FLSH		; Make sure user sees everything we've typed
   TOPS20<
	TXNN F,F%LOGO
	JRST CKXIT1
	MOVNI A,1
	LGOUT
	 JRETER <Failed to logout job>
CKXIT1:	HALTF
   >;End TOPS20

   TOPS10<
;**;[3095] Insert 2 lines at ckxit0: + 16	Ned	5-Aug-87
	SKIPLE MSGJFN		; [3095]Do we still have the file open?
	CALL CLOSEF		; [3095]Yes, don't leave the file sitting around
	MONRT.
	MOVX A,.PRIOU		; In case continued
	HRRM A,SBK+.CMIOJ	;  turn echoing back on
   >;End TOPS10
;**;[3087] Change 1 line at .DEFAS:-8L	MDR	 7-APR-87
;**;[3086] Add 1 line at .DEFAS:-8L	MDR	20-MAR-87
	SETOM MSGSSQ		;[3086][3087] Reset the message sequence
	CALL TTINI		; See if user changed terminal types
	RET

SUBTTL Define commands - define alias and define address-list

;Define alias

.DEFAS:	MOVX B,AB%INV		; This flavor is invisible to recipient
	MOVEI A,[FLDDB. (.CMTOK,,<POINT 7,[ASCIZ /*/]>,,,[FLDDB. (.CMQST,,,,,[FLDDB. (.CMFLD,,,<name of alias>)])])]
	JRST .DEFS1


;Define address-list

.DEFSS:	SETZ B,			; This kind will be visible to recipient
	MOVEI A,[FLDDB. (.CMTOK,,<POINT 7,[ASCIZ /*/]>,,,[FLDDB. (.CMQST,,,,,[FLDDB. (.CMFLD,,,<name of address list>)])])]
;	JRST .DEFS1
;Common code to define address lists or aliases

.DEFS1:	STKVAR <SYN0,ADRL,TBENT0,FLGS,FCNB>	; Synonym ptr, addr list ptr, table entry addr, fcn blk addr
	MOVEM A,FCNB		; Save function block address
	MOVEM B,FLGS		; Save flags
	NOISE (name)
	MOVE A,FCNB		; Restore function block address
	CALL RFIELD		; Parse the synonym
	MOVE A,CR.COD(A)	; Get fcn parsed
	CAIN A,.CMTOK		; * (all)?
	JRST .DEFA8		; Yes, go delete all aliases/address-lists
	SETZM SYN0		; No string yet
	CALL CPYATM		; No, allocate string blk and copy atom to it
	 JRST .DEFAE		; No space
	MOVEM A,SYN0		; Save address of string
	HRLI A,(POINT 7)	; Scan string, allow only reasonable things
	CALL SCNASN		;..
	JUMPN B,[
		WARN <Illegal character in Alias name>
		MOVE A,SYN0
		CALL RELSB
		RET
	]
	NOISE (to be)
	SETZM ADRL		; No address list
	CALL ADRLST		; Parse addresses and form list
	 JRST .DEFAE		; Error
	MOVEM A,ADRL		; Save ptr to head of addr list
	MOVE B,FLGS		; Get flags for this synonym
	MOVEM B,AB.FLG(A)	; Stuff into A-block
	MOVE A,KWDTBL		; See if this one already exists
	MOVE B,SYN0		; Point to synonym string
	HRLI B,(POINT 7,)	;  ..
	$CALL S%TBLK		;  ..
	TXNN B,TL%EXM		; Exact match?
	JRST .DEFA1		; No, just add to table then
	MOVEM A,TBENT0		; Yes, save address of entry
	HRRZ B,(A)		; Get code or pointer to A-block
	CAIN B,SYSCOD		; Code?
	JRST [	WARN (Can't redefine or delete definition of SYSTEM)
		MOVE A,SYN0		;  ..
		CALL RELSB		; Release string block no longer needed
		RET]
	CALL ABREL		; Delete or supersede - release all A-blocks
	MOVE A,SYN0		; So release that as well
	CALL RELSB		;  ..
	SKIPE ADRL		; Any address list returned?
	JRST .DEFA2		; Yes, superseding
	MOVE B,TBENT0		; No, deleting - release synonym name also
	HLRZ A,(B)		;  ..
	CALL RELSB
	MOVE A,KWDTBL		; Remove entry from table
	MOVE B,TBENT0		;  ..
	$CALL S%TBDL		;  ..
	RET			; All done!
;Here to supersede an existing alias

.DEFA2:	MOVE A,ADRL		; Point to address list
	MOVE B,TBENT0		; Address of table entry
	HRRM A,(B)		; Point existing table entry at new expansion
	RET			; All done

;Here to add an entirely new alias

.DEFA1:	SKIPN B,ADRL		; Insure that we got an address
	JRST [	WARN (No address specified)
		RET]
	MOVEI A,KWDTBL		; Where to add table entry
	HRL B,SYN0		; Address of synonym string
	CALL TBADDS		; Add to table, expand if necessary
	JUMPF [	CMERR (Can't add synonym to table)
		RET]
	RET

;Here if no room

.DEFAE:	WARN (Can't get memory)
	SKIPE A,SYN0		; If string block got allocated,
	CALL RELSB		;  release it
	RET


;Here to delete all address-lists/aliases (define alias *)

.DEFA8:	CONFRM
	HLLZ E,@KWDTBL		; Count of entries in table
	JUMPE E,R		; Quit if none
	MOVN E,E		; Form AOBJN ptr to table
	HRR E,KWDTBL		;  ..
	ADDI E,1		; Skip header word
.DEFA9:	HRRZ B,(E)		; Get next entry
	CAIN B,SYSCOD		; SYSTEM?
	JRST .DEFA7		; Yes, skip it
	MOVE A,AB.FLG(B)	; Get flags for this entry
	XOR A,FLGS		; See if the kind we want
	TXNE A,AB%INV		; Does this bit match?
	JRST .DEFA7		; No, skip this entry then
	CALL ABREL		; Delete A-block
	HLRZ A,(E)		; Get address of name string
	CALL RELSB		; Release space
	MOVE A,KWDTBL		; Remove from TBLUK table
	MOVEI B,(E)		; Point to entry to remove
	$CALL S%TBDL		; Delete it
	SUBI E,1		; Account for shortening of table
.DEFA7:	AOBJN E,.DEFA9		; Loop through table
	MOVE A,KWDTBL		; Shorten the table
	CALLRET COMPAC		;  and return

;Scan alias string (BP in A), checking for reasonable characters. Mostly,
; we don't want Comma in an alias name, but A..Z a..z 0..9 .-%&_$ and space
; are sufficient. Return B/0 if it looks OK, B/ nonzero otherwise.
SCNASN:	SETZ C,
SCNALN:	ILDB B,A		; Get character
	JUMPE B,[
		CAIN C,0	;No real characters in?
		MOVEI B," "	; Return failing
		RET]
	CAIN B," "
	JRST	[
		JUMPN C,SCNALN	;Leading space?
		RET]		;Yes; return failing
	CAIL B,"A"
	CAILE B,"Z"
	CAIN B,"-"
	AOJA C,SCNALN
	CAIL B,"a"
	CAILE B,"z"
	CAIN B,"."
	AOJA C,SCNALN
	CAIL B,"0"
	CAILE B,"9"
	CAIN B,"_"
	AOJA C,SCNALN
	CAIE B,"%"
	CAIN B,"$"
	AOJA C,SCNALN
	CAIN B,"&"
	AOJA C,SCNALN
	RET
SUBTTL Define commands - ADRLST - parse an address list

;Parse an address list and form linked list of A-blocks
;
;Return	+1: Failure, no room or bad syntax
;	+2: Success, A points to head of list

ADRLST:	TRVAR <AB0,AB1,<ADRS,SB.LEN>>	; Head, current
	MOVEI A,AB.LEN		; Size of an A-block
	$CALL M%GMEM		; Allocate a chunk
	JUMPF R			; Failure
	MOVEM B,AB0		; Save head pointer
	CALL ADRLSV		; Save state and set up for reparse
ADRLS0:	MOVEM B,AB1		; Make this current
	MOVEI U,ADRS		; Point to string space on stack
	CALL GETUSR		; Parse an address
	 JRST ADRLSX		; CRLF -- all done
	MOVE C,AB1		; Point to current A-block
	HRRZM B,AB.COD(C)	; Store user number or code
	MOVEI A,ADRS		; Point to address we got
	HRLI A,(POINT 7,)	; Form byte pointer
	CALL COUNTS		; Size it up
	CALL ALCSB		; Allocate a string block for it
	 JRST [	WARN <Can't parse address list, insufficient memory>
		RET]
	MOVE C,AB1		; Point to current A-block
	MOVEM B,AB.ADR(C)	; Set up pointer to address string
	HRLI B,(POINT 7,)	; Form byte pointer
	MOVE A,B		; Set up dest for MOVST0
	MOVEI B,ADRS		; Point to stack copy of address
	CALL MOVST0		; Copy to string block
	TXZE F,F%CMA		; More addresses to come?
	JRST [	MOVEI A,AB.LEN		; Yes, get another chunk
		$CALL M%GMEM		;  ..
		JUMPF ADRLSE		; Sigh...  fail
		MOVE A,AB1		; Point to current block
		MOVEM B,AB.LNK(A)	; Chain
		JRST ADRLS0]		; Go fetch next address
ADRLSX:	MOVE A,AB0		; Point to head
	SKIPN AB.COD(A)		; Any addresses typed at all?
	JRST [	MOVE B,A		; For ABREL
		CALL ABREL		; No, release all chunks
		SETZ A,			; Signal null address spec
		RETSKP]
	RETSKP			; Yes, all done

ADRLSE:	MOVE B,AB0		; Failure, release chunks
	CALLRET ABREL		;  and give bad return
;Routine to prepare for reparse -- calls remainder of ADRLST as coroutine

ADRLSV:	MOVEM B,ABLHED		; Save head of list in OWN storage
	MOVEI A,ADRLS2		; Where to go in case reparse needed
	HRRM A,SBK+.CMFLG	; Inform S%CMND
	EXCH A,REPARA		; Inform CMDERR, get what it wanted before this
	MOVEM A,REPAR0		; Save what was originally there
	MOVEI A,ADRLS1		; Where to go to restore world
	EXCH A,(P)		; Set up so coroutine exit restores world
	JRST (A)		; Call remainder of ADRLST as coroutine


;This routine called by reparse code at CMDERR or from S%CMND via .CMFLG word
; First instruction is in case of SOSing reparse address because reprompt needed

	SOS REPAR0		; Decrement saved reparse addr to force reprompt
ADRLS2:	MOVEI A,REPARS		; Original reparse address
	HRRM A,SBK+.CMFLG	; Restore
	MOVE A,REPAR0		; Original reparse routine
	MOVEM A,REPARA		; Restore
	SKIPE B,ABLHED		; Deallocate A-block chain
	CALL ABREL		;  ..
	JRST REPARS		; Now go do fancy reparse stuff


;Routine called when coroutine finally exits (ADRLST finishes or bombs)

ADRLS1:	TDZA B,B		; Watch out for skip/nonskip returns
	MOVEI B,1		; B gets offset (A returns ADRLST's result)
	ADDM B,(P)		; Correct return address
	MOVEI B,REPARS		; Restore default reparse stuff
	HRRM B,SBK+.CMFLG	;  ..
	MOVE B,REPAR0		;  ..
	MOVEM B,REPARA		;  ..
	RET			;  and return

;Here to release chain of A-blocks, B points to first block

ABREL:	STKVAR <AHED>
	MOVEM B,AHED		; Save pointer
	SKIPE A,AB.ADR(B)	; If there is an string block pointed to,
	CALL RELSB		;  release it
	MOVE B,AHED		; Restore pointer to A-block list
	MOVE D,AB.LNK(B)	; Get link
	MOVEI A,AB.LEN		; Length of an A-block
	$CALL M%RMEM		; Release chunk
	JUMPE D,R		; If no link, done
	MOVE B,D		; Link, do next
	JRST ABREL		;  ..
SUBTTL Define commands - MVALST - move an address list

;Move an address list, handling line wrap and XMAILR-style quoting
;Call:	A/ ptr to head of address list
;	X/ Horizontal position

MVALST:	STKVAR <ABLK,BRAKF>	; Ptr to current A-block
	MOVEM A,ABLK
	SETZM BRAKF		; No brackets needed yet
MVALS0:	MOVE A,ABLK
	MOVE B,AB.ADR(A)	; Get address of string block for address text
	HRLI B,(POINT 7,)
	HRRZ C,AB.COD(A)	; Get user number or code
	CAIN C,PFXCOD		; Is this an address list prefix?
	JRST [	CALL MOVTU0		; Yes, type it
		MOVEI A,":"		; Punctuate
		XCT MOVDSP		;  ..
		MOVE A,ABLK		; Restore current A-block ptr
		MOVE A,AB.LNK(A)	; Get ptr to next
		MOVEM A,ABLK		; Make current
		AOJA X,MVALS2]		; Go check for line wrap
	CAIN C,PRNCOD		; Personal name?
	JRST [	CALL MOVTU0		; Yes, type it
		SETOM BRAKF		; Flag punctuation needed for address
		MOVE A,ABLK		; Point to current
		MOVE A,AB.LNK(A)	; Get next
		MOVEM A,ABLK		; Make current
		AOJA X,MVALS2]		; Continue
	MOVEI A,"<"		; Just in case...
	SKIPE BRAKF		; Brackets needed?
	XCT MOVDSP		; Yes, type one
	CALL MOVADR		; Normal address, just type it
	MOVEI A,">"		; Closing bracket if needed
	SKIPE BRAKF		;  ..
	XCT MOVDSP		; Close it up
	SETZM BRAKF		; Clear flag
MVALS1:	MOVE A,ABLK		; Restore A-block pointer
	SKIPN B,AB.LNK(A)	; Any more entries?
	RET			; No, return
	MOVEM B,ABLK		; Yes, make this one current
	MOVE C,AB.COD(B)	; Get usernum or code of this entry
	CAIN C,SFXCOD		; Suffix?
	JRST [	MOVEI A,";"		; Yes, type it
		XCT MOVDSP		;  ..
		AOJA X,MVALS1]		; Check for more suffixes or addresses
	MOVEI A,","		; Type comma, there's more coming
	XCT MOVDSP		;  ..
MVALS2:	CAIL X,ADRWTH		; Or too close to right margin?
	JRST [	MOVEI B,[ASCIZ /
    /]
		CALL MOVSB2		; Move CRLF and indentation
		MOVEI X,4		; Init horizontal position
		JRST MVALS0]		; Type next address
	MOVEI A," "		; Same line, type space
	XCT MOVDSP		;  ..
	ADDI X,2		; Update column position
	JRST MVALS0
SUBTTL Define commands - Define header-item

.DFHDI:	NOISE (name)
	TXZ F,F%F1		; Assume not supersede or delete
	setz	e,			;not rrr
	MOVEI A,[FLDDB. (.CMTOK,,<POINT 7,[ASCIZ /*/]>,,,[FLDDB. (.CMQST,,,,,[FLDDB. (.CMFLD,,,<name of header item>)])])]
	CALL RFIELD		; Get the name
	MOVE A,CR.COD(A)	; Get function parsed
	CAIN A,.CMTOK		; Token? (asterisk)
	JRST .DFHD6		; Yes, confirm and delete all header-items
	MOVE B,[POINT 7,ATMBUF]
	SKIPN A,HDITAB		; See if name already exists
	JRST .DFHD1		; Table empty, this header item is new
	$CALL S%TBLK		; Table nonempty, look up this entry
	TXNE B,TL%EXM		; Exact match?
	JRST [	TXO F,F%F1	; Yes, flag supersede/delete
		MOVEM A,TENT1	; Save addr of existing table entry
		JRST .DFHD0]	; Don't make new name block
.DFHD1:	CALL CPYATM		; New hdr-item, copy name to string block
	 RET			; Failure
	MOVEM A,HDIO		; Save address of string block
;	JRST .DFHD0
;define header-item (cont'd.)

.DFHD0:	NOISE (type)
	MOVEI A,[FLDDB. (.CMKEY,,HTYP0T,,,[FLDDB. (.CMCFM)])]
	CALL RFIELD		; Get name or CR
	MOVE A,CR.COD(A)	; Get function parsed
	CAIN A,.CMCFM		; Confirm?
	JRST .DFHD8		; Yes, delete this entry then
	HRRZ B,(B)		; Get flags for this keyword
	MOVEM B,FLG		; Save
	MOVEI A,[FLDDB. (.CMKEY,,HTYP1T)]
	CALL RFIELD		; Parse type
	HRRZ B,(B)		; Get flags for this keyword
	IORB B,FLG		; Set more bits
	ANDI B,HD%TYP		; ***Should use LOAD
.DFHDA:	HLRZ A,GETHDA(B)	; Get size of chunk for this type H-block
	$CALL M%GMEM		; Get the chunk
	JUMPF .DFHD9		; No room
	MOVEM B,HDI1		; Remember this address
	MOVEM A,HD.SIZ(B)	; Put size into chunk
	MOVE A,HDI1		; Addr of H-block
	MOVE B,FLG		; Get flags and type
	MOVEM B,HD.FLG(A)	; Store in H-block
	ANDI B,HD%TYP		; Get just type
	CAIN B,HD%KWD		; Keyword?
	JRST [	NOISE (list)
		MOVE B,FLG		; Insure not predefined
		TXNE B,HD%PDF		;  ..
		CWARN (Keyword header-item cannot be predefined)
		MOVEI A,^D100		; Allocate table space
		$CALL M%GMEM
		MOVE A,HDI1		; Point to H-block
		MOVEM B,HD.DAT+1(A)	; Point H-block to table
		MOVEI A,^D99		; Number of entries
		MOVEM A,(B)		; Init table header word
		MOVE A,B		; For KWDLST
		PUSH P,A		; Save table address
		CALL KWDLST		; Parse list
		POP P,A			; Restore table address
		HLRZ B,(A)		; Get count of entries presented
		JUMPE B,[MOVE B,A		; None, error - release
			MOVEI A,^D100		;  storage for table
			$CALL M%RMEM		;  ..
			WARN <No keywords specified>
			RET]			; Error return
		CALL COMPAC		; Compact the table
		JRST .DFHD3]		; Can't be predefined
	MOVE B,FLG			;
	TXNN B,HD%PDF		; Predefined header-item?
	JRST .DFHD2		; No, don't parse one now then
	CALL GETHDI		; Parse the header-item
	 RET			; Error, msg already typed
	JRST .DFHD3		; GETHDI got the confirmation
.DFHD2:	JUMPN E,.DFHD3		; If a rrr commmand don't need to confirm
	CONFRM
.DFHD3:	TXZE F,F%F1		; Superseding existing entry?
	JRST [	MOVE D,TENT1		; Yes, get its addr
		HRRZ A,(D)		; Get old H-block addr
		CALL HBREL		; Release
		MOVE A,HDI1		; Addr of new block
		MOVE D,TENT1		; recover address to store to!
		HRRM A,(D)		; Replace
		RET]			; All done
	MOVEI A,HDITAB		; Header-item table
	HRLZ B,HDIO		; String address (name of header-item)
	HRR B,HDI1		; Address of header-item block
	CALL TBADDS		; Add to table
	JUMPF [	WARN (Couldn't add header-item to table)
		RET]
	RET


;Here to delete all header-items (define header-item *)

.DFHD6:	CONFRM
	SKIPN A,HDITAB		; If a table exists
	RET			;  ..
	HLRZ E,(A)		; Get number of header-items
	JUMPE E,R		; If none, done
.DFHD7:	MOVE A,HDITAB		; Entry to be removed is always first
	ADDI A,1		;  ..
	CALL HDIDEL		;  since HDIDEL moves 'em all down one
	SOJG E,.DFHD7		; Loop through all entries
	RET			;  and return


;Here to delete header-item definition

.DFHD8:	TXNN F,F%F1		; Insure that we found a match
	JRST [	HRRO A,HDIO		; Point to name
		WARN <Header-item "%1S" does not exist>
		MOVE A,HDIO		; Get pointer to string block again
		CALL RELSB		; Release storage
		RET]
	HRRZ A,TENT1		; Address of entry to delete
	CALLRET HDIDEL		; Delete it and return


.DFHD9:	CMERR (No room)
	RET
SUBTTL Define commands - HDIDEL - delete a header-item

;Delete an entry from HDITAB and associated storage
;A/ address of entry to delete

HDIDEL:	STKVAR <T0>
	MOVEM A,T0		; Save address of table entry
	HLRZ A,(A)		; Get ptr to name block
	CALL RELSB		; Release it
	MOVE A,T0		; Recover address of table entry
	HRRZ A,(A)		; Addr of H-block
	CALL HBREL		; Release H-block
	MOVE A,HDITAB		; Header-item table
	MOVE B,T0		; Addr of entry to remove
	$CALL S%TBDL		; Do it
	RET
SUBTTL Define commands - HBREL - release H-block storage

;Release H-block storage - must release associated blocks too
;Call:	A/ Addr of H-block

HBREL:	STKVAR <HBADD>
	MOVEM A,HBADD		; Remember address for a bit
	MOVE A,HD.FLG(A)	; Get flags
	ANDI A,HD%TYP		; *** Get type (should use LOAD)
;	LOAD A,HDTYP(A)
	CAIN A,HD%ADR		; Address spec?
	JRST [	MOVE A,HBADD		; Yes, point to H-block
		SKIPE B,HD.DAT(A)	; Point to address list
		CALL ABREL		; Release it if present
		JRST HBREL0]
	CAIN A,HD%KWD		; Keyword?
	JRST [	MOVE A,HBADD		; Yes, point to H-block
		SKIPE A,HD.DAT+1(A)	; If keyword table present,
		CALL KWDREL		; Release it
		JRST HBREL0]
	CAIN A,HD%TXT		; Text?
	JRST [	MOVE A,HBADD		; Yes, point to H-block
		SKIPE A,HD.DAT(A)	; Get pointer to text block
		CALL RELSB		; Release it if present
		JRST HBREL0]
HBREL0:	MOVE B,HBADD		; Point to H-block again
	MOVE A,HD.SIZ(B)	; Size
	$CALL M%RMEM		; Release chunks
	RET
SUBTTL Define commands - KWDLST - parse keyword list

;Parse keyword list and enter into TBLUK-style table
;Call:	A/ address of table

KWDLST:	STKVAR <STRB,HDBLK,IDX>	; String block address, table address, index
	MOVEM A,HDBLK		; Save H-block ptr
	SETZM IDX		; Init index for ordering of keywords
KWDLS0:	MOVEI A,[FLDDB. (.CMFLD,CM%SDH,,<
Enter keywords, separated by commas
>)]
	CALL RFIELD		; Get next word
	LDB A,[POINT 7,ATMBUF,6]
	JUMPE A,KWDLS1		; Insure something typed
	CAIE A,15		;  ..
	CAIN A,12
	JRST KWDLS1
	CALL CPYATM		; Allocate string blk, copy atom to it
	 RET			; Failure, give up now
	MOVEM A,STRB		; Save address of the string
	MOVE A,HDBLK		; Table address
	AOS B,IDX		; Count items as they go in
	HRL B,STRB		; String pointer,,index
	$CALL S%TBAD		; Add to it
	JUMPF [	CALL CRIF
		HRRZ A,STRB
		HRLI A,(POINT 7,)
		$TEXT (KBFTOR,<Can't add keyword ^Q/A/ to table because: ^E/[-1]/>)
		RET]
KWDLS1:	MOVEI A,[FLDDB. .CMCFM,,,,,[FLDDB. .CMCMA]]
	CALL RFIELD
	MOVE A,CR.COD(A)	; Get function parsed
	CAIN A,.CMCMA		; Comma typed?
	JRST KWDLS0		; Yes, go for next keyword
	RET

SUBTTL HDTYPS - Header-item definitions

;Parse header-item and store
;Call:	A/ Address of H-block
;Returns +1: failure, error msg already printed
;	 +2: success, H-block updated

GETHDI:	MOVEM A,HBLKP		; Save H-block pointer and result pointer
	MOVE B,HD.FLG(A)	; Should use LOAD for this
	ANDI B,HD%TYP		; Isolate type field
;	LOAD B,HDTYP(A)		; Get type of H-block
	HRRZ B,GETHDA(B)	; Get routine address
	CALLRET (B)		; Dispatch to appropriate routine


;Define types of header-items, names, and size of H-blocks

DEFINE HDTYPS,<
X	ADR,address,HD.LEN
X	DAT,date,HD.LEN
X	DTI,<date-and-time>,HD.LEN
X	KWD,keyword,<HD.LEN+1>
X	TXT,<text-string>,HD.LEN
X	TIM,time,HD.LEN
>
SUBTTL Routines to parse header-items

;Build command table

DEFINE X(COD,STRNG,SIZ),<
	CMD (<STRNG>,HD%'COD)
>

HTYP1T:	HTYP10,,HTYP10
	HDTYPS
HTYP10==.-HTYP1T-1


;Define type codes and build dispatch table

	%%%ZZZ==0
DEFINE X(COD,STRNG,SIZ),<
	HD%'COD==%%%ZZZ		;; Define type code
	XWD SIZ,GTH'COD		;; Address of routine to parse header-item
	%%%ZZZ==%%%ZZZ+1	;;  and size of H-block
>

GETHDA:	HDTYPS

;Define name strings

DEFINE X(COD,STRNG,SIZ),<
	EXP POINT 7,[ASCIZ /STRNG/]
>

HDTNAM:	HDTYPS
;Parse address header-item

GTHADR:	MOVE B,HBLKP		; Point to H-block
	SKIPE B,HD.DAT(B)	; Any address list already there?
	CALL ABREL		; Yes, release it first
	CALL ADRLST		; Parse an address list
	 RET			; Error
	MOVE C,HBLKP		; Point to H-block
	MOVEM A,HD.DAT(C)	; Store pointer to address list
	JUMPE A,GTHEX0		; Null list typed -- mark not present
GTHEX1:	MOVX A,HD%PRS		; Non-null list -- mark item present
	IORM A,HD.FLG(C)	;  ..
	RETSKP			; Give good return

GTHEX0:	MOVX A,HD%PRS		; Mark header-item not present
	ANDCAM A,HD.FLG(C)	;
	RETSKP

;Parse date

GTHDAT:	MOVEI A,[FLDDB. .CMCFM,,,,,[FLDDB. (.CMTAD,,CM%IDA)]]
GTHDT0:	CALL RFIELD
	MOVE A,CR.COD(A)	; Get function parsed
	MOVE C,HBLKP		; Point to H-block
	CAIN A,.CMCFM		; Just CR typed?
	JRST GTHEX0		; Yes, mark item not present
	PUSH P,B		; Save date/time over CONFRM
	CONFRM
	POP P,HD.DAT(C)		; Store datum
	JRST GTHEX1		; Mark present

;Parse date/time

GTHDTI:	MOVEI A,[FLDDB. .CMCFM,,,,,[FLDDB. (.CMTAD,,CM%IDA!CM%ITM)]]
	JRST GTHDT0		; Join common code

;Parse time

GTHTIM:	MOVEI A,[FLDDB. .CMCFM,,,,,[FLDDB. (.CMTAD,CM%SDH,CM%ITM,<
Time in hours, or hh:mm for hours and minutes
>)]]
	JRST GTHDT0		; Join common code
;Parse text header-item

GTHTXT:	MOVEI A,[FLDDB. .CMCFM,,,,,[FLDDB. (.CMTXT)]]
	CALL RFIELD		; Get field
	MOVE A,CR.COD(A)	; Get function parsed
	MOVE C,HBLKP		; Point to H-block
	CAIN A,.CMCFM		; Just CR?
	JRST GTHEX0		; Yes, mark as not present
	CONFRM
GTHTX1:	MOVE A,[POINT 7,ATMBUF]	; Count chars in string
	CALL COUNTS		;  ..
	ADDI A,2		; Add 2 in case quotes required
	CALL ALCSB		; Allocate a string block
	 JRST [	MOVE A,[POINT 7,ATMBUF]
		WARN <Can't add header-item, insufficient memory>
		RET]
	MOVE C,HBLKP		; Point to H-block
	MOVEM B,HD.DAT(C)	; Save pointer to string block
	MOVE B,[POINT 7,ATMBUF]	; Check to insure special chars are quoted
	SETZ D,			; Assume no quotes required
	CALL SPCCHK		;  ..
	 MOVEI D,42		; Quotes required, supply 'em
	MOVE A,HD.DAT(C)	; Point to text space
	HRLI A,(POINT 7,)	; Form byte pointer
	SKIPE D			; If quoting,
	IDPB D,A		;  move the quote
	CALL MOVST1		; Move 'em on out!
	SKIPE D			; If quoting,
	IDPB D,A		;  move close quote
	SETZ B,			; ASCIZ pleaze
	IDPB B,A		;  ..
	MOVE C,HBLKP		; Restore H-block pointer
	JRST GTHEX1		; Mark present and return

;Parse keyword

GTHKWD:	STKVAR <<FLDB0,10>>	; Two writeable FLDDB. blocks
	HRLI A,[FLDDB. (.CMCFM)]
	HRRI A,FLDB0		; Copy templates to writeable storage
	BLT A,3+FLDB0		;  ..
	HRLI A,[FLDDB. (.CMKEY)]
	HRRI A,4+FLDB0		; Stupid MACRO can't put both macros inside
	BLT A,7+FLDB0		;  one literal so we need two BLTs
	MOVEI A,4+FLDB0		; Pointer to second block (.CMKEY)
	HRRM A,FLDB0		; Chain to first block (.CMCFM)
	MOVE B,HBLKP		; Point to H-block
	MOVE B,HD.DAT+1(B)	; Point to keyword table
	MOVEM B,.CMDAT+4+FLDB0	; Store in 2nd function block
	MOVEI A,FLDB0		; Point to COMND arg block
	CALL RFIELD		; Parse keyword or CR
	MOVE A,CR.COD(A)	; Find out which
	MOVE C,HBLKP		; Point to H-block
	CAIN A,.CMCFM		; CR?
	JRST GTHEX0		; Yes, mark not present and return
	PUSH P,B		; Save datum returned from S%CMND
	CONFRM
	POP P,HD.DAT(C)		; Store in H-block
	JRST GTHEX1		; Mark present and return
SUBTTL Define, Retrieve, and Save command dispatchers

F%NO==F%F1			; local flag indicating "no" typed

.DEFIN:	SKIPN INIP		; If not from init file,
	TXZ F,F%RSCN		;  don't uselessly return to exec
	MOVEI A,[FLDDB. (.CMKEY,,DFNCTB)]
	CALL RFIELD
	HRRZ A,(B)		; Get routine address
	CALL (A)
	RET

;Retrieve commands

.RETRI:	MOVEI A,[FLDDB. (.CMKEY,,RETRCM)]
	CALL RFIELD
	HRRZ A,(B)
	CALL (A)
	RET


.SAVTL:	MOVEI A,[FLDDB. (.CMKEY,,SVTLTB,,<outgoing-messages>)]
	CALL RFIELD		; Parse keyword
	HRRZ A,(B)		; Get routine address
	CALLRET (A)		; Go to it

SUBTTL Save-outgoing-messages (in file)

.SAVMS:	NOISE (in file)
   TOPS20<
	HRROI A,[ASCIZ /txt/]	; Default extension
	MOVEM A,CJFNBK+.GJEXT	;  ..
   >;End TOPS20
   TOPS10<
	SETZM CJFNBK		; Zero previous fields
	MOVE A,[CJFNBK,,CJFNBK+1]
	BLT A,CJFNBK+CJFNLN-1
	MOVSI A,(SIXBIT /TXT/)
	MOVEM A,CJFNBK+.FDEXT	; Default extension
	MOVE A,MYPPN		; Put outgoing mail into my PPN
	MOVEM A,CJFNBK+.FDPPN	;  ..
   >;End TOPS10
	CALL GETPRS		; Parse filespec, don't open
	 JRST [	DMOVE A,SVMFOB		; No filespec given, just release this
		SKIPE A			;  if one to release
		CALL RELFOB		;  ..
		SETZM SVMFOB		;  ..
		SETZM SVMFOB+1		;  ..
		RET]
	DMOVE A,SVMFOB		; Release previous FOB
	SKIPE A			;  if any
	CALL RELFOB		;  ..
	DMOVE A,OUTFOB		; Save this away in a safe place
	DMOVEM A,SVMFOB		;  ..
   TOPS10<
	MOVE A,FOB.FD(B)	; Point to FD
	MOVE B,MYPPN		; Get my PPN in case needed
	SKIPN .FDPPN(A)		; PPN supplied by user?
	MOVEM B,.FDPPN(A)	; No, default to logged-in PPN then
   >;End TOPS10
	MOVX A,F2%NSV
	ANDCAM A,FLAGS2		; Reset "suppress save" bit
	RET

SUBTTL Expunge command


.EXPUN:	NOISE (deleted messages)
	CONFRM			; Confirm first
	SKIPG MSGJFN
	 JRST [	WARN (No current mail file)
		RET]
	JRST EXPUNG


SUBTTL Read mode commands

.SKIM:	SAVMSX			; Save context if necessary
	STKVAR <<RPROMP,3>,RHNDLR> ; Prompt string, handler routine address
	MOVEI A,RPROMP		; Built byte pointer
	HRLI A,(POINT 7,)	;  ..
	MOVEM A,UPDPTR		; Set pointer up for $TEXT
	$TEXT (UPDTOR,<MS skim^A>)
	MOVEI A,TYPHDR		; Handler for skim mode (type header line)
	MOVEM A,RHNDLR		; Set up for common code
	CALLRET .READ0		; Join common code

.READ:	SAVMSX			; Save context if necessary
	STKVAR <<RPROMP,3>,RHNDLR> ; Prompt string, handler routine address
	MOVEI A,RPROMP		; Build byte pointer
	HRLI A,(POINT 7,)	;  ..
	MOVEM A,UPDPTR		;  ..
	$TEXT (UPDTOR,<MS read^A>)
	MOVEI A,.RTYP0		; Handler routine which types message
	MOVEM A,RHNDLR		; Set up for common code

.READ0:	CALL CHECKT		; Check for recently arrived mail
	SKIPE REDLVL		; Recursive read level?
	JRST [	CALL DFSQTH		; Yes, default to current, not new
		JRST .READ1]
	CALL DFSQNW		; Get sequence, default to new
.READ1:	AOS REDLVL		; Count depth of recursion
	MOVE A,MSGSEQ
	ADD A,[POINT 18,0,17]
	LDB A,A
	CAIN A,777777		; Any messages selected?
	JRST [	WARN <No messages match this specification>
		JRST RQUIT0]
	MOVE A,REDLVL		; Get depth of this read level
	SOJLE A,.READ2		; If first level, no recursion level nonsense
	$TEXT (UPDTOR,<(^D/A/) ^A>) ; Type recursion level
.READ2:	MOVE A,UPDPTR		; Add the two wedgie brackets
	MOVEI B,">"		;  ..
	IDPB B,A		;  ..
	IDPB B,A		;  ..
	SETZ B,			; ASCIZ pleaze
	IDPB B,A
READ0:	CALL NXTSEQ		; Get next message
	 JRST [ CALL SETREF	  ; None, update last time file was read
		JRST RQUIT0 ]	  ; All done
	MOVEM L,SAVEL		; Save current msg sequence pointer
	CALL CHKDEL		; Dont if deleted msg
	 JRST REDRET
	CALL @RHNDLR		; Call read/skim handler routine

REDRET:	MOVE L,SAVEL		; Restore msg sequence pointer
	CALL CMDINI		; Init this level
REDCLP:	HRROI A,RPROMP		; Point to prompt string
	CALL DPROMP		; Prompt user
	MOVE A,REDPTR		; Point to command table
	TXZ F,F%VBTY		; Default is not verbose-type
	CALL RFIELD		; Parse a command
	HRRZ A,(B)		; Dispatch
	CALL (A)
	TXZN F,F%ESND		; Want to send something
	JRST REDCLP		; Keep going
	SETZM LSTCHR		; Setup for send
	CALL ERSAL1		; Erase all but text
	CALL SEND0
	JRST REDCLP		; Continue
;Read level commands

.RQUIT:	NOISE (read mode)
	CONFRM			; Confirm first
	CALL UPDBIT		; Update this message
	POP P,A			; Dump return address in read level loop
RQUIT0:	SOS REDLVL		; Count levels of read level
	RESMSX			; Restore context if still in a read level
	CALL @SCRRGR		; Undo fancy scroll-region stuff
	CALL @SCRBTM		; Get to bottom of screen if need be
	SETZM SCRLFL		; Reset scroll-region flag
	CALL CHECK0		; Any new messages?
	 RET			; No, quit now
	CALL CHECKS		; Yes, print the message
	TXZ F,F%RSCN		; Don't quit, user probably wants to read 'em
TOPS10<	CALL ECHOON >		; In case monitor command
	RET			; Return to caller (top level)

.RDNXT:	NOISE (message in sequence)
.RNEX0:	CONFRM
	CALL UPDBIT		; Update message bits
	POP P,A			; Flush unused return address
	JRST READ0		; Step to next message

.RBACK:	NOISE (to previous message in sequence)
	JRST .RPRV0
.RPREV:	NOISE (message in sequence)
.RPRV0:	CONFRM
	CALL UPDBIT		; Update message bits
	MOVNI A,2		; Back byte pointer up one msg
	ADJBP A,L		;  ..
	MOVE B,MSGSEQ		;**
	SUBI B,1
	ADD B,[POINT 18,0,17]
	CAMN A,B
	JRST [	WARN (There are no messages prior to this one in this sequence)
		RET]
	MOVE L,A
	POP P,A			; Flush unused return address
	JRST READ0		; Step to next message

.REXIT:	NOISE (and update message file)
	CONFRM
.REXIZ:	CALL UPDBIT		; Update this message
.REXI0:	CALL RQUIT0		; Unwind
	SKIPE REDLVL		; Completely unwound yet?
	JRST .REXI0		; No, keep unwinding
	CALLRET .EXIT0		; Exit

SUBTTL Send mode commands

;SSEND command -- do a send without entering text mode

.XSEND:	NOISE <message -- going directly to send level>
	CONFRM
	CALL SNDINI		; Initialize buffers, etc.
	JRST SEND1

; ZSEND - Send but suppress saving of outgoing message

.ZSEND:	MOVX A,F2%NSV
	IORM A,FLAGS2
	CALL .SEND
	RET

;Normal SEND command

.SEND:	NOISE (message)
	CALL SNDINI		; Reset fields
	MOVEI A,[FLDDB. .CMCFM]	; Either CR or addresses must follow
	CALL RFLDE		; See which it is
	 JRST [	CALL GETMS0		; Addresses - parse message
		JRST SEND0]		;  and go handle
	CALL GETMSG		; Prompt for message
SEND0:	MOVE A,LSTCHR		; Get last character
	CAIN A,32		; ESC - wants more stuff
	 CALL SSEND0		; ^Z - just send if off then
SEND1:	TXZ F,F%ESND		; Clear this
SNDRET:	TXZE F,F%ESND		; Want auto send?
	JRST [	CALL SSEND0		; Yes - do it
		JRST SEND1]		; Failed, stay at send level
	CALL CMDINI		; Init this level
SNDLUP:	PROMPT (MS send>>)
	TXZ F,F%VBTY		; Default is not verbose-type
	MOVE A,SENPTR		; Point to command set
	CALL RFIELD		; Parse a command
	HRRZ A,(B)		; Dispatch
	CALL (A)		;  ..
	TXZN F,F%ESND		; Want to send it now?
	JRST SNDLUP		; Nope
	CALL SSEND0		; Yes - off it goes
	JRST SEND1		; Failure, stay at send level (success
				;  returns to next level, not here)
.ZSSND:	NOISE (message without saving in outgoing mail file)
	CONFRM
	MOVX A,F2%NSV
	IORM A,FLAGS2
	JRST SSEND0

.SSEND:	NOISE (message)
	CONFRM			; Make sure if just null command
SSEND0:	TXZ F,F%ESND		; Clear this here in case its set
	CALL SNDMSG		; Send it off and fall thru
	 RET			; Failed, enter (or remain in) send level
	JRST SQUIT0

.SQUIT:	NOISE (send mode)
	CONFRM			; Confirm first
	SKIPG MSGJFN		; Do we have a message file?
	 JRST SQUIT0		; No, then this cann't be a reply, move on
	GTMBL (M,B)		; Get ptr to message block
	MOVX A,M%RPLY		; Check if reply being done for
	TDNN A,MSGBTS(B)	;  this message
	 JRST SQUIT0		; No - go on
	LDB C,[POINT 12,MSGBTS(B),17]	; Yes
	TXNN C,M%RPLY		; See if previous reply in file bits
	 ANDCAM A,MSGBTS(B)	; No - clear this reply then
SQUIT0:	POP P,A			; Dump useless return address
	TXZ F,F%ESND		; Not in send command any more
	RET			; Return to caller of send level
.VSTYP:	TXO F,F%VBTY		; Set "verbose type" flag
.STYPE:	SKIPG MSGJFN		; Have a message file?
	JRST [	WARN (No current mail file)
		TXZ F,F%VBTY
		RET]
	MOVEM F,SAVF
	JSP F,SAVMSQ		; Save message sequence context
	MOVE F,SAVF
	CALL .TYPE		; Call type routine
	MOVEM F,SAVF
	JSP F,RESMSQ			; Restore context
	MOVE F,SAVF
	TXZ F,F%VBTY
	RET			; And return

.SEDIT:	NOISE (field)
	MOVEI A,[FLDDB. (.CMKEY,,EDCMTB,,<text>)]
	JRST .ERAS2		; Get field to edit

.ERASE:	NOISE (field)
	MOVEI A,[FLDDB. (.CMKEY,,ECMDTB,,<text>)]
	CALL RFIELD
	SKIPA
.ERAS2:	CALL CFIELD		; Parse keyword and confirm
	HRRZ A,(B)
	CALLRET (A)

.DISPL:	NOISE (field)
	MOVEI A,[FLDDB. (.CMKEY,,DCMDTB,,<all>)]
	JRST .ERAS2

.RETUR:	NOISE (for this message)
	CONFRM
	SETO E,
	MOVEI	B,[asciz/Return-receipt-requested-to/]
	MOVEM	B,HDIO		; store name for .DFHDA
	HRLI	B,(POINT 7,)	; byte pointer to asciz string
	SKIPN	A,HDITAB	; see if name already exists
	 JRST	.RR1		; table is empty - this header is new
	$CALL	S%TBLK		; table is nonempty - look up this entry
	TXNE	B,TL%EXM	;exact match?
	TXOA	F,F%F1		; yes - we need to replace old block
.RR1:	TXZA	F,F%F1		;no - don't try to replace nonexistant block
	MOVEM	A,TENT1		; yes - address of old block entry is here
	MOVX	B,HD%OPT!HD%ADR	;set optional bit and ADDRESS bit
	MOVEM	B,FLG		;save the flags
	ANDI	B,HD%TYP	;get header type
	SETZM	HDI1		;so we know if .DFHDA fails
	CALL	.DFHDA		;go to define code to build header block
	SKIPN	HDI1		;make it?
	RET			;.DFHDA didn't have room for it
	PROMPT	(Return-receipt-requested-to: )
	MOVE	A,HDI1		;go ask for the argument now
	CALL	GETHDI		;..
	 RET			;GETHDI already complained
	RET			;all set

SUBTTL Send level commands - include (header-item)

.INCLU:	STKVAR <<.INCL0,2>>
	NOISE (header-item)
	DMOVE A,[FLDDB. (.CMKEY)]
	DMOVEM A,.INCL0		; Build writeable FLDDB block on stack
	SKIPN A,HDITAB		; Pointer to header-item table
	 CERR (No header-items defined)
	MOVEM A,.CMDAT+.INCL0	; Stuff into FLDDB block
	MOVEI A,.INCL0		; Set up for COMND
	CALL CFIELD		; Parse keyword and confirm
	MOVE E,B		; Put in right AC for later
	HRRZ A,(E)		; Address of H-block for item
	MOVE B,HD.FLG(A)	; Get flags
	TXNN B,HD%PDF		; Predefined?
	CALLRET INCLUD		; No, go on ahead then
	WARN <Header-item is predefined, use "define" command to change>
	RET


;Include user-defined header-item.  Prompts user for it and stores data.
;Call:	E/ Address of entry in HDITAB for item
;Returns +1: always

INCLUD:	MOVE A,[POINT 7,STRBUF]	; Where to form name and colon
	HLRZ B,(E)		; Get address of header-item's name
	HRLI B,(POINT 7,)	; Form byte pointer
	CALL MOVSTR		; Move name
	MOVEI B,":"		; Colon space (for prompt)
	IDPB B,A		;  ..
	MOVEI B," "		;  ..
	IDPB B,A		;  ..
	SETZ B,			; Insure ASCIZ
	IDPB B,A		;  ..
	MOVE A,[POINT 7,STRBUF]	; Point to prompt string
	CALL DPROMP		; Prompt
	HRRZ A,(E)		; Address of H-block
	CALL GETHDI		; Parse it
	 JFCL			; Error msg already printed
	RET			; Return
;Insert file or message

.INSER:	MOVEI A,[FLDDB. (.CMKEY,,INSCTB,,<file>)]
	CALL RFIELD
	HRRZ A,(B)		; Get routine address
	CALLRET (A)		;  and dispatch to it


.INSFI:
   TOPS20<
	SETZM CJFNBK+.GJEXT	; [ESM] No default extension
   >;End TOPS20
   TOPS10<
	SETZM CJFNBK		; Zap previous defaults
	MOVE A,[CJFNBK,,CJFNBK+1]
	BLT A,CJFNBK+CJFNLN-1
	SETZM CJFNBK+.FDEXT	; [ESM] No default extension
   >;End TOPS10
	CALL FSPEC		; Get a file spec
	 RET			; Just CR - ignore
	CALL RDTEXT		; Get contents of file
	 RET			; Error - just return
	RET
;Insert message into message

.INSMS:	SAVMSX			; Save context maybe
	MOVEI A,INSMSG		; Action routine address
	MOVEI B,[ASCIZ / Inserted: /]
	CALLRET .FLAGX		; Clean up and return

;Insert one message into current message

INSMSG:	GTMBL (M,B)		; Get ptr to message block
	MOVE V,MSGBOD(B)	; Get char pointer to message body
	CALL REMAP
	PUSH P,V
	SUB V,WBOT
	MOVE A,MSGFAD
	IMULI A,5
	ADD V,A
	CHR2BP			; Form byte pointer in A
	POP P,V
	MOVE B,MSGBON(B)	; Get size of msg body
	CALLRET TXTCPT		; Insert counted string to text buff and return

SUBTTL Send level commands - save-draft

.SAVE:	MOVEI A,[FLDDB. (.CMKEY,,SVCMTB,,<draft>)]
	CALL RFIELD		; Parse keyword
	HRRZ A,(B)		; Get routine address
	CALLRET (A)		; Go to it


.SAVDF:	NOISE (in file)
   TOPS20<
	HRROI A,[ASCIZ /draft/]	; Default extension
	MOVEM A,CJFNBK+.GJEXT	;  ..
   >;End TOPS20
   TOPS10<
	SETZM CJFNBK		; Zap previous defaults
	MOVE A,[CJFNBK,,CJFNBK+1]
	BLT A,CJFNBK+CJFNLN-1
	MOVSI A,(SIXBIT /DRF/)	; Default extension
	MOVEM A,CJFNBK+.FDEXT
   >;End TOPS10
	CALL GETNEW		; Get file, open for write (not append)
	 JRST [	WARN (No file specified)
		RET]
 	MOVE A,[POINT 7,HDRPAG]	; First must build header text
	MOVEM A,OBPTR
	CALL MOVTO		; Just need to, cc, and subject
	CALL MOVCC
	TXO F,F%F1		; Want CRLF first
	CALL MOVSUB
	MOVEI B,[BYTE (7) 15, 12, 0]	; Separate hdrs from text
	CALL MOVSB2		;  ..
	SETZ A,			; Tie this off with null
	IDPB A,OBPTR		;  ..
	MOVE A,OUTIFN		; IFN of draft file
	TXO F,F%F3		; Don't put the trailing dashes in
	CALL SAVDRF		; Write headers and text
	 JFCL			; Don't care (msg already typed)
	DMOVE A,OUTFOB		; Release chunks
	CALL RELFOB		;  ..
	SETZM OUTIFN
	RET
SUBTTL Reply command

.REPLY:	CALL DFSQTH		; Get range arg
REPRET:	CALL NXTSEQ		; Next message in list
	 RET			; Done
	CALL CHKDEL		; Deleted?
	 JRST REPRET		; Yes - skip it
	CALL CMDINI		; Init this level
	MOVE A,[POINT 7,STRBUF]	; Setup prompt string in strbuf
	MOVEM A,UPDPTR		; Put byte ptr where TOR can get to it
	MOVEI B,1(M)		; Message #
	$TEXT (REPRE0,< Reply message number ^D/B/ to: ^A>)
	SETZ A,			; Insure ASCIZ
	IDPB A,UPDPTR		;  ..
	HRROI A,STRBUF		; Point to prompt string
	CALL DPROMPT
	CALL .RRPL1		; Used common reply code
	JRST REPRET		; Loop over all in list


;Here by $TEXT macro above to stuff bytes

REPRE0:	IDPB A,UPDPTR
	RET
.RREPL:	NOISE (to)
.RRPL1:	TXNE F,F%RPAL		; Want default of all?
	JRST [	MOVEI A,[FLDDB. (.CMKEY,,RPCMTB,,<all>)]
		JRST .ERAS2]
	MOVEI A,[FLDDB. (.CMKEY,,RPCMTB,,<sender-only>)]
	JRST .ERAS2

.REPAL:	TXOA F,F%F3		; Say reply to everyone
.REPTO:	 TXZ F,F%F3		; Say just reply to sender
	TXZ F,F%CC!F%AT		; Clear some bits
	SETOM TRYSND		; Only try sender once
	CALL SNDINI		; Erase drafts
	GTMBL (M,MX)		; Pointer to message block
	CALL CONREP		; Construct reply lines (In-reply-to,Regarding)
	CALL REPSUB		; Construct the subject
;	SKIPE V,MSGSND(MX)	; Use "sender" field if there
;	JRST .REPLX		;   ..
	MOVE V,MSGFRM(MX)	; Find "from" field (for hostname defaulting,
	JUMPE V,.REPL3		;  even if reply-to field present)
.REPLX:	CALL SETSFL
	PUSH P,V
	SUB V,WBOT
	MOVE A,MSGFAD
	IMULI A,5
	ADD V,A
	CHR2BP
	POP P,V
	MOVEI W,TCPAG-1		; Where to build address list
	SETZ E,			; No host name defaulting
	CALL PRADDR		; Get the guy
;**;[3097] Add 2 lines at .REPLX+11L	MDR	18-NOV-87
	CAMN B,[-1]		;[3097] Did we get something?
	JRST .REPLQ		;[3097] No, get it by hand
	HRRM W,TOPTRS		; Starting to pointer
	SETZ E,			; assume default
	TXNN F,F%AT		; Was there an @ in the main name?
	 JRST .REPL3		; No, leave default at null
	MOVE E,FRENAM		; Yes, point to first name
.REPL6:	ILDB B,E
	JUMPE B,[SETZ E,	; If node name removed (because local node),
		JRST .REPL3]	;  then don't default node name
	CAIE B,"@"		; Start it just after the @
	 JRST .REPL6

.REPL3:	MOVEI T,[ASCIZ /
Reply-to: /]
	PUSH P,E		; Clobbered by FNDHDR
	CALL FNDHDR		; Reply-to field present?
	 JRST [	POP P,E			; No, use from field then
		JRST .REPL0]		;  ..
	POP P,E
	HRRZ W,TOPTRS		; Yes, add to list (reply to all)
	SKIPN W			; Valid starting pointer there?
	MOVEI W,TCPAG-1		; No, make one up then
	TXNE F,F%F3		;  or only use this one?
	JRST .REPL5		; Reply-to-all -- skip deletions
	PUSH P,A		; Save pointer to "Reply-to" field
	HRRZ A,@NAMTAB		; Release name table
	ADDI A,1		; Length
	SKIPE B,NAMTAB		; Address
	CALL M%RMEM		; ZAP
	SETZM NAMTAB		;  ..
	POP P,A			; Restore string pointer
	MOVEI W,TCPAG-1		; Reset addr list (but keep "from"
;	JRST .REPL5		;  string in name space for host defaulting)
;Reply (cont'd.)

.REPL5:	PUSH P,F		; Save state of hostname flag
	CALL PRADDR		;  so hostname defaulting (at PRTOCC) works
	POP P,F			; Restore flags
	HRRM W,TOPTRS		; Save this address
.REPL0:	HRRZ A,TOPTRS		; See if any names found ("from" or "reply-to")
	JUMPE A,.REPL2		; No, go ask user then
.REPL4:	TXZN F,F%F3		; Wants reply to all addresses?
	 JRST .REPL1		; No, have enuf now
	MOVE V,MSGTO(MX)	; Yes, point to "To:" list
	CALL SETSFL
	PUSH P,V
	SUB V,WBOT
	MOVE A,MSGFAD
	IMULI A,5
	ADD V,A
	CHR2BP			; Form byte pointer
	POP P,V
	CALL PRTOCC		; Get to and cc lists
	TXNE F,F%RPIN		; Including me in replies?
	JRST .REPL1		; Yes, don't remove myself
	MOVEI U,MYDIRS		; Remove me from the list
	SETZ A,			; Not removing list, just single name
	CALL DOUNTO
.REPL1:	MOVE M,MSGNUM(MX)	; Restore M as msg number
	CALL GETUHD		; Prompt for required header-items
	CALL GETTXT		; Get text of reply
	GTMBL (M,B)		; Get ptr to message block
	MOVX A,M%RPLY		; Mark message as replied to
	IORM A,MSGBTS(B)	; Careful about updating bits
	CALLRET SEND0		; And go get more or send it off

.REPL2:	SKIPE	V,MSGSND(MX)	; Is there at least a SENDER?
	AOSE	TRYSND		; Yes, did we attempt this stunt once?
	JRST	.REPLQ		; None, or tried and failed, just ask
	WARN (No FROM or REPLY address in message - trying SENDER)
	JRST	.REPLX		; We can at least try this...
.REPLQ:	WARN (Cannot tell who message is from) ;Pretty odd message!
	CALL GETTO		; Ask him who it's to then...
	HRRZ A,TOPTRS		; Anything supplied?
	JUMPE A,.REPL4		; No, don't loop...
	JRST .REPL0
SUBTTL CONREP - Construct reply lines (In-reply-to and Reference)

;Must be called with MX set up, not M

CONREP:	STKVAR <REPDAT,REPPTR>
	MOVE A,MSGDAT(MX)	; Get date message was sent
	MOVEM A,REPDAT		; Save for a bit
	MOVE A,[POINT 7,REPLIN]	; Point to where this junk will go
	MOVEI B,[ASCIZ /References: /]
	CALL MOVSTR
	SKIPN V,MSGFRM(MX)	; Sender known?
	JRST [	MOVEI B,[ASCIZ /Your message of /]
		JRST CONRP1]		; No, just mumble then...
	MOVEI B,[ASCIZ /Message from /]
	CALL MOVSTR		; Yes, say something intelligent
	MOVEM A,REPPTR		; Preserve pointer for a bit
	CALL SETSFL
	PUSH P,V
	SUB V,WBOT
	MOVE A,MSGFAD
	IMULI A,5
	ADD V,A
	CHR2BP			; Get ptr to name
	POP P,V
	MOVE C,A
	MOVE B,MSGFRN(MX)	; Get msg's length
CONRP0:	ILDB A,C		; Next byte of name
	IDPB A,REPPTR		; Stuff it
	SOJG B,CONRP0		; Until done
	MOVE A,REPPTR		; Set up for MOVSTx again
	MOVEI B,[ASCIZ / of /]	; Make grammatical
	MOVE C,MSGFRN(MX)	; Get length of "from"
	CAIL C,^D24		; Will continuing on this line exceed 72 chars?
	MOVEI B,[ASCIZ /
              of /]		; Yes, make a continuation line then
CONRP1:	CALL MOVSTR
	MOVE B,REPDAT
   TOPS20<
	MOVX C,<OT%NSC!OT%NCO!OT%TMZ!OT%SCL>
	ODTIM			; Must use ODTIM because GLXLIB doesn't
   >;End TOPS20			;  do time zones
   TOPS10<
	MOVEM A,UPDPTR		; Stash PTR or IFN for TOR
	$TEXT (UPDTOR,<^H/B/^A>)
	MOVE A,UPDPTR		; Get updated byte pointer
   >;End TOPS10
	SKIPN V,MSGMID(MX)	; Message-ID exist for this message?
	JRST CONRP3		; No, all done then
	MOVEI B,[ASCIZ /
In-reply-to: /]			; Yes, include in reply then
	CALL MOVSTR
	MOVEM A,REPPTR		; Save pointer for a bit
	CALL SETSFL
	PUSH P,V
	SUB V,WBOT
	MOVE A,MSGFAD
	IMULI A,5
	ADD V,A
	CHR2BP			; Form BP to message-ID
	POP P,V
	MOVE C,A		; Copy
	MOVE B,MSGMIN(MX)	; Length of message-ID
CONRP2:	ILDB A,C		; Get next byte of message-ID
	IDPB A,REPPTR		; Stuff it
	SOJG B,CONRP2
	MOVE A,REPPTR
CONRP3:	MOVEI B,[BYTE (7) 15, 12, 0]	; Tie everything off
	CALLRET MOVST0
SUBTTL REPSUB - Construct subject for reply from subject of msg being answered

;Call with MX, not M, set up

REPSUB:	SKIPN A,MSGSUB(MX)
	 RET			; No subject
	MOVE C,MSGSUN(MX)	; Size of subject field
	CAILE C,<STRBSZ*5>-1	; [ESM] Don't overflow buffer!
	 MOVEI C,<STRBSZ*5>-1
	MOVE B,[POINT 7,STRBUF]
	CALL FORMSS		; Move it to temp space
	SETZ D,
	IDPB D,B		; And a null
	MOVE A,[POINT 7,ATMBUF]	; Where to build string
	MOVE B,STRBUF		; Get start of original subject string
	ANDCM B,[<BYTE (7) 40,40,0,0,177>+1] ; Uppercase and clear last byte
	CAMN B,[ASCIZ /RE: /]	; Already a response?
	 JRST REPSB1		; Yes, dont propogate Re: 's
	MOVEI B,[ASCIZ /Re: /]	; No, make a Re:
	CALL MOVSTR
REPSB1:	MOVEI B,STRBUF		; From here
	CALL MOVST0		; Move remainder of subject and a null
	SKIPE A,SUBJEC		; Release old subject, if any
	CALL RELSB		;  ..
	SETZM SUBJEC
	CALL CPYATM		; Copy string we built into new block
	 JRST [	WARN <Can't set subject, insufficient memory>
		RET]
	MOVEM A,SUBJEC
	RET

SUBTTL Repair undeliverable mail
.REPAI:	TRVAR <<DFOB,2>,DIFN,DBUF,DPGS,DSIZ>
	NOISE (undeliverable mail in .RPR file)
	MOVEI A,[FLDDB1 (.CMNUM,CM%SDH,^D10,<-1,,HPTEXT>)]
	CALL RFIELD		; Read the file number
	MOVE E,B		; Save the integer
	CONFRM
	CAIL E,0		;Must be a valid number
	CAIL E,^D10000
	JRST [WARN <Number must be between 0 and 9999>
              RET]

	MOVE A,[Z.DRFB,,Z.DRFB+1] ;Set up BLT to zero FOB and FD
	SETZM Z.DRFB		;Clear first word
	BLT A,Z.DRFE		;Zero DRF file's FOB and FD
	MOVEI A,DRFFD		;Point to FD
	MOVEM A,DRFFOB+FOB.FD	;Save in the FOB
	MOVEI A,7		;Byte size of the DRF file
	MOVEM A,DRFFOB+FOB.CW	;Save in the control word
	MOVEI A,FDXSIZ		;Get size of FD
	HRLZM A,DRFFD		;And save it in the FD
	CALL CHNSIX		;Change integer to SIXBIT file name
TOPS10	<
	MOVEM C,DRFFD+.FDNAM	;SAVE NAME ON -10
	MOVSI A,'DSK'		;Device
	MOVEM A,DRFFD+.FDSTR	;Place in the FD
	MOVSI A,'RPR'		;Extension
	MOVEM A,DRFFD+.FDEXT	;Place in the FD
	MOVE A,MYPPN		;PPN
	MOVEM A,DRFFD+.FDPPN	;Place in the FD
>
TOPS20	<$TEXT (<-1,,DRFFD+.FDFIL>,<^T/MYSTR/[^T/MYDIRS/]^W/C/.RPR.1;P777700^0>)>
	MOVEI A,FOB.MZ		;FOB size
	MOVEI B,DRFFOB		;FOB address
	DMOVEM A,DFOB		;Save for RETRIEVE processing routine
	$CALL F%IOPN		;Open file for output
	JUMPF [CAIN A,ERFNF$	  ;File does not exist?
	       WARN <No such dead letter>
	       CAIE A,ERFNF$	  ;File does not exist?
	       WARN (Could not open dead letter)
	       RET]
	MOVEM A,DIFN		;Save IFN for retrieval
	MOVX B,FI.SIZ		;Get the size of the file
	CALL F%INFO
	JUMPE A,[WARN <Dead letter is empty>
		 MOVE A,DIFN 	  ;Get the IFN
		 CALL F%REL	  ;Close the file
	         RET]

	SETZM DSIZ		; Init size in bytes of draft
	SETZM DBUF		; No buffer pages yet
	PUSH P,A		; Save file size
	CALL SNDINI		; Init draft
	POP P,A			; Restore file size
	SETOM RPRHNP		; At SEND level we'll know it's a REPAIR
	CALL .RESD		; RETRIEVE DRAFT and then repair
	SKIPE RPRHNP		; Was the message sent?
	RET			; No, so return now
	DMOVE A,DFOB		; Yes, so delete it
	CALL F%DEL
	RET

CHNSIX:	MOVE C,[SIXBIT/MS0000/]	;Init result
	MOVE D,[POINT 6,C,35]	;Get pointer to end of the SIXBIT file spec

CHNSI2:	IDIVI E,^D10		;Peel off a digit
	ADDI T,20		;Convert integer to SIXBIT
	DPB T,D 		;Store into C
	ADD D,[6B5]		;Back up byte pointer
	JUMPN E,CHNSI2		;Loop if more to do
	RET			;Return to next higher level

HPTEXT:	ASCIZ/Type in the four digits from the POSTMASTER
message Repair (RPR) file
/

SUBTTL Retrieve commands - retrieve last-message

;Recover-last-message -- puts user back into send level after having
; sent something and belatedly realizing that, say, an address was
; missing

.RECOV:	NOISE (and enter send level)
	CONFRM
	SKIPE TOPTRS		; See if address lists empty
	JRST .RESD2		; No, go ahead with it then
	SKIPN A,TXTPTR		; No addresses, is there any text?
	JRST .RECV2		; Nope, this is silly then
	SKIPN B,TXTFPG		; Are there any text pages in the list?
	 JRST .RECV2		; No, complain
	ADD B,[POINT 7,TB.TXT]	; Form virgin ptr for comparison
	CAME A,B		; Is TXTPTR virgin?
	JRST .RESD2		; No, OK
.RECV2:	WARN (There is no previous message draft)
	RET
SUBTTL Retrieve commands - retrieve saved-draft

;Retrieve saved-draft -- parses saved draft and enters send mode

.RESDF:	TRVAR <<DFOB,2>,DIFN,DBUF,DPGS,DSIZ>
				; FOB, IFN, bfr addr, pages, size (bytes)
	NOISE (from file)
	SETZM DSIZ		; Init size in bytes of draft
	SETZM DBUF		; No buffer pages yet
	CALL SNDINI		; Init draft
   TOPS20<
	HRROI A,[ASCIZ /draft/]	; Default extension
	MOVEM A,CJFNBK+.GJEXT	;  ..
   >;End TOPS20
   TOPS10<
	SETZM CJFNBK
	MOVE A,[CJFNBK,,CJFNBK+1]
	BLT A,CJFNBK+CJFNLN-1	; Zap previous fields
	MOVSI A,(SIXBIT /DRF/)	; Default extension
	MOVEM A,CJFNBK+.FDEXT
   >;End TOPS10
	CALL FSPEC		; Get a IFN
	 JRST [	WARN (No file specified)
		RET]
	DMOVEM A,DFOB		; Save FOB info
	$CALL F%IOPN		; Open for read
	JUMPF [	WARN (Can't read draft)
		DMOVE A,DFOB
		CALLRET RELFOB]
	MOVEM A,DIFN		; Save IFN
	MOVX B,FI.SIZ		; Get size of file in bytes
	$CALL F%INFO		;  ..
.RESD:	IDIVI A,5*1000		;  ..
	ADDI A,1		; Round up
	MOVEM A,DPGS		; Remember how many we take
	$CALL M%AQNP		; Get the pages
	JUMPF [	WARN (Can't read draft file -- insufficient memory)
		JRST .RESD1]		; Release file blocks and return
	LSH A,^D9		; Compute address of buffer
	MOVEM A,DBUF
	HRLI A,(POINT 7,)	; Point to it
	MOVE C,A		; Safer AC
;	JRST .RESD0
.RESD0:	MOVE A,DIFN
	$CALL F%IBYT		; Get a byte
	JUMPF [	CAIE A,EREOF$		; EOF?
		WARN (Error reading draft)
		JRST .RESD1]		; Release file blocks
	JUMPE B,.RESD0		; Ignore nulls
	AOS DSIZ		; Count bytes in draft
	IDPB B,C		; Stuff into text pag
	JRST .RESD0		; Keep going
.RESD1:	SETZ A,			; Insure ASCIZ
	IDPB A,C		;  ..
	MOVE A,DIFN
	$CALL F%REL		; Close file
	DMOVE A,DFOB		; Release file info blocks
	SKIPN RPRHNP		; No FOB to release if from REPAIR
	CALL RELFOB
	MOVE A,DBUF		; Address of buffer
	HRLI A,(POINT 7,)	; Point to draft
	SKIPE B,DSIZ		; Size of draft, in bytes
	CALL PRSDRF		; Parse the draft
	 CALLRET .RESDX		; Error - release pages and return now
	CALL .RESDX		; Release buffer pages
.RESD2:	CALL .DSALL		; Type current draft
	SETZM LSTCHR		; No special action
	JRST SEND0		; Enter send mode

;Release buffer pages, if any, used by .RESDF

.RESDX:	SKIPN B,DBUF		; Any buffer allocated?
	RET			; No
	LSH B,-^D9		; Yes, form page number
	MOVE A,DPGS		; Number of pages
	$CALL M%RLNP		; Release 'em
	RET
SUBTTL Retrieve commands - retrieve saved-draft - PRSDRF - parse draft

;Here to parse a draft and insert good info into send buffer
;Call:
;	A/ Byte pointer to draft
;	B/ Byte count
;	CALL PRSDRF
;Return	+1: failure, probably bad syntax in draft
;	+2: OK, send buffers all set up

PRSDRF:	STKVAR <DRFSIZ,DRFPTR>	; Size of draft, pointer to it
	MOVEM A,DRFPTR		; Save pointer
	MOVEM B,DRFSIZ		;  and size
	MOVEI A,TCPAG-1		; Init to list pointer
	MOVEM A,TOPTRS		;  ..
	MOVE A,DRFPTR		; Get pointer to draft again
	BP2CHR			; Form character pointer
	MOVEM V,DRFPTR		; Remember for later
	MOVE W,DRFSIZ		; Length of draft
	MOVEI T,[ASCIZ /
To: /]				; Look for addressee lists
	CALL SSEARC		;  ..
	 JRST [	WARN (Can't find To field in draft)
		JRST PRSDR0]
	SETZ E,			; No hostname defaulting
	CALL PRTOCC		; Fetch to and cc lists into new draft
	MOVE B,TOPTRS		; Did PRTOCC find anybody?
	CAIN B,TCPAG-1		;  ..
PRSDR0:	SETZM TOPTRS		; No, don't confuse MOVTO then
	MOVE V,DRFPTR		; Point at start again
	MOVE W,DRFSIZ		;  ..
	MOVEI T,[ASCIZ /
Subject: /]			; Find subject
	CALL SSEARC		;  ..
	 JRST PRSDR1		; Not there
	MOVE B,[POINT 7,ATMBUF]	; Make temp copy in ATMBUF
PRSDR2:	ILDB C,A		; Next byte
	CAIN C,15		; Stop at CR
	JRST PRSDR3		;  ..
	IDPB C,B
	JRST PRSDR2
PRSDR3:	SETZ A,			; Put null at end
	IDPB A,B		;  ..
	SKIPE A,SUBJEC		; First release old subject
	CALL RELSB		;  ..
	SETZM SUBJEC		;  ..
	CALL CPYATM		; Now set new one from ATMBUF
	 JRST [	WARN <Can't set subject, insufficient storage>
		JRST PRSDR1]
	MOVEM A,SUBJEC
;	JRST PRSDR1
	; ..

PRSDR1:	MOVE V,DRFPTR		; Search through entire msg
	MOVE W,DRFSIZ		;  ..
	MOVEI T,[ASCIZ /

/]				; For end of header area (two CRLFs)
	CALL SSEARC		;  ..
	 JRST RSKP		; No text, I guess
	CALL TXTPUT		; Ok, move everything up to null to text area
	RETSKP			; Give good return
SUBTTL COPY, FILE, and MOVE commands - Move messages into files

;COPY just sopies the message
;MOVE copies and then deletes
;FILE copies and then asks the user if deletion is desired (a la EMS)

.FILE:	DMOVE A,[PUTMSG
		 [ASCIZ / Filed: /]]
	CALL .MOVE0		; Call common code
	CALL CMDINI		; Init this level
	SKIPE REDLVL		; Read level?
	JRST .FILE0		; Yes, be a little cleverer about the prompt
	PROMPT < Delete this message from current message file? >
	JRST .FILE1
.FILE0:	PROMPT < Delete from current message file the message(s) just filed? >
.FILE1:	CALL YESNO		; Get a yes or no
	 RET			; No, just return
	SKIPE REDLVL		; Read level?
	CALLRET DELMSG		; Yes, this is easy
	DMOVE A,[DELMSG		; No, set up for SEQUEN
		 [ASCIZ / Deleted: /]]
	DMOVEM A,DOMSG		; Save dispatch
	SETOM LSTMSG		; Re-init message sequencer states
	MOVE L,MSGSEQ		;**
	ADD L,[POINT 18,0]
	CALLRET SEQUE0		; Delete 'em and return


YESNO:	MOVEI A,[FLDDB. (.CMKEY,,<[2,,2
				   [ASCIZ /no/],,0
				   [ASCIZ /yes/],,1]>,,<no>)]
	CALL CFIELD		; Get the answer
	HRRZ A,(B)		; Get the code
	JUMPE A,R		; 'no' -- nonskip
	RETSKP

;Just like YESNO only default (CR) is yes.
NOYES:	MOVEI A,[FLDDB. (.CMKEY,,<[2,,2
				   [ASCIZ /no/],,0
				   [ASCIZ /yes/],,1]>,,<yes>)]
	CALL CFIELD		; Get the answer
	HRRZ A,(B)		; Get the code
	JUMPE A,R		; 'no' -- nonskip
	RETSKP
.PUT:	DMOVE A,[PUTMSG
		 [ASCIZ / Copied: /]]
	SKIPA
.MOVE:	DMOVE A,[MOVMSG
		 [ASCIZ / Moved: /]]
.MOVE0:	DMOVEM A,DOMSG
	SKIPE REDLVL		; Read level?
	 JRST .RPUT1		; Yes
	CALL DFSQTH		; Get message sequence
	CALL CMDINI		; Init this level
	PROMPT (  Into file: )
   TOPS20<
	HRROI A,[ASCIZ /txt/]	; Default extension
	MOVEM A,CJFNBK+.GJEXT	;  ..
	HRROI A,[ASCIZ /DSK/]	; Default device
	MOVEM A,CJFNBK+.GJDEV	;  ..
	SETZM CJFNBK+.GJDIR	; No default for directory
   >;End TOPS20
   TOPS10<
	SETZM CJFNBK		; Zap previous defaults
	MOVE A,[CJFNBK,,CJFNBK+1]
	BLT A,CJFNBK+CJFNLN-1
	MOVSI A,(SIXBIT /TXT/)
	MOVEM A,CJFNBK+.FDEXT
   >;End TOPS10
	CALL GETOUT		; Get output file
	 JRST [	WARN (No output file specified)
		RET]
.PUT1:	CALL SEQUE0		; go handle the sequence
.PUT2:	SKIPE A,OUTIFN		; If still open,
	$CALL F%REL		;  close file
	SETZM OUTIFN
	DMOVE A,OUTFOB		; Release chunks
	CALL RELFOB		;  ..
	RET
.RPUT1:	NOISE (into file)
   TOPS20<
	HRROI A,[ASCIZ /txt/]	; Default extension
	MOVEM A,CJFNBK+.GJEXT	;  ..
  >;End TOPS20
   TOPS10<
	SETZM CJFNBK		; Zap previous fields
	MOVE A,[CJFNBK,,CJFNBK+1]
	BLT A,CJFNBK+CJFNLN-1
	MOVSI A,(SIXBIT /TXT/)
	MOVEM A,CJFNBK+.FDEXT	; Default extension
   >;End TOPS10
	CALL GETOUT		; Get output file
	 JRST [	CMERR (No output file specified)
		RET]
.RPUT2:	CALL @DOMSG		; Process it
	JRST .PUT2		; And go close it up

.LIST:	MOVEI A,LPTMSG
	MOVEI B,[ASCIZ / Listed: /]
	DMOVEM A,DOMSG
	SKIPE REDLVL		; Read level
	 JRST .RLIS1		; Yes
	CALL DFSQTH		; Get sequence
	CALL GETLPT		; Open LPT for output
	 RET			; Failure, return
	TXNN F,F%HLPT		; Headers wanted on LPT output?
	JRST .PUT1		; No, skip this then
	PUSH P,L		; Yes, save initial msg pointer
.LIST0:	CALL NXTSEQ		; Get next msg in sequence
	 JRST [	POP P,L			; Done, restore original sequence
		MOVE A,OUTIFN		; Put headers on separate page
		MOVEI B,14		;  ..
		$CALL F%OBYT		;  ..
		CALLRET .PUT1]		; Go print the messages and return
	CALL TYPHDR		; Type header for this message
	JRST .LIST0		; Go through 'em all

.RLIS1:	NOISE (on line-printer)
	CONFRM
	CALL GETLPT
	 RET			; Failure, just quit
	JRST .RPUT2
SUBTTL FORWARD and REDISTRIBUTE commands

.FORWA:	SAVMSX			; Save message sequence context, maybe
	CALL DFSQTH		; Get message sequence, default to this
.FORW0:	CALL SNDINI		; Reset message drafts
	CALL GETTO		; Get recipients
	CALL GETCC		;  ..
	CALL GETUHD		; Get required header-items
	CALL GETTXT		; Get initial comments
	MOVE A,TXTPTR		; Get pointer to text field
	MOVE B,TXTFPG		; Address of first text page
	ADD B,[POINT 7,TB.TXT]	; Form virgin text pointer
	CAMN A,B		; Is buffer empty?
	 JRST .FORW2		; Yes, no need to check crlf
	LDB C,A			; Get last char
	MOVEI A,[BYTE (7) 15, 12, 0]
	CAIN C,12		; Unless have crlf
	JRST .FORW2
	CALL TXTPUT		; Put one in
.FORW2:	CALL NXTSEQ		; Get next guy in list
	 JRST .FORW3		; Maybe send if off or get more
	CALL CHKDEL		; Dont forward deleted msgs
	 JRST .FORW2
	CALL .FORWD		; Include original message
	JRST .FORW2		; Then look for more
.FORW3:	CALL SEND0		; Send it off
	RESMSX			; Restore message sequence, maybe
	RET
;Here to move forwarded message into text buffer

.FORWD:	MOVEI A,[ASCIZ /- - - - - - - Begin message from: /]
	CALL TXTPUT
	GTMBL (M,B)		; Get ptr to message block
	SKIPN V,MSGFRM(B)	; Original sender
	 JRST [	MOVEI A,[ASCIZ /(Unknown)/]
		CALL TXTPUT
		JRST .FRWD1]
	CALL SETSFL
	PUSH P,V
	SUB V,WBOT
	MOVE A,MSGFAD
	IMULI A,5
	ADD V,A
	CHR2BP			; Form byte pointer to sender
	POP P,V
	GTMBL (M,B)		; Get ptr to message block
	MOVE B,MSGFRN(B)	; Length of from field
	CALL TXTCPT		; Move counted string to text
.FRWD1:	MOVEI A,[ASCIZ /
/]				; add a CRLF
	CALL TXTPUT		;  ..
	CALL FORMSG		; Include text
	MOVEI A,[ASCIZ /- - - - - - - End forwarded message
/]
	CALL TXTPUT		; Move this out
	RET			; And return
FORMSG:	GTMBL (M,B)		; Get ptr to message block
	SKIPN D,MSGFRM(B)	; Has an author?
	 JRST FORMS2		; No
	SKIPE A,SUBJEC		; Release existing subject string
	CALL RELSB		;  ..
	SETZM SUBJEC
	MOVE B,[POINT 7,ATMBUF]	; Make temp copy of this stuff in ATMBUF
	MOVEI C,"["
	IDPB C,B
	GTMBL (M,C)		; Get ptr to message block
	MOVE C,MSGFRN(C)	; Get length of from field
	MOVE A,D		; Get pointer back
	CALL FORMSS
	MOVEI C,":"
	IDPB C,B
	GTMBL (M,A)		; Get ptr to message block
	SKIPN A,MSGSUB(A)	; Subject field present?
	 JRST FORMS1		; No
	MOVEI C," "
	IDPB C,B
	GTMBL (M,C)		; Get ptr to message block
	MOVE C,MSGSUN(C)	; Size of subject field
	CALL FORMSS
FORMS1:	MOVEI C,"]"
	IDPB C,B
	SETZ C,
	IDPB C,B
	CALL CPYATM		; Copy this string to a newly allocated block
	 JRST [	WARN <Can't set subject, insufficient storage>
		JRST FORMS2]
	MOVEM A,SUBJEC		; Set subject string
FORMS2:	GTMBL (M,B)		; Get ptr to message block
	MOVE V,MSGBOD(B)	; body of the message
	MOVE C,MSGBON(B)	; Length
	JUMPE C,R		; No body? return
	MOVE D,V		; Start of message body in scratch AC
	ADD D,C			; Add to it the number of chars to move
	SOS D			; Last character to me moved
	CAMG D,WTOP		; Is the whole message in core?
	  JRST FORM28		; Yes, we can take the easy way out
FORM25:	CALL REMAP		; Remap to get as much in core as possible
	CAMG D,WTOP		; Is rest of the message in core?
	  JRST FORM28		; Yes, the simple ending
	MOVE C,D		; Last char to move
	SUB C,WTOP		; How many we'll have left
	PUSH P,C		; Remember for later
	MOVE C,WTOP		; Compute how many chars we will
	SUB C,V			; move this time
	AOS C			; around
	SKIPA
FORM28:	PUSH P,[0]		; This indicates that we'll be done soon
	PUSH P,V		; Save the begining of the message body
	SUB V,WBOT		; Offset into message window
	MOVE A,MSGFAD		; Beginning of message window
	IMULI A,5		; Change into a character count
	ADD V,A			; Message body starts this far into memory
	CHR2BP			; Form byte pointer to it
	POP P,V			; Restore the beginning of the message body
	MOVE D,A		; Better AC
FORMS3:	ILDB A,D		; Move all nonnull chars
	JUMPE A,FORMS4		;  ..
	CALL TXTCHR		;  to text area
FORMS4:	SOJG C,FORMS3		;  and repeat as necessary

	POP P,C			; Restore the number of bytes left
	SKIPN C			; Anything to do?
	  RET			; Nope, quit
	MOVE V,WTOP		; This is where we left off
	MOVE D,V		; Start of message body in scratch AC
	AOS V			; This is where we'll continue
	ADD D,C			; Last character to me moved
	JRST FORM25
;
;
FORMSS:	JUMPE C,R		; None to do
	MOVE V,A
	CALL SETSFL
	PUSH P,V
	SUB V,WBOT
	MOVE A,MSGFAD
	IMULI A,5
	ADD V,A
	CHR2BP			; Get byte pointer to it
	POP P,V
FRMSS1:	ILDB D,A		; Get char
	JUMPE D,FRMSS2		; Skip nulls
	IDPB D,B
FRMSS2:	SOJG C,FRMSS1
	RET
;Redistribute

.REDIS:	SAVMSX			; Maybe save context
	CALL DFSQTH		; Get sequence, default to current
	CALL SNDINI		; Init drafts
	CALL GETTO
	CALL GETCC
.REDI0:	CALL NXTSEQ		; Next message in sequence
	 JRST .REDIX		; Go send it
	CALL CHKDEL		; Don't do deleted messages
	 JRST .REDI0
	GTMBL (M,B)		; Get ptr to message block
	MOVE V,MSGBOD(B)	; Point to message body
	CALL REMAP
	PUSH P,V
	SUB V,WBOT
	MOVE A,MSGFAD
	IMULI A,5
	ADD V,A
	CHR2BP			; Form kosher byte pointer
	POP P,V
	MOVE C,A		; Better AC
	MOVE D,MSGBON(B)	; Get total length of message
.REDI1:	ILDB A,C		; Next byte of message text
	JUMPE A,.REDI2		; Don't move nulls
	CALL TXTCHR		; Move to text of this message
.REDI2:	SOJG D,.REDI1		; Count through text of redistributed message
	MOVEI A,[BYTE (7) 15, 12, 0]
	CALL TXTPUT		; Put one in
	JRST .REDI0		; Repeat for all msgs in sequence

.REDIX:	SETZM LSTCHR		; Enter send level rather than sending
	TXO F,F%ESND		; Auto send this
	TXO F,F%REDI		; Flag redistribute in progress
	CALL SNDRET		; Send the message
	TXZ F,F%REDI		; Clear redistribute flag
	RESMSX
	RET


SUBTTL CHECK command - Check for new mail

.CHECK:	NOISE (for new messages)
	CONFRM
;	CALLRET CHECKT		; Check and type stuff if new msgs

CHECKT:	CALL CHECK0		; Check for new messages
	 RET			; None
;	CALLRET CHECKS		; There are some, announce them

; Print message when there are new guys

CHECKS:
TOPS20<	CALL GETJF2		; Lock the file with a READ/WRITE JFN
	RET			; File in use
	MOVE A,MSGJFN		; Set JFN
	CALL SETREF >		; Update read date-time
TOPS10<	MOVE A,MSGJFN >		; Get JFN
	PUSH P,M		; Save current message
	MOVE M,LASTM		; Start at current end or
	PUSH P,M		;  from beginning if new file
	AOJ M,			; From that one on,
	CALL PARSEF		; Parse these new ones
TOPS20<	CALL CLSJF2> 		; Release the READ/WRITE lock
CHECK1:	POP P,A			; Get old number
	MOVEI M,1(A)		; For headers (TYPHDR)
	SUB A,LASTM		; Get number of new guys
	JUMPGE A,[POP P,A		; Clean up stack
		RET]			; None - someone's mucking the file
	MOVM A,A
	MOVEI B,[ASCIZ /are/]
	CAIN A,1
	 MOVEI B,[ASCIZ /is/]
	CIETYP < There %2S %1D additional message%P:
>
	MOVEI E,(A)		; Get number of new messages
CHECK2:	PUSH P,E		; TYPHDR is hairy and clobbers most ACs
	CALL TYPHDR		; Announce each new message
	ADDI M,1		;  ..
	POP P,E
	SOJG E,CHECK2		;   ..
	POP P,M			; Restore current message
	CIETYP < Currently at message %M.
>
	MOVEI A,^D5		; Five seconds
	CALLRET RDELAY		; Delay if read mode and exit


; Already have a READ/WRITE JFN
TOPS20<
CHECKM:	CALL CHECK0		; Check for new messages
	RET			; None
	MOVE A,MSGJFN		; Set JFN
	CALL SETREF 		; Update read date-time
	PUSH P,M		; Save current message
	MOVE M,LASTM		; Start at current end or
	PUSH P,M		; From beginning if new file
	AOJ M,			; From that one on,
	CALL PARSEF		; Parse these new ones
	JRST CHECK1 >		; Continue in common code

;Check to insure a message isn't deleted, or if return receipt was
; requested, that it's sent.
;Call:	CALL CHKDEL
;Return	+1: deleted or acknowledged refused, don't allow user access
;	+2: All OK, access allowed

CHKDEL:	MOVX A,M%DELE
	GTMBL (M,B)		; Get ptr to message block
	TDNN A,MSGBTS(B)	; Deleted?
	JRST CHKDL0
	CIETYP < Message %M is deleted.
>
	RET
CHKDL0:	CALL RRECPT		; Return receipt OK?
	 JRST [	CIETYP < Message %M has return receipt requested, but not yet sent.>
		RET]
	RETSKP


;Check to see if return receipt needs to be sent and send it if so.
;Call:	CALL RRECPT
;Return	+1: receipt requested but user refused, don't display the message,
;	    or we were unable to send the receipt
;	+2: receipt not requested, or requested and sent OK

RRECPT:	GTMBL (M,B)		; Get ptr to message block
	MOVX A,M%RSNT		; Has return receipt already been sent?
	TDNE A,MSGBTS(B)	;  ..
	RETSKP			; Yes, quit now then
	SKIPN V,MSGRRR(B)	; Is receipt requested?
	RETSKP			; No, quit then
	MOVE W,MSGRRN(B)	; Yes, get length of reply field then
	$CALL K%FLSH		; Flush output buffer
	CALL CLRFIB		; Clear typeahead, this is unexpected
	CALL TYPHDR		;
	MOVEI A,1(M)		; Get 1-origin message number
	$TEXT (KBFTOR,< Sender of message ^D/A/ has requested return receipt.>)
	CALL CMDINI
	PROMPT ( Send it ? )
	CALL NOYES
 	 JRST [ GTMBL (M,MX)
		JRST RRECP1]		;

	CALL SNDINI		; User said OK, init draft
	GTMBL (M,MX)		; Get ptr to message block
	CALL CONREP		; Construct default header like REPLY
	CALL REPSUB		; Subject too
	HRROI A,[ASCIZ / This is a RETURN RECEIPT for your message./]
	CALL TXTPUT		; Text of message
	MOVE V,MSGRRR(MX)	; Get char pointer to return receipt address
	CALL SETSFL
	PUSH P,V
	SUB V,WBOT
	MOVE A,MSGFAD
	IMULI A,5
	ADD V,A
	CHR2BP			; Form byte pointer to return receipt field
	POP P,V
	MOVEI W,TCPAG-1		; Where to store address entries
	SETZ E,			; shouldn't have to do this but...
	CALL PRADDR		; Parse the address
	HRRM W,TOPTRS		; Stuff it
	CALL SNDMSG		; Now send the receipt
	 WARN <Could not send return receipt> ;
	MOVX A,4		; Give user 4 seconds to watch this
	$CALL I%SLP		; ..
	MOVX A,M%RSNT		; Set flag saying receipt was sent
	IORM A,MSGBTS(MX)	;  ..
RRECP1:	MOVE M,MSGNUM(MX)	; Restore M
	CALL UPDBIT		; Update message bits please
	RETSKP			; Give good return
; Find the subject of the message.
; All header search routines must be called with MX, not M, set up.

FNDSUB:	MOVEI T,[ASCIZ /
Subject: /]
	CALL FNDHDR		; Try to find this header
	 JRST FNDSB3		; Not there
FNDSB1:	SETZ W,			; Count size of field in w
FNDSB2:	ILDB T,A		; Get char
	CAIE T,15		; Until the CR
	 AOJA W,FNDSB2
	RET
FNDSB3:	MOVEI T,[ASCIZ /
Re: /]				; Try this then
FNDSB4:	CALL FNDHDR
	 JRST FNDSB5		; Not there either
	JRST FNDSB1		; Found it then
FNDSB5:	SETZB V,W		; Say we didnt find it anywhere
	RET

   IFE MHACK,<

; Find the "From" field a message

FNDFRM:	MOVEI T,[ASCIZ /
From: /]
	JRST FNDSB4


; Find "Sender" field

FNDSND:	MOVEI T,[ASCIZ /
Sender: /]
	JRST FNDSB4


; Find the message-ID

FNDMID:	MOVEI T,[ASCIZ /
Message-ID: /]
	JRST FNDTO0		; Use common code

;Find reference field

FNDREF:	MOVEI T,[ASCIZ /
In-reply-to: /]
	JRST FNDTO0		; Use common code

   >;End IFE MHACK
; Find "to" field.  Returns position in V, length of first line in
; W (for headers command), length of entire field in X

FNDTO:	MOVEI T,[ASCIZ /
To: /]
FNDTO0:	CALL FNDHDR		; Find it
	 JRST [	SETZB V,W		; say didn't find it
		SETZ X,
		RET]
	SETZ W,			; Count size of first line in W
FNDTO1:	ILDB T,A		; Look for EOL
	CAIE T,15		;  ..
	AOJA W,FNDTO1		;  ..
	MOVE D,W		; OK, W has length of first line...
FNDTO2:	MOVE X,D		; Save candidate for end of field
	ADDI D,1		; Count CR in case next line is continuation
FNDTO4:	ILDB T,A		; See if next line is continuation
	SKIPE T			; Ignore nulls
	CAIN T,12		; Ignore LF
	AOJA D,FNDTO4		;  ..
	CAIE T,40		; Is first char of line Linear White Space?
	CAIN T,11		; ie., space or tab?
	AOJA D,FNDTO3		; Yes, keep counting
	RET			; Not continuation, return size of whole field
FNDTO3:	ILDB T,A		; Get next char of this line
	CAIN T,15		; Until CR
	JRST FNDTO2		; CR found, see if continuation
	AOJA D,FNDTO3		; Still in this line... count away

   IFE MHACK,<

;Find cc field, similar to FNDTO

FNDCC:	MOVEI T,[ASCIZ /
cc: /]
	JRST FNDTO0		; Join common code

;Find return-receipt, similar to FNDTO and FNDCC

FNDRRR:	MOVEI T,[ASCIZ /
Return-receipt-requested-to: /]
	JRST FNDTO0		; Common code

; (Still inside IFE MHACK)
; (Still inside IFE MHACK)

; Find the date field

FNDDAT:	MOVE V,MSGALL(MX)	; First thing in header is recv date
	CALL SETSFL		;SET STUFF FOR FILE SEARCHING
	PUSH P,V
	MOVE A,MSGFAD
	IMULI A,5
	SUB V,WBOT
	ADD V,A
	CHR2BP
	POP P,V
	SETZB B,C
TOPS20<	IDTIM
	 ERJMP [MOVE A,MSGNUM(MX)
		ADDI A,1		; Message number for error msg
		CMERR (File has bad format - message %1D has no receive date)
		SETO B,			; supply a random one (now)
		RET]
>;End TOPS20
TOPS10<
;	CHR2BP
	CALL XDATI		; *** Call date/time crock
	JUMPF [	MOVE A,MSGNUM(MX)
		ADDI A,1
		CMERR (File has bad format - message %1D has no receive date)
		SETO B,
		RET]
>;End TOPS10
	RET


FNDSDT:	MOVEI T,[ASCIZ /
Date: /]
	CALL FNDHDR
	 JRST FNDDT1		; Not there
TOPS20<	SETZB B,C
	IDTIM >			; Try to parse it, will skip on success

TOPS10<	CALL XDATI		; *** Call date/time crock
	JUMPF FNDDT1		; Failure, use receive date
	RET >			; Success, keep date just parsed

FNDDT1: MOVE B,MSGDAT(MX)	; Bad format, use recv date
	RET

>;End IFE MHACK
SUBTTL File parsing subroutines - SEARCH - fast string search

; Try to find a header in the message body

FNDHDR:	SETZ W,			; Clear counter in case message is unparseable
	SKIPN V,MSGBOD(MX)	; Start of msg body, if any
	 RET			; None, so skip it.
	MOVE W,MSGHDN(MX)	; Look in header area only
	SUBI V,2		; Include CRLF before 1st item in search
	ADDI W,2		;  because headers must begin with CRLF
	CALL SETSFL		;SET STUFF FOR FILE SEARCHING
	MOVE A,MSGFAD
	IMULI A,5
	SUB V,WBOT
	ADD V,A
	CALL SSEARC
	 RET			; No good
	AOS (P)
	BP2CHR			; Form char pointer
	ADD V,WBOT
	MOVE B,MSGFAD		;TO CHAR POINTER
	IMULI B,5		;FROM THE BEGINNING
	SUB V,B			;OF THE FILE
	RET			;  and return

SUBTTL PRADDR - Parse address lists in received mail

;Parse the rest of this line as addresses, inserting default host
; name pointed to by E, using free space from FRENAM and into list in W

PRADDR:	TRVAR <SAVB,HSTBEG,NAMBEG,<TEMP,10>,SRC>
	MOVE U,FRENAM
	MOVEM A,SRC		; Stash source string ptr
PRADD0:	TXZ F,F%AT		; No @ seen yet
	MOVEI T,(U)		; Save pointer for later
PRADD1:	ILDB B,SRC		; Get char
	CAIE B,","
	 CAIN B,15
	 JRST NXTAD1
	CAIN B," "
	 JRST PRADD1		; flush leading spaces
	HRLI U,(<POINT 7,0>)	; Make byte pointer
	MOVEM U,NAMBEG		; Save start of name string
PRADD2:	CAIN B,42		; Start of quoted string?
	 JRST PRADD9		; Yes, eat to matching quote
	CAIN B,":"
	 JRST PRADDL		; This is start of list of addresses
	CAIN B,"("		; ( - search for matching )
	 JRST PRADD4
	CAIE B,","
	 CAIN B,15		; End of line or this address
	 JRST PRADD5
	CAIN B,";"		; End of named address-list?
	 JRST PRADD5		; Yes, that ends this name as well
	CAIN B,"<"		; Opening bracket?
	 JRST PRNET6		; Yes - flush what we've got
	CAIN B,">"		; Terminating bracket?
	 JRST PRNET3		; Yes - flush remainder of address
	CAIN B,"@"		; Allow @ in net address
	 JRST PRNETB
	CAIN B," "		; Non-initial spaces
	 JRST PRNETA		; Terminate this part of it
PRADD3:	IDPB B,U		; Stick it in
	ILDB B,SRC		; Get next
	JRST PRADD2
;We've parsed the name of a list of addresses - increment list depth
; and store name

PRADDL:	MOVEI A,(T)		; Point to string
	AOS LDEPTH		; Increment depth
	TXO A,AD%PFX		; Flag this as prefix to list
PRADL0:	AOS W			; Step to next table entry location
	MOVEM A,(W)		; Store this entry
	SETZ A,			; Insure ASCIZ
	IDPB A,U		;  ..
	MOVEI U,1(U)		; Step to next free string space location
	MOVEI B,","		; Pretend comma so coming address gets scanned
	JRST NXTAD1		; Continue parsing

;Here if open wedge seen.  Store personal name and keep scanning.

PRNET6:	TXZ F,F%AT		; Forget "@" seen
	MOVEI A,(T)		; Get address of start of string
	TXO A,AD%PRN		; Light personal name flag
PRNT6A:	LDB B,U			; Get character before open wedge
	CAIE B,11		; Space or tab?
	CAIN B,40		;  ..
	JRST [	MOVNI B,1		; Yes, back up over it
		ADJBP B,U		;  ..
		MOVEM B,U		;  so we can stomp on it with a null
		JRST PRNT6A]
	JRST PRADL0		; Store and keep scanning

; Skip to ")"

PRADD4:	IDPB B,U
	ILDB B,SRC
	CAIE B,")"
	 JRST PRADD4
	JRST PRADD3


; Skip to close quote (same as PRADD4)

PRADD9:	IDPB B,U
	ILDB B,SRC
;**;[3097] Add 5 lines at PRADD9:+2L	MDR	18-NOV-87
	CAIE B,.CHCRT		;[3097] CR seen before end quote?
	IFSKP.			;[3097] If so,
	  SETO B,		;[3097] See we have none
	  RET			;[3097] And return to caller
	ENDIF.			;[3097]
	CAIE B,42
	 JRST PRADD9
	JRST PRADD3
;Here when address terminator is seen (comma, semicolon, or EOL)
; Default hostname if none given and defaulting requested
; B/ terminating character
; E/ byte pointer to default hostname

PRADD5:	TXNN F,F%AT		; "at" seen?
	CALL NETDEF		; No, default the hostname then
PRADD6:	MOVEM B,SAVB		; Save terminating character
	SETZ B,
	IDPB B,U		; End with null
	TXNN F,F%AT		; Net address?
	JRST PRADD8		; No, validate local username then
	CALL CHKHNM		; Yes, parse and validate hostname
	 JRST FLSADR		; No such name and user wants to flush
	JUMPL C,ADDAD0		; If C <> 0, net address
PRADD8:	HRRO B,T		; Local user, point to name string
   TOPS20<
	MOVX A,RC%EMO		; Exact match only
	RCUSR
	 ERJMP PRADD7		; Not a user, go see if SYSTEM
	TXNN A,RC%NOM		; Match?
	JRST ADDAD0		; Yes - add to list
   >;End TOPS20
   TOPS10<
	HRLI B,(POINT 7,)	; Form byte pointer to name
;
;  Ask me NO questions and I'll tell you NO lies!
;  We won't even talk about what a crock this is.
;  We need to verify the local user here - we ain't.
;
;	MOVE A,USRTAB		; See if known local user
;	$CALL S%TBLK		;  ..
;	TXNE B,TL%EXM		; Exact match?
	MOVEI C,1
;	JRST [	HRRZ C,(A)		; Yes, get ptr to PPN
		JRST ADDAD0		; Go add to table
;]
   >;End TOPS10
PRADD7:	HRRO A,T		; See if special
	HRROI B,[ASCIZ "SYSTEM"]
	$CALL S%SCMP		; See if strings match
TOPS20<	JUMPN A,PRADDE		; Jump if no match (no user on PS:)>
TOPS10<	JUMPN A,[CALL NOUSER	; TOPS-10 does the old thing because
		 JRST FLSADR]	; it doesn't use POBOX:
>;End TOPS10
	MOVEI C,SYSCOD		; Match, supply code
	JRST ADDAD0		;  and proceed
TOPS20<
PRADDE:	HRRO A,T		; Now check to see if on POBOX:
	SETZ B,			; Flag we have just username
	CALL CHKPBX
	IFNSK.
	 CALL NOUSER		; Not on POBOX:, definitely no user!
	 JRST FLSADR		; Complain, and flush the address
	ELSE.
	 JRST ADDAD0		; User is there on POBOX:, fine.
	ENDIF.
>; End TOPS20
;Routine to insert the default hostname, pointed to by E

NETDEF:	SKIPN D,E		; Is there a default hostname?
	RET			; No, return
	MOVEI C,"@"		; Yes, do the atsign
	IDPB C,U		;  ..
	MOVEM U,HSTBEG		; Save pointer to hostname for later
	TXO F,F%AT		; Flag that we have a net address
NETDF1:	ILDB C,D		; Move hostname now
	JUMPE C,[RET]		; If null, return
	IDPB C,U
	JRST NETDF1


;No such user name - issue warning

NOUSER:	CITYPE <% No such user: >
	MOVE A,NAMBEG		; Print name parsed
	$CALL KBFTOR
	$TEXT (KBFTOR,< - ignored^M>)
	RET
;CHKHNM - Check for valid host name
;Call: HSTBEG/ pointer to host name
;Return	+1: no such name and user decided to flush the address, or no network
;	+2: OK, C = 0 if local host name, C = -1 if remote host name

CHKHNM:	STKVAR <<CHKHN0,6>>	; Temp space for FLDDBs
	TXNN F,F%ARPA!F%DECN!F%ANFX	; Have a net here?
	JRST [	CALL NOUSER		; No, complain about the address
		RET]			;  and flush the address
;	SKIPN HOSTAB		; Have host table?
;	CALL HSTINI		; No - get one now
;	MOVE A,HOSTAB		; Point to table
;	MOVE B,HSTBEG		; Host name to lookup
;	$CALL S%TBLK		; See if in table
;	TXNN B,TL%EXM		; Exact match only!
	MOVE A,HSTBEG		; Get the pointer to the host name
	CALL VALID8		; Check it out
	 JRST CHKHN2		; Oops - ask user for help
	HRRZ A,(B)		; Get node block pointer
   Repeat 0,<
	TXNE F,F%XMLR		; XMAILR/HOSTS2 type host table?
	JRST [	MOVE A,N.SITE(A)	; Yes, get ptr to site table entry
		CAMN A,LSITE		; Is this the local host?
		JRST CHKLCL		; Yes, treat differently
		SETO C,			; Nope - set net flag
		RETSKP]
   >;End Repeat 0
	MOVE A,N.FLGS(A)	; Non-XMAILR -- get host flags
	TXNN A,NT%LCL		; Local host?
	JRST [	SETO C,			; No, set net flag
		RETSKP]
CHKLCL:	SETZ C,			; Local host - zap host name with leading null
	DPB C,HSTBEG		;  ..
	RETSKP			; Good return

CHKHN2:	$TEXT (KBFTOR,<% No such host: ^Q/NAMBEG/>)
CHKHN3:	WARN < Enter new host name or CR to ignore.
>
	PROMPT <Host: >
;**;[3099] Change 1 line at CHKHN3:+2L	MDR	20-NOV-87
	MOVEI A,[FLDBK. (.CMFLD,,,<-1,,HSTHLP>,,[BRMSK.(FLDB0.,FLDB1.,FLDB2.,FLDB3.,<-#.>)])] ;[3099] Parse host (maybe ARPA)
	SKIPN HOSTAB		; Is there anything in the host cache?
	IFSKP.			; Yes, so point to it
	 DMOVE B,[FLDDB. (.CMCFM)]
	 DMOVEM B,CHKHN0		; Build writeable FLDDB blocks on stack
	 HRRZI B,2+CHKHN0	; 2nd FLDDB goes here
	 HRRM B,.CMFNP+CHKHN0	; Link to 1st
	 HRLI B,[FLDDB1 (.CMKEY,,,<-1,,HSTHLP>)]
	 BLT B,5+CHKHN0		;  ..
	 MOVEI B,2+CHKHN0	; Point to the chain
	 MOVE C,HOSTAB		; Address of host table
	 MOVEM C,.CMDAT(B)	; Set into FLDDB block
	 MOVEI B,CHKHN0		; Get head of chain
	 HRRM A,.CMFNP(B)	; Chain .CMFLD function onto it
	 EXCH A,B		; Add it onto the list
	ENDIF.			; and rejoin...
	CALL RFIELD		; Read the host name
	MOVE C,CR.COD(A)	; Get the function code
	CAIN C,.CMCFM		; Skip if it wasn't a Confirm
	 RET			; It was, so flush the address
	CAIE C,.CMFLD		; Skip if it was a .CMFLD
	IFSKP.			; It was, so...
	 HRROI A,ATMBUF		; Node name is here
	 CALL VALID8		; And check it out
	 IFNSK.			; Invalid host again?
	  $TEXT(KBFTOR,<% Invalid host name: ^T/ATMBUF/.>)
	  JRST CHKHN3		; Try again
	 ENDIF.			; Invalid host name
	ENDIF.			; .CMFLD validation
	HRRZ A,(B)		; Get pointer to node block
	MOVE A,N.FLGS(A)	; Get flags for this node
	TXNE A,NT%LCL		; Local node?
	JRST CHKLCL		; Yes, throw away node name then
	HLR B,(B)		; No, get address of node name string
	MOVE A,(B)		; Get potential flags word
	TLNN A,(177B6)		; First byte empty?
	TXNN A,CM%FW		; And flags bit lit?
	SKIPA			; No, must be text
	ADDI B,1		; Yes, skip to text word then
	HRLI B,(POINT 7,)	; Form byte pointer
	MOVE A,HSTBEG		; Where old (bad) hostname begins
	CALL MOVST0		; Overwrite with good name
	MOVE U,A		; Update new free pointer
	CONFRM			; Get CRLF
	SETO C,			; Flag as net address
	RETSKP			; Give good return
; Add address to list c(C) := user number or code
;	-1 := net address
;	-2 := SYSTEM
;	 0 := no known address
; c(T) := pointer to name string

ADDAD0:	HRRZ B,C		; User number or code
	HRL B,T			; Pointer to string
	MOVEI A,NAMTAB		; Name string table
	CALL TBADDS		; Attempt to add
	 JUMPF FLSADR		; Reclaim space (dupl entry)
	AOS W			; Step to next entry
	HRRZM T,(W)		; Save pointer to string
	MOVEI U,1(U)
ADDAD1:	MOVE B,SAVB		; Restore terminator character
ADDAD2:	CAIE B,";"		; End of named list?
	JRST NXTADR		; No, check for comma
	SOSGE LDEPTH		; Watch nesting level
	JRST [	WARN (Too many terminators in named address list)
		SETZM LDEPTH
		JRST .+1]
	AOS W			; Make room for next entry
	MOVX C,AD%SFX		; Stuff the suffix into the list
	MOVEM C,(W)		;  ..
	ILDB B,SRC		; Get char after semicolon
	MOVEM B,SAVB		; For NXTADR
	JRST ADDAD2		; Check for nested lists

;Flush current address because of some bogosity and keep parsing

FLSADR:	MOVEI U,(T)		; Reclaim unused string
;**;[3099] Change 1 line at FLSADR:+1L	MDR	30-NOV-87
	CAILE W,TCPAG		;[3099] Watch those boundary conditions!
	CAIN W,TCPAG+NTCENT-1	;   ..
	JRST ADDAD1		; Nothing special to do here if list empty
	MOVX A,AD%PRN		; Get personal-name bit
	TDNN A,(W)		; Is previous entry a personal-name field?
	JRST ADDAD1		; No, skip this
	SOJA W,ADDAD1		; Yes, flush the personal name too

;Go on to next address in the list

NXTADR:	MOVE B,SAVB		; Restore break character
NXTAD1:	CAIN B,","		; more names?
	 JRST NXTAD2		; Yes - check for ,<crlf>
NXTAD4:	HRRZ T,FRENAM		; No - end of line then
	MOVEM U,FRENAM		; Update free space
	CAIE T,(U)		; If no names gotten,
	 JRST NXTAD3
	TXNN F,F%CC		; Must undo update to pointer
	 HRRZ W,TOPTRS
	TXNE F,F%CC
	 HLRZ W,TOPTRS
NXTAD3:	MOVE A,SRC		; Return updated source pointer to caller
	SKIPN LDEPTH		; Insure all named lists terminated
	RET			; OK, return to caller
	WARN <Message has bad format:  unterminated named address list>
	MOVX C,AD%SFX		; Generate all terminators required
NXTAD5:	AOS W			; Next loc in list please
	MOVEM C,(W)		; Hallucinate a terminator
	SOSE LDEPTH		; In case nested lists, do all levels
	JRST NXTAD5		;  ..
	RET


;Comma seen - check line continuation

NXTAD2:	MOVE A,SRC		; Get temp source pointer for lookahead
NXTADS:	ILDB B,A		; Peek ahead to next char
	CAIE B," "		; Allow space, tab after comma
	CAIN B,.CHTAB
	JRST NXTADS
	CAIE B,15		; Maybe <CR>?
	JRST PRADD0		; No, just parse next address then
	ILDB B,A		; Yes, skip <LF> also
	MOVEM A,SRC		; Update source pointer
	ILDB B,A		; See if next line starts with LWSP
	CAIE B,40		; Does it start with space or tab?
	CAIN B,11		;  ..
	JRST PRADD0		; OK, this is continuation - get next address
	JRST NXTAD4		; Nope -- this line has spurious comma then
;Check possible net address

PRNETA:	ILDB B,SRC
	CAIN B," "
	 JRST PRNETA
	CAIN B,"@"		; Allow space-atsign-space host delimiter
	 JRST PRNETB		;  ..
	CALL ATP		; Is this the word "at"?
	 JRST [	MOVEI B," "		; No, assume multi-word username.
		IDPB B,U		; Insert the space...
		LDB B,SRC	; Re-prime the pump with next nonspace
		JRST PRADD2]		;  character and keep scanning.
	MOVEI B,"@"
PRNETB:	IDPB B,U		; Got the at, start it out
	TXO F,F%AT
	MOVEM U,HSTBEG		; Save start of host name
PRNET1:	ILDB B,SRC
	CAIN B," "
	 JRST PRNET1		; Flush any intermediate spaces
PRNET2:	IDPB B,U
	ILDB B,SRC
	CAIN B,">"		; Terminating bracket?
	 JRST PRNET3		; Yes - skip to end
	CAIN B,";"		; End of address list?
	 JRST PRADD6		; Yes, add this addr and check for next
	CAIE B,","		; End of single address?
	 CAIN B,15		;  ..
	 JRST PRADD6		; Yes, tie off string and validate
	CAIE B," "		; Eat trailing spaces
	 JRST PRNET2
	CALL ATP		; Is this the word "at"?
	 JRST PRNET3		; No, assume trailing whitespace
PRNET0:	SETZ B,			; Yes, tie off the string so far (ASCIZ)
	IDPB B,U		;  ..
	EXCH U,HSTBEG		; Save this host ptr, restore preceding
PRNT0A:	MOVEI A,TEMP		; Copy the preceding hostname to TEMP
	HRLI A,(POINT 7,)	;  ..
	MOVEI B,[ASCIZ /@/]	;  only 1st replace " at " with "@"
	CALL MOVSTR		;  ..
	MOVE B,U		; Point to beginning of preceding hostname
	CALL MOVST2		; Move preceding hostname to TEMP, with null
	MOVNI A,1		; Form byte pointer to preceding hostname - 1
	ADJBP A,U		;  so we will stomp on the @
	MOVEI B,TEMP		; Move " at <preceding-host-name>" on top
	HRLI B,(POINT 7,)	;  of "@<preceding-host-name>"
	CALL MOVST1		;  ..
	MOVE U,A		; Point to end of preceding hostname
	MOVEI B,"@"		; Fetch real hostname marker
	JRST PRNETB		; Go do the hostname bit again

PRNET3:	ILDB B,SRC
	CAIN B,"("		; Handle comment
	 JRST SKPCOM
	CAIE B,","		; Flush the rest of this address
	 CAIN B,15
	 JRST PRADD6		; Tie off string and validate
	JRST PRNET3


;Try to parse the word "at", followed by a space.  Call with B already
; containing the suspect for the letter "a", or leading whitespace
; before the suspect, and SRC pointing to it.
;
;Return	+1: failure, SRC not changed
;	+2: success, SRC moved over the word and the trailing space

ATP:	CAIE B," "		; Do we have leading whitespace to skip?
	JRST ATP0		; No
	ILDB B,SRC		; Yes, gobble it upt
	JRST ATP		;  ..
ATP0:	CAIE B,"a"
	CAIN B,"A"		; Allow either case
	SKIPA A,SRC		; Is an "a", fetch the source pointer
	RET			; Oops, failure
	ILDB B,A		; Get candidate for "t"
	CAIE B,"t"
	CAIN B,"T"
	SKIPA
	RET
	ILDB B,A		; Now check for space
	CAIE B," "
	RET
	MOVEM A,SRC		; Winnage, update SRC
	RETSKP			;  and give skip return


;Flush this field

SKPADR:	MOVEI U,(T)
SKPAD1:	ILDB B,SRC
	CAIE B,","
	 CAIN B,15
	 JRST NXTAD1
	JRST SKPAD1


;Here on open paren (personal name)

SKPCOM:	PUSH P,T		; Save current start of real address
	SETZ B,			; Insure ASCIZ
	IDPB B,U		;  ..
	MOVEI U,1(U)		; Step to next free string space location
	HRLI U,(POINT 7,)	; Form byte pointer
	HRRZ T,U		; Save start address of this string
SKPCM0:	ILDB B,SRC		; Get next character of personal name
	CAIN B,")"		; End?
	JRST SKPCM1		; Yes
	IDPB B,U		; No, keep storing
	JRST SKPCM0		;  ..
SKPCM1:	MOVEI A,(T)		; Get start address of string
	TXO A,AD%PRN		; Light personal-name flag
	AOS W			; Store entry in address list
	MOVEM A,(W)		;  ..
	POP P,T			; Restore start of actual address
	JRST PRNET3
; Get to and cc lists from message

PRTOCC:	HRRZ W,TOPTRS		; Where to store more of list
	TXZ F,F%CC		; Not in CC yet
PRTO11:	CALL PRADDR		; Parse this line
	IBP A			; Move over the LF too
	ILDB B,A		; Get next char
	CAIE B,"T"		; More to maybe
	 CAIN B,"t"
	 JRST PRTO20
	CAIE B,"C"		; Or maybe start of cc
	 CAIN B,"c"
	 JRST PRTO30
PRTO12:	TXNN F,F%CC		; If doing to still
	 HRRM W,TOPTRS		; Update to list
	TXZE F,F%CC
	 HRLM W,TOPTRS		; Else cc
	RET			; And done
PRTO20:	ILDB B,A
	CAIE B,"O"
	 CAIN B,"O"
	 CAIA
	 JRST PRTO12
	ILDB B,A
	CAIE B,":"
	 JRST PRTO12		; No good I guess
	JRST PRTO11		; Get rest of this line then
PRTO30:	ILDB B,A
	CAIE B,"C"
	 CAIN B,"c"
	 CAIA
	 JRST PRTO12
	ILDB B,A
	CAIE B,":"
	 JRST PRTO12
	TXOE F,F%CC		; Now doing cc
	 JRST PRTO11		; Already was
	HRRM W,TOPTRS		; Update list of to's
	HLRZ W,TOPTRS		; Get list of cc
	JUMPN W,PRTO11		; Already a list started
	MOVEI W,TCPAG+NTCENT-1	; No, start it now
	JRST PRTO11		; And go get more

SUBTTL .RTYPE and .RVBTY - Read-level type (verbose-type) commands

.RVBTY:	TXO F,F%VBTY
.RTYPE:	MOVEI A,[FLDDB. (.CMKEY,,RTYPKW,<
Name of the part of this message you want displayed,>,<everything>)]
	CALL RFIELD		; Get keyword
	HRRZ A,(B)		; Get routine address
	CALL (A)		; Dispatch to it
	TXZ F,F%VBTY		; Clear verbose flag
	RET

;Type everything

.RTYPA:	CONFRM
	CALL CHKDEL		; Can we do this?
;**;[3089] Change 1 line at .RTYPA:+2L	MDR	8-APR-87
	 JFCL			;[3089] Deleted messages are always TYPEable
.RTYP0:	CALL BLANK0		; Clear screen
	CALL TOPLIN		; Type top (summary) line of screen
	MOVEI A,1		; Init line counter
	MOVEM A,LFCNT		;  ..
	SETZ A,			; We're not selecting any particular headers
	CALL TYPMHD		; Type message headers, if distinguishable
	 JRST [	CALL TYPLIT		; Lossage, type message literally then
		RET]			;  and return
	CALL @SCRREG		; Init scrolling region if desired
	CALL TYPBOD		; Type message body
	CALL MRKMSG		; Mark message as having been seen
	CALL SETREF		; Update the last time message file was read
	RET			;  and return

;Type text

.RTYPT:	CONFRM
	CALL CHKDEL		; Can we do this?
	 RET			; No, msg already printed
	CALLRET TYPBOD		; This is the easiest

;Command table for read-level TYPE command

RTYPKW:	RTYPK0,,RTYPK0
	CMD (Everything,.RTYPA)
	CMD (Header-items,.RTYPH)
	CMD (Text,.RTYPT)
RTYPK0==.-RTYPKW-1
;Type header-items

.RTYPH:	STKVAR <TBL0,IDX,PTR>	; Ptr to table of headers, index, string ptr
	MOVEI A,^D100		; Space for TBLUK table of header-names
	$CALL M%GMEM		; Get a chunk
	MOVEM B,TBL0		; Remember its address
	MOVEI A,^D99		; Init table count
	MOVEM A,(B)		;  ..
	MOVE A,B		; Pass table address to KWDLST
	CALL KWDLST		; Parse a list of keywords
	MOVE A,TBL0		; Compact the table now
	CALL COMPAC		; Waste not, want not!
	CALL CHKDEL		; Can we do this?
	 JRST [	MOVE A,TBL0		; No, release storage
		CALLRET KWDREL]		;  and quit
	MOVE B,TBL0		; Point to table header
	HLRZ A,(B)		; Get count of header-items requested
	JUMPE A,[CALL TYPMHD		; None, type all of headers
		  JFCL			; Can't distinguish them, ignore this
		 RET]			; Return
	SETZM IDX		; Init current index
.RTYP2:	CALL CRIF		; Insure we're at left margin
	AOS C,IDX		; Count header-items
	MOVE B,TBL0		; Point to table
	HLRZ B,(B)		; Get count of table entries
	CAILE C,(B)		; Is current index greater than entry count?
	JRST [	MOVE A,TBL0		; Yes, all done, release storage
		CALLRET KWDREL]		;  for keyword table and return
	MOVNS B			; No, negate count
	HRL B,B			; Form AOBJN pointer
	HRR B,TBL0		;  ..
	ADDI B,1		; Skip header word
.RTYP3:	HRRZ A,(B)		; Get this entry's index
	CAIN A,(C)		; The one we want this pass?
	JRST .RTYP4		; Yes, go handle it then
	AOBJN B,.RTYP3		; Loop through table
	FATAL <Badly-formed keyword table at .RTYP3>

.RTYP4:	HLRZ A,(B)		; Get pointer to this header-item's name
	HRLI A,(POINT 7,)	; Form kosher byte pointer
	MOVEM A,PTR		; Save for possible later use
	CALL TYPMHD		; Type this header-item
	 JRST [	MOVE A,PTR	; Point to losing name
		WARN <Header-item "%1S" not found in message>
		JRST .RTYP2]	; Continue through list
	JRST .RTYP2		; Continue through list

SUBTTL .TYPMS and .VBTYP - Top- and send-level type (verbose-type) commands

.VBTYP:	TXO F,F%VBTY		; Set verbose flag
.TYPMS:	CONFRM			; Confirm first
TYPMSG:	CALL TOPLIN		; Type first summary line
	SETZ A,			; Don't select particular header-item
	CALL TYPMHD		; Type message headers
	 JRST [	CALL TYPLIT	; Headers not distinguished, type literally
		RET]		;  and return
	CALL TYPBOD		; Type message body
	CALL MRKMSG		; Mark message as seen
	RET			;  And return

SUBTTL Message typeout and display routines

;Type top (summary) line

TOPLIN:	GTMBL (M,B)		; Get ptr to message block
	MOVX A,M%VALI		; Have we parsed this message yet?
	TDNN A,MSGBTS(B)	;  ..
	CALL PRSMS0		; No, do so then
	GTMBL (M,A)		; Get ptr to message block
	MOVE C,MSGBON(A)	; Get length of message
	MOVEI B,1(M)		; Make external msg number to type
	$TEXT (KBFTOR,< Message ^D/B/ (^D/C/ chars), received ^A>)
   TOPS10<
	$TEXT (KBFTOR,<^H/MSGDAT(A)/>)
   >;End TOPS10
   TOPS20<
	$CALL K%FLSH		; Fancy date/time output please
	GTMBL (M,B)		; Get ptr to message block
	MOVE B,MSGDAT(B)	; Date/time
	MOVX A,.PRIOU		; Output to terminal
	MOVX C,OT%DAY!OT%FDY!OT%FMN!OT%4YR!OT%DAM!OT%SPA!OT%NSC!OT%TMZ!OT%SCL
	ODTIM			; Fancy date/time output
	CALL CRLF
   >;End TOPS20
	RET


SUBTTL RECENT - type out headers of recent messages

RECENT:	TXO F,F%F2		; Want headers
RECEN0:	STKVAR <PRIORM>
	SETZB M,NFLAGD		; Init counts
	SETZM NDELET
	SETZM UNSEEN		; ...
	SETOM PRIORM		; No new messages yet
	CALL CRIF		; Get fresh line if needed
RECEN1:	GTMBL (M,B)		; Get ptr to message block
	TXNE F,F%MOD		; Mod hack?
	 CALL RECMOD		; Yes - special test for new msgs
	MOVE A,MSGBTS(B)	; Get flags
	TXNE A,M%DELE		; Deleted?
	 AOS NDELET		; Count deleted ones
	TXNE A,M%ATTN		; Flagged?
	 AOS NFLAGD		; Count 'em
	TXNE A,M%SEEN		; Seen this one?
	 JRST RECEN2		; Yes - skip it
	AOS UNSEEN		; Count unseen messages
	SKIPGE PRIORM		; If this is our first unseen
	MOVEM M,PRIORM		; Save first unseen
	TXNE F,F%F2		; Header?
	CALL TYPHDR		; Yes - tell him what it's about
RECEN2:	CAMGE M,LASTM		; Thru with all msgs?
	 AOJA M,RECEN1		; No
	SKIPGE M,PRIORM		; Set current message to first unseen
	SETZB M,PRIORM		;  Else use first message
	TXZ F,F%F2		; Don't leave stray bits lying around
	RET

; Special routine to update M%SEEN for system-messages

RECMOD:	MOVX W,M%SEEN		; Bit to twiddle
	SKIPLE A,MSGDAT(B)	; Get recv date of message
	CAMG A,LASTRD		; Check against last read date
	 JRST [	IORM W,MSGBTS(B)	; Mark as seen (ie not new)
		RET]
	ANDCAM W,MSGBTS(B)	; Not seen - assume new
	RET
SUBTTL SNDMSG - send the current message off

SNDMSG:	SKIPN A,TOPTRS		; Must have some addresses
	JRST [	WARN (No addresses specified)
		RET]
	TRNN A,-1		; Must have some To people too
	JRST [	WARN <No TO, only CC>
		RET]
	SKIPG B,TXTPTR		; Get ptr to terminator
	JRST [	HRLI B,(POINT 7,,34)	; If funny (nonexistent byte),
		SUBI B,1		;  correct
		JRST .+1]
	MOVEI A,[BYTE (7) 15, 12, 0]
	LDB C,B
	CAIE C,12		; Unless ended with CRLF
	CALL TXTPUT		;  tack one on
	TXZ F,F%QDEC!F%QARP	; Note no queued mail yet
	CITYPE <Processing mail...>
	$CALL K%FLSH
	$CALL I%NOW		; Get current date/time
	MOVEM A,MSGID0		; Save for construction of message-ID
	SETO A,			; This job
	MOVX B,JI.JNO		; Job number for message-ID
	$CALL I%JINF		;  ..
	MOVEM B,MSGID1		;  ..
	MOVX B,JI.USR		; PPN or usernumber
	$CALL I%JINF		;  ..
	HRRZM B,MSGID2		; Only less significant half
	MOVX B,JI.RTM		; Also runtime in msec
	$CALL I%JINF		;  ..
	HRRZM B,MSGID3		; Only need low-order part, really
;	..			;  continued on next page
;	..			; continued from previous page
	SKIPE A,SVMFOB		; Saving outgoing mail?
	JRST [	MOVX B,F2%NSV		; Suppress this one?
		TDZE B,FLAGS2		;  ..
		JRST .+1		; Yes, skip it
		MOVE B,SVMFOB+1		; Yes, do it up
		CALL CRIF		; Left margin, please
		MOVE C,FOB.FD(B)	;  now to FD for message
		$TEXT (KBFTOR,<Message filed in ^F/(C)/ ^A>)
		PUSH P,B
		$CALL K%FLSH
		POP P,B
		SKIPE RPRHNP		; From REPAIR?
		SOS RPRHNP		; Yes
		CALL SAVMSG		;  ..
		 JRST [	DMOVE A,SVMFOB		; Failure, release chunks
			CALL RELFOB		; ..
			SETZM SVMFOB		; Stop saving messages
			WARN (No more messages will be saved)
			JRST .+1]
		$TEXT (KBFTOR,<- OK>)
		JRST .+1]
	$CALL K%FLSH		; This might take a while, so speak to the user
	CALL DELIVR		; Go deliver the mail
	 RET			; Failure, give nonskip
	RETSKP

; Get user number from table , string pntr c(u)

GETUNM:	MOVE A,NAMTAB		; Table header
	HRRZ B,(U)		; String pointer
	$CALL S%TBLK		; Lookup entry
	HRRE B,(A)		; Get code or user number
	RET

 SUBTTL Message draft editing and display routines

ERSAL1:	SKIPE A,SUBJEC		; Release block if one exists
	CALL RELSB		;  ..
	SETZM SUBJEC		; Reset subject
	SETZM TOPTRS		; Reset to and cc pointers
	SETZM REPLIN		; No reply lines
	SETZM SVABLK		; No saved A-block
	MOVE A,[POINT 7,NAMTXT]
	MOVEM A,FRENAM		; Reset free string pointers
	HRRZ A,@NAMTAB		; Release name table
	ADDI A,1
	SKIPE B,NAMTAB
	CALL M%RMEM
	SETZM NAMTAB
	SKIPN E,DEFCC		; Any default cc-list?
	JRST ERSAL4		; No, skip this
ERSAL0:	MOVEI W,TCPAG+NTCENT-1	; Yes, init cc list pointer
ERSL0B:	HRL B,AB.ADR(E)		; Address of string to LH
	HRR B,AB.COD(E)		; Code to RH
	MOVEI A,NAMTAB		; Enter in NAMTAB
	CALL TBADDS		;  ..
	HRRZ A,AB.COD(E)	; Get code again
	CAIN A,SFXCOD		; Suffix?
	JRST [	MOVX B,AD%SFX		; Yes, get appropriate magic bit
		JRST ERSL0A]
	MOVE B,AB.ADR(E)	; Get address of string
	CAIN A,PFXCOD		; Is this a prefix?
	TXO B,AD%PFX		; Yes, light the bit
ERSL0A:	AOS W			; Step through list
	MOVEM B,(W)		; Stuff into list
	SKIPE E,AB.LNK(E)	; Any more entries?
	JRST ERSL0B		; Yes, keep going
	HRLM W,TOPTRS		; Set cc list pointer
ERSAL4:	SKIPN A,HDITAB		; Header-item table exist?
	JRST ERSAL2		; No, skip this
	HLLZ E,(A)		; Point to user-defined header-items
	JUMPE E,ERSAL2		; None exist
	MOVN E,E		; Form AOBJN pointer
	HRR E,HDITAB		;  ..
	ADDI E,1		; Account for header word
	MOVX B,HD%PRS		; "Present" flag
	MOVX C,HD%PDF		; "Predefined" flag
ERSAL3:	HRRZ A,(E)		; Get addr of H-block for this one
	TDNN C,HD.FLG(A)	; Predefined?
	ANDCAM B,HD.FLG(A)	; No, clear "present" flag
	TDNE C,HD.FLG(A)	; Predefined?
	IORM B,HD.FLG(A)	; Yes, set "present" flag
	AOBJN E,ERSAL3		; Do for all
ERSAL2:	RET

.ERSAL:	CONFRM
SNDINI:	CALL ERSAL1
	SETZM RPRHNP		; Clear REPAIR flag
	JRST .ERST0
.ERSTX:	CONFRM
	TXNE F,F%REDI		; REDISTRIBUTE in progress?
	JRST [	WARN <Erasing the text of a REDISTRIBUTEed message is not allowed.>
		RET]
	CALLRET .ERST0		; Go call erase-text routine
.ERSDT:	CONFRM
	SETZM REPLIN
	RET

.ERSSB:	CONFRM
.ERSB0:	SKIPE A,SUBJEC		; Release string if one exists
	CALL RELSB		;  ..
	SETZM SUBJEC
	RET

.ERSCC:	CONFRM
	HLRZ T,TOPTRS		; get end of cc list
	JUMPE T,R		; if list empty, quit now
	MOVEI V,TCPAG+NTCENT	; and start
.ERSC2:	MOVX A,AD%SFX!AD%PRN	; Don't delete nonexistent strings
	TDNN A,(T)		;  ..
	CALL NAMDEL		; delete this name string
	CAME T,V		; done yet?
	SOJA T,.ERSC2		; no, keep going
	HRRZS A,TOPTRS		; yes, erase cc pointer
.ERSC3:	JUMPN A,R		; if names left in to list, done
	MOVE A,[POINT 7,NAMTXT]
	MOVEM A,FRENAM		; Reset free pointer
	HRRZ A,@NAMTAB		; Release name table
	ADDI A,1
	SKIPE B,NAMTAB
	CALL M%RMEM
	SETZM NAMTAB
	RET
; Erase to field

.ERSTO:	CONFRM
	HRRZ T,TOPTRS		; end of to list
	JUMPE T,R		; if list empty, quit now
	MOVEI V,TCPAG		; and start
.ERST9:	MOVE A,(T)		; Get this entry
	TXNN A,AD%SFX!AD%PRN	; Funny entry?
	CALL NAMDEL		; No, delete this name
	CAME T,V		; done?
	SOJA T,.ERST9		; no, keep going
	HLLZS A,TOPTRS		; yes, reset to pointer
	JRST .ERSC3		; clean up and return

.DSALL:	MOVE A,[$CALL KBFTOR]	; Set up to type it out to tty
	TXO F,F%LCL		; Treat local names w/o net addrs
	CALL MOVTO0
	CALL MOVCC1
	TXO F,F%F1		; want crlf before
	CALL MOVOP1		; Type header options
	CALL MOVSB1		; Type subject
	TXZ F,F%LCL
	SKIPN REPLIN		; Have reply lines?
	JRST .DSTXT		; No, skip this
	MOVEI B,REPLIN		; Yes, type them
	CALL MOVSB2
.DSTXT:	CALL CRLF
	MOVX A,.PRIOU		; Where to put text
	CALL TXTOUT		; Type it and return
	CALLRET CRIF		; CRLF if needed

.DSSUB:	TXO F,F%F1		; Want crlf before
	MOVEI B,MOVSB0
	JRST .DSCC1

.DSTO:	SKIPA B,[MOVTO0]
.DSCC:	 MOVEI B,MOVCC0
	TXO F,F%LCL		; Treat local names w/o net addrs
.DSCC1:	MOVE A,[$CALL KBFTOR]
	JRST (B)


;Erase header-item

.ERSHD:	STKVAR <<.ERSH0,2>>
	NOISE (name)
	DMOVE A,[FLDDB. (.CMKEY)]
	DMOVEM A,.ERSH0		; Build writeable FLDDB block on stack
	SKIPN A,HDITAB		; Pointer to header-item table
	IFNSK.			; No skip means no headers defined
	 WARN (There are no header items defined)
	 RET
	ENDIF.			; Say so, and exit this command
	MOVEM A,.CMDAT+.ERSH0	; Stuff into FLDDB block
	MOVEI A,.ERSH0		; Point to FLDDB block
	CALL CFIELD		; Parse header-item name and confirm
	HRRZ A,(B)		; Point to H-block
	MOVX B,HD%PRS		; Bit to clear
	ANDCAM B,HD.FLG(A)	; Clear "present" bit
	RET
MOVSUB:	MOVE A,[IDPB A,OBPTR]
MOVSB0:	MOVEM A,MOVDSP		; Set up to move into memory
MOVSB1:	SKIPN SUBJEC
	RET			; No subject, return now
	MOVEI B,[ASCII /
/]
	TXZE F,F%F1		; Want crlf
	CALL MOVSB2		; Yes
	MOVEI B,[ASCIZ /Subject: /]
	CALL MOVSB2		; Print header part
	MOVE B,SUBJEC		; Start of actual string
	CALL MOVSB2
	MOVEI B,[BYTE (7) 15, 12, 0]
MOVSB2:	HRLI B,(<POINT 7,0>)
MOVSB3:	ILDB A,B		; Get char
	JUMPE A,R		; Done
	XCT MOVDSP		; Handle it
	JRST MOVSB3


MOVCC:	MOVE A,[IDPB A,OBPTR]
MOVCC0:	MOVEM A,MOVDSP		; Set up to move into memory
MOVCC1:	MOVEI T,[ASCIZ /
cc: /]
	TXNE F,F%REDI		; REDISTRIBUTE command?
	MOVEI T,[ASCIZ /
Resent-cc: /]
	HLRZ C,TOPTRS		; Head of list
	MOVEI E,TCPAG+NTCENT
	JRST MOVTO2


;Construct and insert message-ID

MOVMID:	MOVE A,[IDPB A,OBPTR]
	MOVEM A,MOVDSP
	$TEXT (MVODSP,<Message-ID: ^A>)
	MOVEI A,"<"		; Stupid MACRO can't handle wedgies in args
	XCT MVODSP
	MOVE T,MYHSPT
	TXNE F,F%DNNM		; Are we using the DECnet host name?
	 MOVE T,MYHDPT		; Yes, change pointer to DECnet name
	$TEXT (MVODSP,<"MS^V/VERSN./+GLXLIB^V/libver/" ^D/MSGID0/.^D/MSGID1/.^D/MSGID2/.^D/MSGID3/ at ^Q/T/^A>)
	MOVEI B,[BYTE (7) ">", 15, 12, 0]
	CALLRET MOVSB2
MOVTO:	MOVE A,[IDPB A,OBPTR]
MOVTO0:	MOVEM A,MOVDSP
	MOVEI T,[ASCIZ /
To: /]
	TXNE F,F%REDI		; Redistribute command in progress?
	MOVEI T,[ASCIZ /
Resent-to: /]
	HRRZ C,TOPTRS
	MOVEI E,TCPAG

;Common code for moving address elements to draft

MOVTO2:	$SAVE <X>		; Save possible TRVAR pointer
	STKVAR <BRKF>		; Flag for wedgy brackets needed
	SETZM BRKF		; None needed yet
	JUMPE C,R		; None here, forget it
	TXZ F,F%AT		; Init flag
	SKIPA B,T		; header supplied
MOVTO3:	MOVEI B,[ASCIZ /
    /]				; List continuation
	SETZ X,			; Init horizontal position
	CALL MOVTOU		; Print header
MOVTO4:	MOVE B,(E)		; Get entry
	TXNE B,AD%PFX		; Prefix of list?
	JRST [	HRLI B,(POINT 7,)	; Yes, point to string
		CALL MOVTOU		; Move it
		MOVEI A,":"		; Prefix separator
		XCT MOVDSP		; Move it also
		AOS LDEPTH		; Count levels of list nesting
		JRST MOVTO6]		; OK, finish this and go to next
	TXNE B,AD%SFX		; Is this a suffix entry?
	JRST MOVTO7		; Yes, decrement depth counter, etc.
	TXNE B,AD%PRN		; Is this a personal name?
	JRST [	HRLI B,(POINT 7,)	; Yes, form byte pointer
		CALL MOVTOU		; Move it on out
		SETOM BRKF		; Flag brackets needed
		JRST MOVTO6]		; Continue
	HRLI B,(<POINT 7, 0>)	; No, must be address element, form byte ptr
	MOVEI A,74		; Open bracket
	SKIPE BRKF		; Brackets needed to delimit from pers. name?
	XCT MOVDSP		; Yes, type one then
	CALL MOVADR		; Move address fancily
	MOVEI A,76		; Yes, close them then
	SKIPE BRKF		; Are we enclosing address in brackets?
	XCT MOVDSP		; Yes, move the closing bracket
	SETZM BRKF		; Reset brocket flag
	CAIL E,(C)		; At the end yet?
	RET			; Yes, return then
	MOVE B,1(E)		; See if next entry is a suffix entry
	TXNE B,AD%SFX		;  ..
	JRST MOVTO7		; End of list, this can be tricky
MOVTO5:	MOVEI A,","		; More addresses to come - move comma
	XCT MOVDSP
MOVTO6:	CAIL X,ADRWTH		; near end?
	 AOJA E,MOVTO3		; Yes, get new line for more then
	MOVEI A," "
	XCT MOVDSP
	ADDI X,2
	AOJA E,MOVTO4
;Here to close a named address list

MOVTO7:	MOVEI A,";"		; First close it with semicolon
	XCT MOVDSP		;  ..
	SOSGE A,LDEPTH		; Keep track of nesting level
	JRST [	WARN (Bad named address list nesting found at MOVTO7)
		SETZM LDEPTH
		JRST .+1]
	ADDI E,1		; Move past suffix entry
	CAIE E,1(C)		; Done with list?  (I know this looks funny
	CAIN E,(C)		;  but there is a reason for it)
	RET			; Yes, quit
	ADDI X,1		; Account for semicolon
	MOVE B,1(E)		; See if another suffix (list closure)
	TXNE B,AD%SFX		;  ..
	JRST MOVTO7		; Yes, another semicolon then
	JRST MOVTO5		; No, type comma and do next address
;MOVADR - Move address fancily, handling XMAILR-style address
;	  lists and host translation
;Call:
;	B/ Byte pointer to address string
;	X/ Horizontal position (updated)
;  MOVDSP/ Instruction to execute with character in A

MOVADR:	ILDB A,B		; Get next char of address
	JUMPE A,MOVAD6		; Done - maybe supply hostname, and return
	CAIN A,42		; Quoted string?
	JRST MOVADQ		; Yes, go handle
	CAIN A,"@"		; Start of hostname?
	 JRST MOVAD7		; Yes, handle nodename
	XCT MOVDSP		; No, just move character
	AOJA X,MOVADR		; Count columns

MOVADQ:	XCT MOVDSP		; Move opening quote
	AOS X			; Count columns
MOVAQ0:	ILDB A,B		; Move contents literally
	XCT MOVDSP		;  ..
	LDB A,B			; In case clobbered by MOVDSP
	CAIE A,42		; Close quote?
	AOJA X,MOVAQ0		; No, count columns and continue
	JRST MOVADR		; Yes, finish remainder of text

MOVAD6:	TXZE F,F%AT		; Host name seen?
MOVADX:	RET			; All done
	;JRST	MOVAD8

MOVAD8:	TXNN F,F%ARPA!F%DECN!F%ANFX	; Networks?
	JRST MOVADX		; No - done with name
	MOVE B,MYHSPT		 ; Yes -- add local host name
	TXNE F,F%DNNM		; Use DECNET names?
	MOVE B,MYHDPT		 ; Yes -- use it instead
MOVAD7:	PUSH P,B
	MOVEI B,[ASCIZ /@/]
	CALL MOVTOU
	POP P,B
	TXO F,F%F1		; Don't always translate
	CALL TRANSH		; Translate host name, maybe
	MOVE	A,TRANFG	; Get flags from that nodename block
	TXNE	A,NT%LCL	;Is it local?
	TXNE	F,F%DNNM	;Yes, do we want ARPA name?
	JRST	MOVADN		;No to something
	MOVE	B,MYHSPT	;Yes, fill in local ARPA nodename
MOVADN:	TXZ F,F%F1
	TXO F,F%AT		; Remember that we've done this
MOVAD1:	ILDB A,B		; Translated -- move translated name
	JUMPE A,MOVAD6
	XCT MOVDSP
	AOJA X,MOVAD1		; Do for all chars in string
;Utility routine to move string out via MOVDSP -- updates horizontal
; position in X.  Call with string address in B.

MOVTOU:	HRLI B,(POINT 7,)
MOVTU0:	ILDB A,B
	JUMPE A,R
	XCT MOVDSP
	AOJA X,MOVTU0
;Translate host name if necessary
;Call: 	B/ Pointer to host name
;	F%F1 = Don't translate hostnames with NT%NXL bit (no translate)
;	CALL TRANSH
;Returns +1: B points to translated name -- preserves all other ACs

TRANSH:	TXNE F,F%DECN!F%ARPA	; Have a net?
	TXNE F,F%XMLR		; XMAILR support?
	RET			; No nets, or XMAILR -- don't translate
	$SAVE <C>
	STKVAR <ORIG>		; Original name
	MOVEM B,ORIG		; Save ptr to original name
;	SKIPN HOSTAB		; Have a host table?
;	 CALL HSTINI		; No, get one
	MOVE A,ORIG		; Point to original name
TRANS1:	CALL VALID8		; Check it out
	 JRST [	MOVE A,ORIG		; Point to name being translated
		CMERR (Can't translate host name %1S)
		MOVE B,ORIG
		RET]
	HRRZ A,(B)		; Get ptr to node block
TRANS2:	MOVE B,N.FLGS(A)	; Get flag bits
	TXNN B,NT%SYN		; Synonym?
	JRST TRANSX		; No, just quit
	TXNE F,F%F1		; Suppress translations maybe?
	TXNN B,NT%NXL		; Suppress this one?
	SKIPA
	JRST TRANSX		; Yes, just quit
	SKIPN A,N.REAL(A)	; No, get ptr to real name's N-block
	FATAL (Host name table messed up)
	JRST TRANS2		; Unwind next name
TRANSX:	MOVEM B,TRANFG		; Keep flags for caller
	MOVE B,N.NAME(A)	; Get pointer to name string for host
	MOVE A,(B)		; Get possible flags word
	TLNN A,(177B6)		; Flags present?
	TXNN A,CM%FW		;  ..
	SKIPA
	ADDI B,1		; Yes, skip to text part
	HRLI B,(POINT 7,)
	RET
;Move header options - "Reply-to" and user-defined header-items

MOVOPT:	MOVE A,[IDPB A,OBPTR]
	MOVEM A,MOVDSP
MOVOP1:	SKIPN REPADD		; Any "Reply-to" addresses?
	JRST MOVHDI		; No, do user-defined header-items
	MOVEI B,[BYTE (7) 15, 12, 0]	; CRLF
	TXZE F,F%F1		; If needed
	CALL MOVSB2
	MOVEI B,[ASCIZ /Reply-to: /]
	CALL MOVSB2
	MOVEI X,^D10		; Init horizontal position
	MOVE A,REPADD		; First A-block
	CALL MVALST		; Move this address list
MOVOP3:	MOVEI B,[BYTE (7) 15, 12, 0]	; Move the CRLF
	CALL MOVSB2		;  ..
;	JRST MOVHDI
;Move user-defined header-items out

MOVHDI:	TXNE F,F%REDI		; Redistributing ?
	RET			; Yes, don't do headers
	MOVEI B,[BYTE (7) 15, 12, 0]	; CRLF needed first?
	TXZE F,F%F1		; We're told this by caller lighting F%F1
	CALL MOVSB2		; Yes, move it out
	SKIPN A,HDITAB		; Any header-items?
	RET			; No, return
	HLLZ E,(A)		; Any user-defined header-items?
	JUMPE E,R		; No, return now
	MOVN E,E		; Yes, form AOBJN pointer
	HRRI E,1(A)		;  accounting for header word
MOVHD0:	SETZ X,			; Init horizontal position
	HRRZ A,(E)		; Get ptr to H-block for this one
	MOVE B,HD.FLG(A)	; Get flags
	TXNN B,HD%PRS		; Present?
	JRST MOVHD1		; No, skip it then
	HLRZ B,(E)		; Yes, get name
	HRLI B,(POINT 7,)	; Form ptr
	SETZ C,			; Assume no quoting needed
	CALL SPCCHK		; Qutoing required?
	 MOVEI C,42		; Yes, get the quote char
	SKIPE A,C		; If quoting required,
	XCT MOVDSP		;  move the quote char
	CALL MOVTOU		; Move it out
	SKIPE A,C		; If quoting,
	XCT MOVDSP		;  move closing quote
	MOVEI B,[ASCIZ /: /]	; Colon space
	CALL MOVTOU
	HRRZ A,(E)		; Point to H-block again
	MOVE B,HD.FLG(A)	; Get type code
	ANDI B,HD%TYP		;  *** should use load
;	LOAD B,HDTYP(A)
	CALL @MOVHDO(B)		; Call appropriate routine to move data
	MOVEI B,[BYTE (7) 15, 12, 0]	; CRLF
	CALL MOVSB2		;  ..
MOVHD1:	AOBJN E,MOVHD0		; Go on to next one
	RET


;Table of routines indexed by type to move data of header-item out

DEFINE X(COD,STRNG,SIZ),<
	EXP MVO'COD
>

MOVHDO:	HDTYPS			; Generate the dispatch table
;Move address

MVOADR:	MOVE A,HD.DAT(A)	; Address of address list
	CALLRET MVALST		; Move fancily

;Move text string

MVOTXT:	MOVE B,HD.DAT(A)	; Address of text for this field
	CALLRET MOVSB2		; Move 'em on out

;Move date

MVODAT:	MOVE A,HD.DAT(A)	; Get universal date/time
	$TEXT (MVODSP,<^H9/A/^A>)	; Type only first 9 columns
	RET

;Move date/time

MVODTI:	MOVE A,HD.DAT(A)	; Get universal format date/time
	$TEXT (MVODSP,<^H/A/^A>)	; Use GLXLIB routine
	RET

;Move time

MVOTIM:	MOVE A,HD.DAT(A)	; Get universal date/time
	$TEXT (MVODSP,<^C5/A/^A>)	; Only do minutes and seconds
	RET

;Called by $TEXT macro above with char in A

MVODSP:	XCT MOVDSP
	$RET

;Move keyword

MVOKWD:	MOVE B,HD.DAT(A)	; Get keyword index
	HLRZ B,(B)		; Get string address
	CALLRET MOVSB2		; Move it

; Get some more text

.TEXT:	CONFRM			; Confirm command
	TXNE F,F%REDI		; REDISTRIBUTE in progress?
	JRST [	WARN <Adding text to a REDISTRIBUTEed message is not allowed.>
		RET]
	CALL GETTXT		; Resume text
	MOVE A,LSTCHR		; See if want to send
	CAIN A,32		;  by ^Z term.
	JRST SSEND0
	RET			; Nope
; Get a new subject

.SUBJE:	CONFRM			; Confirm command
GETSUB:	PROMPT (Subject: )
	MOVEI A,[FLDDB. (.CMCFM,CM%SDH,,<
Type a single line terminated with a <CR> which summarizes
the subject of the message you are sending.
>,,[FLDDB. (.CMTXT,CM%SDH)])]
	CALL RFIELD		; Read subject line or crlf
	MOVE A,CR.COD(A)	; See which
	CAIN A,.CMCFM		; Just CR?
	 JRST .ERSB0		;  No subject
	CONFRM
	SKIPE A,SUBJEC		; Release existing block
	CALL RELSB
	CALL CPYATM		; Allocate block and copy string to it
	 JRST [	WARN <Can't set subject, insufficient storage>
		RET]
	MOVEM A,SUBJEC
	RET

.CC:	MOVEI A,[FLDDB. .CMCFM]	; Try confirmation
	CALL RFLDE		;  ..
	 JRST GETCC0		; Non, maybe addresses to parse then
GETCC:	PROMPT (cc: )
GETCC0:	TXO F,F%CC		; Say in cc command
	CALL SVSTAT		; Save state of address lists in case reparse
	HLRZ W,TOPTRS		; Pointer to cc links
	JUMPN W,.TO2
	 MOVEI W,TCPAG+NTCENT-1	; Init for start
	JRST .TO2		; Go join common code


;Save state of address lists in case reparse occurs

SVSTAT:	MOVE A,FRENAM		; String space pointer
	MOVEM A,SV.FNM
	MOVE A,TOPTRS		; Address list pointers
	MOVEM A,SV.TOP
	SKIPE B,SV.NTB		; Any old saved table to release?
	JRST [	HRRZ A,(B)		; Yes, get its size
		ADDI A,1		;  ..
		CALL M%RMEM		; Release it
		JRST .+1]
	SKIPN A,NAMTAB		; Any name table to save?
	JRST [	SETZM SV.NTB		; No, skip this then
		JRST SVSTT0]		;  ..
	HRRZ A,(A)		; Get size of name table
	ADDI A,1		;  ..
	CALL M%GMEM		; Allocate new block for it
	MOVEM B,SV.NTB		; Save address of saved name table
	HRL B,NAMTAB		; From
	HLRZ A,@NAMTAB		; Get number of actual entries
	ADDI A,(B)		; Compute last address BLT'ed
	BLT B,(A)		; Copy the table
SVSTT0:	MOVEI A,SVSTA0		; Where to go to restore all this stuff
	HRRM A,SBK+.CMFLG	; Fake out COMND routines
	EXCH A,REPARA		; Fake out CMDERR also
	MOVEM A,REPAR0		;  but remember what it wanted to do
	MOVEI A,SVSTA1		; Dummy return to reset default reparse addr
	EXCH A,(P)		; Push on stack
	JRST (A)		; Return to caller

;Here if no reparse needed -- reset default reparse address

SVSTA1:	MOVEI A,REPARS
	HRRM A,SBK+.CMFLG
	MOVE A,REPAR0		; Restore original reparse address
	MOVEM A,REPARA
	RET

;Here from COMND JSYS to restore things because reparse needed

	SOS REPAR0		; *** Note that this will only be called
				;     because CMDER1 SOS's REPARA, which
				;     points to SVSTA0.  This remembers that.
SVSTA0:	MOVE A,SV.FNM
	MOVEM A,FRENAM
	MOVE A,SV.TOP
	MOVEM A,TOPTRS
	SKIPE B,NAMTAB		; Any old name table to release?
	JRST [	HRRZ A,(B)		; Yes, get its size
		ADDI A,1		;  ..
		CALL M%RMEM		; Release it
		JRST .+1]
	SKIPN A,SV.NTB		; Any saved table to restore?
	JRST [	SETZM NAMTAB		; No, skip this
		JRST SVSTA2]		;  ..
	HRRZ A,(A)		; Get size of saved name table
	ADDI A,1		;  ..
	CALL M%GMEM		; Allocate new block for it
	MOVEM B,NAMTAB		; Save address of restored name table
	HRL B,SV.NTB		; From
	HLRZ A,@SV.NTB		; Get number of actual entries
	ADDI A,(B)		; Compute last address BLT'ed
	BLT B,(A)		; Copy the table
SVSTA2:
TOPS10<	CALL KILLST >		; Clean up lists built by MSGUSR
	MOVEI A,REPARS		; Restore normal reparse address
	HRRM A,SBK+.CMFLG	;  ..
	MOVE A,REPAR0		; Restore original REPARA
	MOVEM A,REPARA		;  ..
	JRST REPARS		; Go do default reparse things

.TO:	MOVEI A,[FLDDB. .CMCFM]	; Try confirmation
	CALL RFLDE		;  ..
	 JRST GETTO0		; None, maybe addresses to parse
GETTO:	PROMPT (To: )
GETTO0:	TXZ F,F%CC
	CALL SVSTAT		; Save state in case reparse
	HRRZ W,TOPTRS
	JUMPN W,.TO2
	 MOVEI W,TCPAG-1
.TO2:	MOVE U,FRENAM		; Get free space pointer
.TO3:	CALL GETUSR		; Get the user entry in (b)
	 JRST .TO6		; Empty field, finish up and return
	HRRZ A,B		; See if funny code returned
	CAIN A,SFXCOD		; Suffix entry?
	JRST [	MOVE B,(W)		; Yes, was last entry prefix?
		TXNE B,AD%PFX		;  if so, this list is empty
		JRST [	HRRZ T,W		; Empty list -- get ptr to name
			CALL NAMDEL		; Delete the name
			SETZM (W)		; Zap!
			SUBI W,1		;  ..
			JRST .TO5]		; Keep on truckin'
		AOS W			; Yes, stuff into table
		MOVX A,AD%SFX		;  ..
		JRST .TO4]
	MOVE C,B		; Preserve over call to S%TBAD
	CAIN A,PRNCOD		; Personal name?
	JRST .TO1		; Yes, don't stick into name table
	MOVEI A,NAMTAB		; Regular name, add to table
	CALL TBADDS		; Duplicate?
	 JUMPF .TO7		; Could be, go complain maybe
.TO1:	MOVEM U,FRENAM		; Update free pointer
	AOS W			; Add to address
	HLRZ A,C		; Get ptr to string
	HRRZ B,C		; Get user number or code
	CAIN B,PFXCOD		; Prefix of named address-list?
	TXO A,AD%PFX		; Yes, light appropriate flag
	CAIN B,PRNCOD		; Personal name?
	TXO A,AD%PRN		; Yes, light flag
.TO4:	MOVEM A,(W)		; Stuff entry into list
.TO5:	TXNE F,F%CMA		; More wanted
	JRST .TO3		; Yes - get some
.TO6:	TXNN F,F%CC		; In the cc field?
	 JRST [	CAIE W,TCPAG-1		; Check null to list
		HRRM W,TOPTRS
		RET]
	CAIE W,TCPAG+NTCENT-1	; Check null cc list
	HRLM W,TOPTRS
	RET
;Here if failure return from TBADD, either internal error, or
; duplicate name of some sort.  Analyze and inform the user.

.TO7:	CAIE A,EREIT$		; Duplicate entry?
	JRST [	CMERR <Name table full>
		RET]
	HLRZ B,C		; point to string
	HRRZ A,C		; Are we purging an entire address list?
	CAIE A,PFXCOD		;  ..
	JRST [	CALL .TO9		; No, purge one name
		JRST .TO5]		; Go continue eating addresses
	MOVEI E,1		; Yes, init depth counter
	CIETYP <%% Duplicate address list purged - %2S
>
.TO8:	CALL GETUSR		; Eat addresses until list closure
	 JRST [	WARN <Internal error at .TO8, 1>
		JRST .TO6]		; This can't happen
	HRRZ A,B		; Get code for this guy
	CAIN A,PFXCOD		; Prefix?
	ADDI E,1		; Yes, count levels of nesting
	CAIN A,SFXCOD		; Suffix?
	SOJL E,[WARN <Internal error at .TO8, 2>
		JRST .TO6]
	JUMPE E,.TO5		; Back to original level -- all done purging
	TXNN F,F%CMA		; There'd better always be more to parse
	JRST [	WARN <Internal error at .TO8, 3>
		JRST .TO6]
	JRST .TO8		; List to be purged still has elements left


;Here to purge one duplicate name.  Purge associated personal name(s) too.

;**;[3091] Replace one line at .TO9		NED  12 May 87
.TO9:	$TEXT (KBFTOR,<% Duplicate name purged - ^T/(B)/>)
	MOVEI T,TCPAG-1		; Fence for personal name alimentation
	TXNE F,F%CC		; To or CC?
	MOVEI T,TCPAG+NTCENT-1	; CC, different fence
.TO10:	CAIN T,(W)		; Empty list yet?
	RET			; Yes, done
	MOVE A,(W)		; Get entry
	TXNE A,AD%PRN		; Associated personal name?
	SOJA W,.TO10		; Yes, flush it
	RET			; No, return then
;Prompt for and get user-defined header-items which are required

GETUHD:	SKIPN A,HDITAB		; Any header-items defined?
	RET			; No, return
	HLLZ E,(A)		; Count of all header-items
	JUMPE E,R		; None, just quit
	MOVN E,E		; Form AOBJN pointer
	HRRI E,1(A)		;  accounting for header word
GETUH0:	HRRZ A,(E)		; Get ptr to H-block for this item
	MOVE B,HD.FLG(A)	; Get flags
	TXNE B,HD%RQD		; Required?
	CALL INCLUD		; Yes, prompt for and store this header-item
	AOBJN E,GETUH0		; OK, keep on truckin'
	RET


; Get prompted message

GETMS0:	CALL GETTO0		; Get To list without prompting
TOPS10<	CALL ECHOON >		; In case monitor command
	JRST GETMS1
GETMSG:
TOPS10<	CALL ECHOON >		; In case monitor command
	CALL GETTO		; To (with prompt)
	CALL GETCC		; cc
GETMS1:	CALL GETSUB		; Subject
	CALL GETUHD		; User-defined header-items
	JRST GETTXT		; Go get text and finish
; Remove user

.UNTO:	NOISE (user)
.UNTO1:	MOVEI U,STRBUF		; Place to put name string
	CALL GETUSR
	 RET			; Null address, just return
	HRRZ C,B		; Get code
	SETZ A,			; Assume not address-list
	CAIN C,PFXCOD		; Is this an address-list prefix?
	SETO A,			; Yes, flag that we're removing a list
	MOVEI U,STRBUF		; Start of buffer
	CALL DOUNTO		; Remove the name
	TXNE F,F%CMA		; More to come?
	 JRST .UNTO1		; Yep
	RET


;Remove a user (or list of users) from to or cc lists
;Call:	A/ -1 to remove address-list, 0 to remove single user
;	U/ address of name to remove (username or address-list name)
;Return	+1: always

DOUNTO:	TRVAR <PFXCNT>		; Count of prefixes seen
	MOVEM A,PFXCNT		;  also flag to remove list
	HRRZ V,TOPTRS		; Get to pointers
	MOVEI T,TCPAG
	TXZ F,F%CC		; Say not in cc
	CALL DOUNC1
	HLRZ V,TOPTRS		; Get cc pointers
	MOVEI T,TCPAG+NTCENT
	TXO F,F%CC		; Say in cc
DOUNC1:	JUMPE V,R		; None of this class
DOUNT0:	HRRZ A,(T)		; Get this one
	HRLI A,(<POINT 7,0>)
	MOVEI B,(U)		; Try to match this
	HRLI B,(<POINT 7,0>)
;	JRST DOUNT1
DOUNT1:	ILDB C,B		; Get char from target
	JUMPE C,DOUNT3		; Null means it matches
	CAIN C,"@"		; Starting host name?
	 TXNE F,F%AT		; Trying to match @ too?
	 CAIA			; No or yes
	 JRST DOUNT3		; Yes and no, matches
	ILDB D,A
	CAIN D,(C)
	 JRST DOUNT1		; Chars match?
	TRC D,(C)
	CAIN D,40		; Case only?
	 JRST DOUNT1		; Yes, keep looking
DOUNT2:	CAIL T,(V)		; Done with this list?
	 RET			; Yes, return
	AOJA T,DOUNT0		; No, check next entry

DOUNT3:	ILDB C,A		; Make sure we've matched entire target
	JUMPN C,DOUNT2		; There's more to target, this isn't a match
	MOVX A,AD%PFX		; Is this entry an address-list prefix?
	TDNE A,(T)		;  ..
	JRST [	SKIPN PFXCNT		; Yes, were we looking for one?
		JRST DOUNT2		; We weren't -- no match then
		JRST DOUNT4]		; We were -- this is it then
	SKIPE PFXCNT		; This isn't a prefix -- did we want one?
	JRST DOUNT2		; Yes, this is wrong -- no match
	CALL RMVADR		; Ordinary address -- remove it
	JRST DOUNT6		; Finish up and return

DOUNT4:	SETZM PFXCNT		; Init depth counter
DOUNT5:	MOVE A,(T)		; Get this entry
	TXNE A,AD%PFX		; Prefix?  (Always true 1st pass)
	AOS PFXCNT		; Yes, count depth
	TXNE A,AD%SFX		; Suffix?
	SOS PFXCNT		; Yes, one lest level now
	CALL RMVADR		; Remove this entry
	SKIPN PFXCNT		; This list totally removed yet?
	JRST DOUNT6		; Yes, finish up
	JUMPN V,DOUNT5		; Loop thru all entries in list
	WARN (Unterminated named address-list)

DOUNT6:	TXNE F,F%CC		; In cc field?
	 HRLM V,TOPTRS		; Yes update cc pointer
	TXNN F,F%CC
	 HRRM V,TOPTRS		; Else update to pointers
	CAIGE T,1(V)		; Was that the last in the list?
	 JUMPN V,DOUNT0		; Or the end of the list?
	RET			; Yes, return
;Remove one address from to or cc list.
;Call:	T/ address of entry in TCPAG to remove
;	V/ address of last entry in list
;Return	+1: always, T preserved, V updated (or zero if list empty)

RMVADR:	MOVX A,AD%SFX!AD%PRN	; Don't try deleting suffixes or personal names
	TDNN A,(T)		;  ..
	CALL NAMDEL		; delete this name
	CAIN T,(V)		; At the end of the list?
	 JRST RMVAD1		; Yes, no need to move anything
	MOVEI A,(T)
	HRLI A,1(T)		; Move up one word
	BLT A,-1(V)
RMVAD1:	CAIE V,TCPAG+NTCENT	; Have we emptied the list?
	 CAIN V,TCPAG		; ie., Was that the first entry?
	 TDZA V,V		; Yes, erase field then
	 SOJ V,			; Else update end pointer

;See if we have leading personal name entries which need to be flushed.

	JUMPE V,R		; If list empty, don't try this stuff
	MOVE A,-1(T)		; Get preceding entry
	TXNE A,AD%PRN		; Is it a personal name?
	SOJA T,[CALL RMVADR		; Yes, delete it
		JRST .+1]		;  and continue

;See if we've emptied a named list by removing the individual names in it.
; If so, must remove prefix and suffix entries.

	CAIN T,1(V)		; Was entry deleted the end entry?
	RET			; Yes, can't be any suffixes then
	MOVE A,(T)		; Get potential suffix
	TXNN A,AD%SFX		; Is deleted entry followed by suffix?
	RET			; No, done
	MOVE A,-1(T)		; Get possible prefix
	TXNN A,AD%PFX		; Is it?
	RET			; No, return
	MOVEI A,-1(T)		; Yes, must delete prefix and suffix
	HRLI A,1(T)		; So must remove two entries
	BLT A,-2(V)		;  ..
	SUBI V,2		;  ..
	CAIE V,TCPAG+NTCENT	; Check for emptied list
	 CAIN V,TCPAG-1		;  ..
	  SETZ V,		; If empty, zero end pointer
	RET

;
;	NAMDEL removes a name from the TO: or CC: list
;

NAMDEL:	MOVE A,NAMTAB		; Remove entry from name table
	HRR B,(T)		; Actual string
	HRLI B,(POINT 7,)	;  ..
	$CALL S%TBLK		; Find in table
	TXNN B,TL%EXM		; Found the entry?
	JRST [	HRR A,(T)		; No, point to string
		WARN (Can't find %1S in name table)
		RET]
	MOVE B,A
	MOVE A,NAMTAB
	HLRZ D,(A)		; [2404] Get the # entries in the table
	SKIPE D			; Don't try if table's empty
	$CALL S%TBDL		; Delete from table (can't fail?)
	RET

	END

; Edit 2443 to MS.MAC by TGRADY on 5-Sep-85
; Fix CPR's, BFD's, and PARSEF bug.
; Edit 2444 to MS.MAC by TGRADY on 5-Sep-85
; Fix up previous edit - bug in PARSEF.
; Edit 2449 to MS.MAC by JROSSELL on 30-Sep-85
; Add support for MSLCL to use GLXLIB's IPCF interface
; Edit 2451 to MS.MAC by JROSSELL on 1-Oct-85
; Do not set IPCF quotas
; Edit 2452 to MS.MAC by MAYO on 18-Oct-85
; "verb SAME" shouldn't always claim %No previous sequence exists
; Edit 2454 to MS.MAC by PRATT on 19-Oct-85
; Merge many of Ned's changes and a couple of other things:
;    Tops-20 conditionalize searching of MONSYM
;    Allow SYSTEM mail for Tops-10
;    Allow MAIL as program name along with MS
;    Remove some ISWS conditional code
;    VT200 series checking
;    Make HEADERS visible again, allow "H" as abrev
;    Make SUMMARIZE, ZSEND, and SSEND invisible
;    Allow ^Z for exiting MS on the -10
;    Make .FLAGX do a call to SEQUEN, change the .FLAGX callers
;    Fix up some comments and their alignment
;    Move the calling of SIZFIL a little bit in the GET1 code
;    Change a couple of label names and add .REXIZ
;    Make sure to call MRKMSG in the correct places
;    Make NET command type out an extra <crlf> before running sender programs
;    Don't blank screen on a PUSH
; Edit 2456 to MS.MAC by PRATT on 21-Oct-85
; Make "%EXPUNGE in progress" message a little more general
; Edit 2457 to MS.MAC by PRATT on 21-Oct-85
; Put STATUS back in
; Edit 2461 to MS.MAC by PRATT on 27-Oct-85
; Don't update message bit if we don't have to
; Edit 2463 to MS.MAC by PRATT on 30-Oct-85
; Redo the last edit the correct way, just before PARS14, make sure "in file"
; message bits are copied to the "in file" MSGBTS field.
; Edit 2462 to MS.MAC by PRATT on 4-Nov-85
; Merge many changes in -10, -20, and common code.
; *** Edit 2465 to MS.MAC by JROSSELL on 5-Nov-85
; Make SUBJEC a global symbol so it can be used by MSLCL
; *** Edit 2468 to MS.MAC by PRATT on 7-Nov-85
; Fix up commands tables; fix headers, add directory for vms compat, add the
; "Get file" bit to status.
; *** Edit 2469 to MS.MAC by PRATT on 9-Nov-85
; After Expunge, do PARSEF with "M" set up, so that we make sure that we have a
; message window after unmapping the old window.
; *** Edit 2471 to MS.MAC by PRATT on 14-Nov-85
; Changes to break up MS into a smaller module.
; *** Edit 2474 to MS.MAC by PRATT on 18-Nov-85
; Changes for TOPS10 to make MS.MAC smaller
; *** Edit 2477 to MS.MAC by MAYO on 20-Nov-85
; Make FORCE-DIRECTORY-LOOKUP be on by default.
; *** Edit 2484 to MS.MAC by SANTEE on 21-Nov-85
; Clean up the various edit histories.
; *** Edit 2486 to MS.MAC by PRATT on 22-Nov-85
; Copyright statements
; *** Edit 2487 to MS.MAC by MAYO on 25-Nov-85
; Merge MSGUSRs for the -10 and -20. Have MS.MAC call KILLST when cleaning up a
; ^U, etc. on the -10 side.
; *** Edit 2492 to MS.MAC by MAYO on 3-Dec-85
; Hack Reply-to addresses during ARPA mail so at least local nodenames get
; translated to ARPA names. Also, remove vestigal code for XMAILR.
; *** Edit 2604 to MS.MAC by PRATT on 9-Dec-85
; Fix problem with DIRECTORY at read level, wrong cmd macro was used
; *** Edit 2605 to MS.MAC by PRATT on 9-Dec-85
; Fix up REDISTRIBUTE. Make headers say Resent, Fix sequence handling, Use
; Auto-send flag, Remove checking of trailer, Change brief-address-list header
; table, Don't include user defined headers in when resending.
; *** Edit 2606 to MS.MAC by PRATT on 9-Dec-85
; Fix more problems with Redistribute
; *** Edit 2607 to MS.MAC by SANTEE on 10-Dec-85
; Make MS/MX get along well together. Have MS write dashes at the end of
; messages. While we're there remove some of the NETMAI code.
; *** Edit 2613 to MS.MAC by JROSSELL on 14-Dec-85
; Repair the REPAIR command
; *** Edit 2614 to MS.MAC by SANTEE on 18-Dec-85
; Keep the number of messages deleted, new, and flagged up-to-date. This makes
; several paths faster and we end up doing alot less work. Also, with windowing
; it is important on the -10 to know if we have any work to do at expunge time.
; Some minor code rearrangements were made in related areas for speed up
; purposes. Finally some comments were added or lined up and paging was
; adjusted in some places.
; *** Edit 2616 to MS.MAC by JROSSELL on 18-Dec-85
; When a message is read or typed; or when SKIM, SUMMARIZE, HEADERS, GET or
; NEXT is given - update the last time the mail file was read. On TOPS20 also
; update the FDB.
; *** Edit 2617 to MS.MAC by JROSSELL on 18-Dec-85
; Change GTJFN error codes when doing a REPAIR from TOPS-20 to GLXLIB so
; TOPS-10 can understand them
; *** Edit 2619 to MS.MAC by SANTEE on 19-Dec-85
; Fix bug with 2614 that caused the setting and unsetting of bits to only
; happen sometimes.
; *** Edit 2622 to MS.MAC by PRATT on 23-Dec-85
; Fix "MOVE or DELETE" length invalid error, SET DEF DIR, SET DEF PROT (-10)
; *** Edit 2626 to MS.MAC by MAYO on 3-Jan-86
; Teach MOVADR not to append nodenames to addresses if they don't already have
; one. Hence, local addresses just typed as NAME stay that way.
; *** Edit 2627 to MS.MAC by PRATT on 3-Jan-86
; Clean up command tables, make some more commands invisible
; *** Edit 2632 to MS.MAC by MAYO on 10-Jan-86
; Allow trailing spaces in a multi-line address (PRADDR)
; *** Edit 2633 to MS.MAC by JROSSELL on 10-Jan-86
; Make the REPAIR command noise words more informative
; *** Edit 2634 to MS.MAC by JROSSELL on 10-Jan-86
; Open up a second JFN as READ/WRITE in places where we don't want another
; process writing to the mail file.
; *** Edit 2635 to MS.MAC by PRATT on 13-Jan-86
; If RETRIEVE DRAFT can't find the TO: field, complain about it but don't abort
; the command.
; *** Edit 2636 to MS.MAC by APPELLOF on 15-Jan-86
; Finish SET DEFAULT DIRECTORY for TOPS-10
; *** Edit 2638 to MS.MAC by PRATT on 17-Jan-86
; Unmerge edit 2626, it's causing grief... will it ever end?
; *** Edit 2640 to MS.MAC by APPELLOF on 24-Jan-86
; SET/CLEAR the "new mail" bit in mail file RIB on TOPS-10 Bit is lit if there
; are unseen messages. Bit is cleared if there are no unseen messages.
; *** Edit 2641 to MS.MAC by APPELLOF on 27-Jan-86
; Re-apply preceeding edit properly
; *** Edit 2642 to MS.MAC by PRATT on 27-Jan-86
; Apply Henry's changes for return-receipt-requested.
; *** Edit 2644 to MS.MAC by PRATT on 27-Jan-86
; HBLKP should not be a TRVAR, and CRFFDH should not be in a common code INTERN
; statement.
; *** Edit 2645 to MS.MAC by SANTEE on 27-Jan-86
; Edit 2634 broke CHECK on the -10 side. Put the code back.
; *** Edit 2646 to MS.MAC by SANTEE on 28-Jan-86
; Eliminate a few duplicate INTERNALs and cause code to flow better.
; *** Edit 2651 to MS.MAC by SANTEE on 2-Feb-86
; Eliminate the need for MSUTAB at all. Move the few useful lines elsewhere.
; *** Edit 2653 to MS.MAC by JROSSELL on 10-Feb-86
; Correct the message length for saved outgoing REPAIRED mail
; *** Edit 2654 to MS.MAC by JROSSELL on 12-Feb-86
; If an unprivileged user is over quota, delete the empty .MAI file and do not
; send a message to MX. Inform the user of being over quota.
; *** Edit 2659 to MS.MAC by MAYO on 20-Feb-86
; Don't allow Aliases with parser-breaking characters in them (like comma).
; *** Edit 2662 to MS.MAC by MAYO on 26-Feb-86
; Fix Return-Receipt-Requested-to to properly parse addresses. Allow the normal
; range of possibilities offered by GETUSR.
; *** Edit 2666 to MS.MAC by MAYO on 3-Mar-86
; Make the -10's SET {no} DIRECTORY-CONFIRMATION command do the same things as
; the -20's SET FORCE-DIRECTORY-LOOKUP. Using either controls F%FDIR, which
; controls whether GETUSR verifies local usernames.
; *** Edit 2671 to MS.MAC by SANTEE on 3-Mar-86
; When we stopped talking to NETMAI we didn't need the storage. Get rid of it.
; *** Edit 2672 to MS.MAC by MAYO on 3-Mar-86
; SET DIRECTORY should be invisible, SET FORCE sufficies.
; *** Edit 2679 to MS.MAC by HDAVIS on 11-Mar-86 (TCO NO )
; Set default for sending RRR to yes. Don't call CHKDEL twice. Give user 4
; second to read error if sending RRR fails.
; *** Edit 2679 to MS.MAC by MAYO on 12-Mar-86
; Consolidate references to TENT1 and TENT. This prevents a BPN when redefining
; headers.
; *** Edit 2682 to MS.MAC by SANTEE on 16-Mar-86
; Forwarding of large messages could get rude if the message was larger than
; the window size. Make it more polite when large messages are present. Also
; make forwarding cause less thrashing of the window.
; *** Edit 2688 to MS.MAC by MAYO on 26-Mar-86
; In REPLY, if there is no FROM or REPLY-TO, complain and try to use SENDER.
; *** Edit 2689 to MS.MAC by APPELLOF on 26-Mar-86
; Prevent ERF (Error Reading File) on TOPS-10 if MX is appending when we check
; the size of the mail file. Also cut down on the number of LOOKUPs we do.
; *** Edit 2694 to MS.MAC by JROSSELL on 8-Apr-86
; If PARSEF detects that the mail file has changed size, have it assume that
; another reader exists and reparse the mail file. This is for TOPS20 only and
; must be used due to a lack of a complete global ENQ/DEQ mechanism.
; *** Edit 2698 to MS.MAC by RASPUZZI on 19-May-86
; Make DEFINE ALIAS * undefine aliases instead of address lists
; *** Edit 2704 to MS.MAC by SANTEE on 22-May-86
; A QUIT from MS SEND> level can get bad results if you entered with MS SEND.
; We check to see if this is a reply by looking at the message bits. Lets see
; if there are any first.
; *** Edit 2706 to MS.MAC by RASPUZZI on 27-May-86
; Teach MS not to use POBOX: when writing files. Instead, find out what STR: is
; being used (saved in MYSTR) and go from there.
; *** Edit 2707 to MS.MAC by PRATT on 28-May-86
; Make TYPE/VERBOSE-TYPE type out deleted messages after issueing a warning.
; The READ command will not do this.
; *** Edit 2708 to MS.MAC by PRATT on 30-May-86
; Fix problem with multiple "There are no messages in MAIL.TXT" messages.
; *** Edit 2716 to MS.MAC by RASPUZZI on 6-Jun-86
; Teach MS about looking for users on other structures when POBOX: contains a
; list of structures and there is no directory for a recipient of a message on
; the first structure listed in the logical name.
; *** Edit 2718 to MS.MAC by SANTEE on 10-Jun-86
; Get the personal name from the accounting system on TOPS-10. Also, while I
; was there clean up ^C (print it out at the right time and clear the scrolling
; region if set up) and make the listing just a little bit prettier.
; *** Edit 3074 to MS.MAC by RASPUZZI on 14-Aug-86, for SPR #21351
; Make sure MS gets the right EMACS from EDITOR:
; *** Edit 3075 to MS.MAC by RASPUZZI on 14-Aug-86
; Silly me. I forgot to put in the edit number for 3074. So now there are 2.
; *** Edit 3086 to MS.MAC by RASPUZZI on 27-Mar-87
; Prevent MS from looping through a non-existant message sequence when a user
; continues their MS fork.
; *** Edit 3087 to MS.MAC by RASPUZZI on 7-Apr-87
; Correct stupidity in edit 3086. Don't fool with MSGSEQ but instead, change
; MSGSSQ to clear the message sequence.
; *** Edit 3089 to MS.MAC by RASPUZZI on 9-Apr-87
; Make sure deleted messages are TYPEable at the read level since this is
; possible at the top level.
; *** Edit 3091 to MS.MAC by SANTEE on 31-Jul-87
; Fix up a few erroneous error messages
; *** Edit 3094 to MS.MAC by SANTEE on 7-Aug-87
; Don't have BBoard, which is invisible, interfere with Blank, which is
; visible.
; *** Edit 3095 to MS.MAC by SANTEE on 7-Aug-87
; Don't leave the mail file open after a quit. Tops-10 only.
; *** Edit 3096 to MS.MAC by SANTEE on 28-Sep-87
; Move all impure data together.
; *** Edit 3097 to MS.MAC by RASPUZZI on 18-Nov-87, for SPR #21573
; Prevent MS from blowing up when a VAX user sends over his personal name with
; an odd number of quotes in it. Also, MX must be fixed to prevent this from
; happening! 
; *** Edit 3099 to MS.MAC by RASPUZZI on 1-Dec-87
; Make MS parse ARPA hosts and fix off by one bug