Trailing-Edge
-
PDP-10 Archives
-
T10_T20_MS_V10_SRCS_830128
-
nmail.mac
There are no other files named nmail.mac in the archive.
TITLE NMAIL - DECNET mail listener
SUBTTL Larry Campbell
SEARCH MACSYM,MONSYM
.REQUIRE SYS:MACREL
SALL
.DIRECTIVE FLBLST
T1=1
T2=2
T3=3
T4=4
P1=10
P2=11
P3=12
PTR=13 ; global byte pointer to received mail
CNT=14 ; global byte count for same
CX=16
P=17
.VER==5
.EDT==^D149
LOC 137
EXP <.VER>B11+.EDT
RELOC
;Macros
DEFINE JERR(STRING),<
XLIST
ERJMP [ HRROI T1,[ASCIZ /NMAIL error: /]
ESOUT
HRROI T1,[ASCIZ /STRING/]
PSOUT
HRROI T1,[ASCIZ / because: /]
PSOUT
MOVX T1,.PRIOU
HRLOI T2,.FHSLF
ERSTR
JFCL
JFCL
CALL LGCRLF
CALL DTSTMP ;; log this lossage also
LOG <STRING>
LOG < because: >
MOVE T1,LOGJFN
HRLOI T2,.FHSLF
ERSTR
JFCL
JFCL
JRST FATAL]
LIST
>
DEFINE LOG(STRING),< ;; put message into log file
XLIST
HRROI T1,[ASCIZ \STRING\] ;; so it can type slashes
CALL LOGMSG
LIST
>
DEFINE NTMSG(STRING),< ;; type at network link
XLIST
LOG <STRING>
MOVE T1,NETJFN
HRROI T2,[ASCIZ /STRING/]
SETZB T3,T4
SOUTR
ERJMP [CALL LGCRLF ;; log this failure
CALL DTSTMP
LOG <SOUT to net link failed because: >
MOVE T1,LOGJFN
HRLOI T2,.FHSLF
ERSTR
JFCL
JFCL
JRST DMPLNK] ;; dump net link and reset world
LIST
>
DEFINE ETMSG(STRING),< ;; type error message at net link
XLIST
NTMSG <?NMAIL error: 'STRING>
LIST
>
DEFINE DIE(STRING),< ;; fatal internal error
XLIST
JRST [ HRROI T1,[ASCIZ /NMAIL fatal internal error: /]
ESOUT
HRROI T1,[ASCIZ /STRING/]
PSOUT
HRROI T1,[ASCIZ /
/]
PSOUT
CALL LGCRLF ;; log this error
CALL DTSTMP ;; time stamp it
HRROI T1,[ASCIZ /Fatal error: /]
CALL LOGMSG
HRROI T1,[ASCIZ /STRING/]
CALL LOGMSG
JRST FATAL]
LIST
>
DEFINE HERALD(VER,EDT),<
XLIST
TMSG <NMAIL version VER(EDT) running>
HRROI T1,[ASCIZ /NMAIL version VER(EDT) running/]
CALL LOGMSG
LIST
>
DEFINE LOG(STRING),<
XLIST
HRROI T1,[ASCIZ \STRING\]
CALL LOGMSG
LIST
>
DEFINE FIND(STRING),< ;; search for the given string
XLIST
MOVE T1,[POINT 7,[ASCIZ /STRING/]]
CALL FINDIT ;; call string compare routine
LIST
>
;In case of old monitor
IFNDEF IOX34,<
PRINTX %IOX34 not defined in this monitor but don't worry about it
IOX34==777777
>
;Storage
NATMBF==40 ; length of atom buffer in words
BBFLEN==200000 ; length of big buffer into which mail is read
NFRMBF==20 ; length of sender name buffer
TIMEN==^D900000 ; Milliseconds before sender declared tardy
STKLEN==200 ; size of stack
ATMBUF: BLOCK NATMBF
BIGBUF: BLOCK BBFLEN ; where mail is read into
ULIST: BLOCK ^D200 ; where to store mailbox directory numbers
FRMMSG: BLOCK 20 ; string to type on recipient's terminal
FRMBUF: BLOCK NFRMBF ; where to put sender's name
HSTNAM: BLOCK 2 ; our host name
GTINF: BLOCK 20 ; GETJI block
STACK: BLOCK STKLEN ; one stack for each fork
NETJFN: BLOCK 1 ; network link JFN
LOGJFN: BLOCK 1 ; log file JFN
NTIME: BLOCK 1 ; time receipt of mail initiated (for stats)
PAPERF: BLOCK 1 ; -1 => Paper mail was queued
IOFNAM: BLOCK 10 ; "Interoffice-mail-<local-hostname>"
ELPTIM: BLOCK 1 ; elapsed time for receipt of mail
BYTCNT: BLOCK 1 ; length of mail in bytes
DLVLST: BLOCK 1 ; -1 => delivery list exists
MSGPTR: BLOCK 1 ; Pointer to message
CAPENB: BLOCK 1 ; saved capabilities
PC1: BLOCK 1 ; PC save locations for PSI code
PC2: BLOCK 1
PC3: BLOCK 1
LEVTAB: PC1
PC2
PC3
CHNTAB: 2,,CONECT ; connect initiate on level 2
1,,TIMOUT ; timeout PSI on level 1
XLIST ; nothing else
REPEAT ^D34,<EXP 0>
LIST
NMAIL:: RESET
MOVE P,[-STKLEN,,STACK]
MOVE T1,[SIXBIT /NMAIL/]
MOVE T2,[SIXBIT /NMAIL/]
SETSN ; declare our name for statistics
JFCL
MOVX T1,.NDGLN ; get local node name function
MOVE T2,[POINT 7,HSTNAM]
MOVEM T2,1(P) ; put pointer on stack
MOVEI T2,1(P) ; and point to it
NODE ; get node name
JERR <Can't get local node name>
HRROI T1,IOFNAM ; Build interoffice mail handler name
HRROI T2,[ASCIZ /Interoffice-mail-/]
SETZB T3,T4 ; First part
SOUT
MOVEM T1,1(P) ; Save on stack
MOVEI T2,1(P)
MOVX T1,.NDGLN ; Get local node name function
NODE
JERR <Can't get local node name>
MOVX T1,.FHSLF ; this process
MOVE T2,[LEVTAB,,CHNTAB]
SIR ; init PSI system
EIR
CALL OPNLOG ; open log file
MOVEM T1,LOGJFN ; save JFN
CALL DTSTMP ; time stamp it
HERALD \.VER,\.EDT
LOG< on node >
TMSG < on node >
HRROI T1,HSTNAM
PSOUT
TMSG <
>
HRROI T1,HSTNAM
CALL LOGMSG
CALL LGCRLF ; CRLF to log file
MOVE T1,LOGJFN ; close log file for perusers
CLOSF
JFCL
NMAIL0: CALL OPNLSN ; open connection and set interrupt up
WAIT ; for connect initiate
;Here when connection initiated
CONECT: MOVE P,[-STKLEN,,STACK] ; reset stack
CALL TIMEIT ; time this guy
CALL OPNLOG ; open log file
MOVEM T1,LOGJFN
CALL DTSTMP ; time stamp this transaction
LOG <----Connect from >
CALL T4NHST ; type foreign host name at log file
CALL LGCRLF ; log a CRLF
MOVE T1,NETJFN ; accept connection
MOVX T2,.MOCC
SETZB T3,T4 ; no additional data
MTOPR
JERR <Couldn't accept net connection>
MOVX T1,.HPELP ; elapsed time since system startup
HPTIM ; snarf it
JERR <HPTIM failed>
MOVEM T1,NTIME ; remember time this reception started
CALL RDMAIL ; read the mail from foreign host
JRST ERRXIT
MOVX T1,.HPELP ; read clock again
HPTIM
JERR <HPTIM failed>
SUB T1,NTIME ; time this transaction took
MOVEM T1,ELPTIM ; remember it
CALL PARSE ; parse the mail
JRST ERRXIT ; failed, quit now
CALL DTSTMP ; time stamp log
LOG <Message from >
HRROI T1,FRMBUF ; sender's name
CALL LOGMSG ; log it
LOG < received
>
CALL LSTATS ; log statistics
CALL MAILIT ; send the mail off
DIE <Failure return from MAILIT>
ERRXIT: CALL CLZNET ; close and reopen net link
CALL CNCLTM ; cancel timeout request
CALL DTSTMP
LOG <----Connection closed
>
MOVE T1,LOGJFN
CLOSF ; close log file for perusers
JFCL
DEBRK ; return to background
;Parse mail received. Place sender name in FRMBUF, recipient directory
; numbers in ULIST, terminated with a zero entry
; Headers must appear in the following order:
; From, To, cc
; Returns +1: failure
; +2: success
PARSE: STKVAR <DLVPTR,SPTR,SCNT>
SETZM PAPERF ; No paper mail yet
SETZM DLVPTR
MOVE T1,[POINT 7,BIGBUF]
MOVEM T1,MSGPTR
MOVSI P1,-^D100 ; Maximum destinations allowed
MOVEM PTR,SPTR ; Save context
MOVEM CNT,SCNT ; ..
FIND <Deliver-to:> ; Overriding delivery list exist?
JRST PARSE0 ; No, nothing special then
CALL PARSE5 ; Yes, parse it for addresses then
RET ; Failure, pass it on up
MOVNI T1,1 ; Back up the pointer one
ADJBP T1,PTR ; ..
MOVEM T1,DLVPTR ; Flag that this occurred
PARSE0: FIND <From:> ; find sender
JRST [ CALL SKPLIN ; not on this line, try next
JRST [ CALL DTSTMP ; time stamp log file
ETMSG <Can't find "From:" field>
CALL LGCRLF
RET] ; failure return
JRST PARSE0] ; try next line
SETZM FRMBUF ; clear sender name area
MOVE T1,[FRMBUF,,FRMBUF+1]
BLT T1,FRMBUF+NFRMBF-1 ; ..
CALL SKPBLK ; skip white space
RET ; text exhausted
MOVE T1,PTR ; copy current pointer into mail
MOVE T2,[POINT 7,FRMBUF] ; copy rest of this line into FRMBUF
CALL CPYLIN ; ..
SKIPE T1,DLVPTR ; Overriding delivery list specified?
JRST [ MOVEM T1,MSGPTR ; Yes, skip over it then
RETSKP] ; Return now
MOVE PTR,SPTR ; Restore context
MOVE CNT,SCNT ; ..
PARSE2: FIND <To:> ; find "To:" list
SKIPA ; Not there
JRST PARSE3 ; OK, eat it up
FIND <Redistributed-to:> ; Maybe this flavor?
SKIPA ; Nope
JRST PARSE3 ; Win!
FIND <Circulate-next:> ; Maybe this flavor?
JRST [ CALL SKPLIN ; not on this line, try next
JRST [ CALL DTSTMP ; none found, complain
ETMSG <Can't find "To: field>
CALL LGCRLF
RET] ; failure return
JRST PARSE2] ; go try again
; JRST PARSE3
;Here to parse "To:" list
PARSE3: CALL PRSUSR ; get a username
JRST [ CALL DTSTMP ; none found, complain
ETMSG <Invalid username in "To:" list: >
MOVE T1,NETJFN
HRROI T2,ATMBUF ; tell user what lost
SETZB T3,T4
SOUTR
HRROI T1,ATMBUF ; also log losing name
CALL LOGMSG
CALL LGCRLF
RET] ; failure return
JUMPE T1,PARSE4 ; dirnum=0 means list exhausted
MOVEM T1,ULIST(P1) ; save this directory number
AOBJN P1,PARSE3 ; go for more
CALL DTSTMP ; woops, too many
ETMSG <Too many user names in "To:" list, truncated at >
MOVE T1,NETJFN
HRROI T2,ATMBUF ; tell him last name accepted
SETZB T3,T4
SOUTR
HRROI T1,ATMBUF ; also tell log file
CALL LOGMSG
; JRST PARSE4
; continue parsing header...
PARSE4: FIND <cc:>
JRST [ FIND <Redistributed-cc:>
JRST PARSE6 ; absence of CC list is OK
JRST .+1]
PARSE5: CALL PRSUSR ; get a user name
JRST [ CALL DTSTMP ; garbage
ETMSG <Invalid user name in "cc:" list >
MOVE T1,NETJFN ; tell him what lost
HRROI T2,ATMBUF
SETZB T3,T4
SOUTR
HRROI T1,ATMBUF ; and log it
CALL LOGMSG
CALL LGCRLF
RET] ; failure return
JUMPE T1,PARSE6 ; all done
MOVEM T1,ULIST(P1) ; store another recipient dirnum
AOBJN P1,PARSE5 ; go round for more
CALL DTSTMP ; too many
ETMSG <Too many names in "cc:" list, truncated at >
MOVE T1,NETJFN
HRROI T2,ATMBUF
SETZB T3,T4
SOUTR
HRROI T1,ATMBUF
CALL LOGMSG
PARSE6: SETZM ULIST(P1) ; tie off recipient list
MOVE T1,PTR ; get current pointer
SKIPE PAPERF ; Any paper queued out?
RETSKP ; Yes, don't worry if no electric recipients
TRNN P1,-1 ; any recipients specified?
JRST [ CALL DTSTMP ; no, log failure
ETMSG <No recipients on this host specified>
CALL LGCRLF
RET]
RETSKP ; all done!
;Parse user name, ignoring if not on this host
;Returns +1: source exhausted or garbage in line
; +2: OK, T1 has dirnum of user or zero if list exhausted
PRSUSR: CALL SKPBLK ; skip white space
RET ; text exhausted, quit
LDB T1,PTR ; get nonblank char
CAIN T1,74 ; Open angle bracket?
JRST [ CALL GETCHR ; Yes, eat it up
RET
JRST PRSUS1] ; Go parse the mailbox name
CAIN T1,.CHLFD ; EOL?
JRST [ CALL GETCHR ; Yes, skip it
RET ; Quit if text gone
CAIE T1,40 ; Does next line start with space
CAIN T1,11 ; or tab?
JRST PRSUSR ; Yes, it's a continuation line
SETZ T1, ; No, indicate list exhausted
RETSKP]
CAIN T1,";" ; Address list terminator?
JRST [ CALL GETCHR ; Yes, skip it
RET ; Text exhausted, pass failure up
JRST PRSUSR] ; Try again
CAIN T1,"," ; Comma?
JRST [ CALL GETCHR ; yes, skip it
RET ; source gone, quit
CALL SKPBLK ; skip blanks
RET ; text gone, quit
LDB T1,PTR ; get first nonblank char
CAIN T1,.CHLFD ; EOL?
JRST [ CALL SKPLIN ; yes, list cont'd on next line
RET ; text gone, error
JRST PRSUSR] ; parse next line
JRST .+1] ; not new line, keep parsing this one
;We now should be looking at the beginning of a mailbox name
PRSUS1: CALL GETTOK ; get token
RET ; text exhausted, quit
CALL SKPBLK ; skip blanks
RET ; text gone, quit
LDB T1,PTR ; Get terminator
CAIN T1,74 ; Open angle bracket (start of mailbox name)?
JRST [ CALL GETCHR ; Yes, flush it and continue
JFCL
JRST PRSUSR]
FIND <:> ; Address list name?
SKIPA ; No, keep looking
JRST PRSUSR ; Yes, just ignore it
FIND <@> ; look for node name separator
SKIPA ; not this kind
JRST PRSUS2 ; found old style separator
FIND <at> ; look for fancy separator
JRST PRSUSR ; none of the above, keep trying
LDB T1,PTR ; get terminator
CAIE T1," " ; "at" should be followed by space
RET ; it isn't, problem
CALL GETCHR ; it is, eat the space
RET ; text gone
;OK, now have found a network address. See if host name is our name.
PRSUS2: CALL PRSHST ; see if this is for us
RET ; text exhausted, quit
JUMPE T1,PRSUSR ; not us, try next name in list
MOVE T2,[POINT 7,ATMBUF] ; Point to username
MOVE T3,T2 ; Make temp copy of ptr
ILDB T1,T3 ; Get first char of username
CAIE T1,42 ; Is this a quoted string?
JRST PRSUS5 ; No, do normal thing
PRSUS3: ILDB T1,T3 ; Yes, unquote it
JUMPE T1,PRSUS4 ; End of text?
CAIN T1,42 ; Or found matching close quote yet?
JRST PRSUS4 ; Yes, go finish up
IDPB T1,T2 ; No, move this byte up
JRST PRSUS3
PRSUS4: SETZ T1,
IDPB T1,T2 ; Insure ASCIZ
HRROI T2,ATMBUF ; Point at dequoted string
PRSUS5: MOVX T1,RC%EMO ; see if we know about this guy
RCUSR ; ..
ERJMP R ; bad syntax in username
TXNE T1,RC%NOM ; match found?
JRST R ; no, bad return
MOVE T1,T3 ; return user number in T1
RETSKP
;Parse host name, check to see if local host
;Returns +1: text exhausted
; +2: T1/ -1 => local host, 0 => not for this host
;If host name is "Interoffice-mail", queues message to line printer
; so operator can rip it off and put it in the mail
PRSHST: MOVE T1,[POINT 7,HSTNAM]
CALL FINDIT ; find host name
JRST PRSHS1 ; not found, skip to end of this username
CALL SKPBLK ; eat spaces
RET
SETZ T1,
LDB T2,PTR ; found, terminator must be comma, EOL, or
CAIN T2,";" ; semicolon
SETO T1,
CAIE T2,","
CAIN T2,.CHLFD
SETO T1, ; Indicate success
CAIN T2,76 ; Trailing close of mailbox stuff OK too
JRST [ CALL GETCHR ; eat the wedge though
JFCL
SETO T1,
JRST .+1]
RETSKP ; return OK
;Not this host, see if destined for paper mail system
PRSHS1: MOVE T1,[POINT 7,IOFNAM]
CALL FINDIT ; Is this "Interoffice-mail-<local-node>"?
JRST PRSHS2 ; Nope -- just ignore this one, then
CALL PAPER ; Yes, print paper
; Fall thru to skip this address
PRSHS2: CALL SKPBLK ; skip blanks
RET
LDB T1,PTR ; eat stuff until comma or EOL
CAIE T1,","
CAIN T1,.CHLFD
JRST [ SETZ T1, ; indicate failure
RETSKP] ; return
CALL GETCHR ; next char
RET ; text gone, pass failure on up
JRST PRSHS2
;Skip to first nonblank char in mail
;Returns +1: text exhausted
; +2: OK, PTR points at first nonblank character
SKPBLK: LDB T1,PTR ; get char
CAIN T1,"" ; ignore control-Vs
JRST SKPBL1
CAIE T1,.CHCRT ; ignore returns
CAIN T1," " ; and blanks
SKIPA
RETSKP
SKPBL1: CALL GETCHR ; skip to next
RET ; text gone, error
JRST SKPBLK
;Here to queue paper copy of message out -- recipient
; isn't on electronic mail, so paper must be printed and mailed
PAPER: PUSH P,PTR ; Don't disturb byte pointer or count
PUSH P,CNT ; ..
MOVX T1,GJ%SHT ; Try to get the lowercase line printer
HRROI T2,[ASCIZ /LL:Interoffice-mail/]
GTJFN
ERJMP [MOVX T1,GJ%SHT ; Try for any old printer, then
HRROI T2,[ASCIZ /LPT:Interoffice-mail/]
GTJFN
ERJMP PAPERR
JRST .+1] ; OK, got LPT, rejoin main flow
MOVX T2,<070000,,0>+OF%WR ; Open for write
OPENF
ERJMP PAPERR ; Oops... something's screwy
SETOM PAPERF ; Remember paper printed
HRROI T2,[ASCIZ /
I n t e r o f f i c e M a i l
To:
/] ; Print header
SETZB T3,T4
SOUT
JERR <Output to line printer failed>
HRROI T2,ATMBUF ; Name of recipient
SOUT ; Write it
JERR <Output to line printer failed>
PUSH P,T1 ; Save LPT JFN for a bit
CALL DTSTMP ; Log this recipient
LOG <Paper mail queued for >
HRROI T1,ATMBUF ; Write address
CALL LOGMSG
CALL LGCRLF
POP P,T1 ; Restore LPT JFN
HRROI T2,[ASCIZ /
From:
/]
SOUT
JERR <Output to line printer failed>
HRROI T2,FRMBUF ; Write name of sender
SOUT
JERR <Output to line printer failed>
HRROI T2,[ASCIZ /
Printed by and mailed from DECSYSTEM-20 /]
SOUT
JERR <Output to line printer failed>
HRROI T2,HSTNAM ; Our name
SOUT
JERR <Output to line printer failed>
MOVEI T2,14 ; New page
BOUT
HRROI T2,[ASCIZ /
+---------------+
! d i g i t a l ! I n t e r o f f i c e M e m o r a n d u m
+---------------+
/]
SOUT
HRROI T2,BIGBUF ; Now output the text of the message
SOUT
JERR <Output to line printer failed>
CLOSF
JERR <Can't close line printer>
PAPERX: POP P,CNT
POP P,PTR
RET
;Can't open LPT -- complain to other end of link, and to log file
PAPERR: CALL DTSTMP ; Time stamp log file
ETMSG <Can't generate paper mail for >
MOVE T1,NETJFN
HRROI T2,ATMBUF
SETZB T3,T4 ; Complain
SOUTR
HRROI T1,ATMBUF
CALL LOGMSG ; Log file, too
CALL LGCRLF
JRST PAPERX ; Quit now
;Skip to beginning of next line
;Returns +1: no more text left
; +2: OK, PTR points to beginning of line
SKPLIN: LDB T1,PTR ; get next char
CAIN T1,.CHLFD ; did we just eat an EOL?
CALLRET GETCHR ; yes, skip over it and return
CALL GETCHR ; no, eat next char
RET ; text exhausted
JRST SKPLIN ; no, eat another
;Get one character from mail
;Returns +1: no more text left
; +2: OK, char eaten in T1
GETCHR: JUMPE CNT,R ; any text left?
ILDB T1,PTR ; yes, snarf char
SOJA CNT,RSKP ; decrement count and return
;Search for a string. Called by FIND macro.
;Call: T1/ pointer to string to find
; CALL FINDIT
;Returns +1: string not found, PTR and CNT preserved
; +2: string found, PTR and CNT updated to point past string
FINDIT: PUSH P,PTR ; save current pointer info
PUSH P,CNT ; in case string not found
MOVE P3,T1 ; copy T1 to safe place
FINDT1: LDB T2,PTR ; fetch a char from source
CAIL T2,"a" ; if lowercase,
CAILE T2,"z" ; ..
SKIPA ; ..
TRZ T2,40 ; then upperize it
ILDB T3,P3 ; fetch a char from test string
JUMPE T3,[ADJSP P,-2 ; test string gone, match found
RETSKP] ; flush old pointers and give OK return
CAIL T3,"a" ; upperize it also
CAILE T3,"z" ; ..
SKIPA ; ..
TRZ T3,40 ; ..
CAME T2,T3 ; matching so far?
JRST FINDTX ; no, quit now and restore pointers
CALL GETCHR ; skip to next char in mail
SKIPA ; none left, fail
JRST FINDT1 ; more to look at, keep going
FINDTX: POP P,CNT ; no match, restore pointers
POP P,PTR ; and give bad return
RET
;Get token - alphanumeric string - and store in ATMBUF
;Returns +1: text exhausted
; +2: OK
GETTOK: ACVAR <PTR1,CNT1>
SETZM ATMBUF ; clear atom buffer
MOVE T1,[ATMBUF,,ATMBUF+1]
BLT T1,ATMBUF+NATMBF-1
CALL SKPBLK ; skip blanks
RET ; text gone, quit
MOVE PTR1,[POINT 7,ATMBUF]
MOVEI CNT1,^D132 ; max chars in token
LDB T1,PTR ; See if this username is a quoted string
CAIE T1,42 ; Does it start with double quote?
JRST GETTK1 ; No, do ordinary thing
GETTK6: IDPB T1,PTR1 ; Yes, start storing literally
GETTK7: CALL GETCHR ; Get next chr
RET ; Text exhausted
CAIE T1,42 ; Close quote?
JRST [ SOJG CNT1,GETTK6 ; No, keep storing if room left
JRST GETTK7] ; Else just drop chars on floor
IDPB T1,PTR1 ; Yes, save close quote
CALL GETCHR ; Skip it
RET ; and fall thru to do rest of address
GETTK1: CALL GTALPH ; get and uppercase alphanumeric char
RET
JUMPE T1,GETTK2 ; Nonalphanumeric
IDPB T1,PTR1 ; store
SOJG CNT1,GETTK1 ; go for more
RETSKP ; return OK
GETTK2: CAIE CNT1,^D132 ; Did we find anything?
RETSKP ; Yes, everything's cool then
LDB T1,PTR ; No, 1st chr of name bad -- retrieve it
GETTK3: IDPB T1,PTR1 ; Save bad name for error message
GETTK4: CALL GETCHR ; Get next chr of bad name
RET ; Skip to comma or EOL then, this name is bad
CAIE T1,"," ; ..
CAIN T1,12 ; ..
RET ; OK, give bad return now
SOJG CNT1,GETTK3 ; Be careful not to overflow atom buffer
JRST GETTK4 ; Full, just toss chars into bit bucket now
;Get alphanumeric char from mail. Ctrl-V, hyphen, apostrophe, dot,
; and underscore are considered alphanumeric.
;Returns +1: text exhausted
; +2: T1=0 if next char no alphanumeric
; T1 contains char if alphanumeric
GTALPH: LDB T1,PTR ; sniff at this char
CAIN T1,"_" ; allow underscore
JRST GTALP1 ; ..
CAIE T1,"'" ; allow apostrophe
CAIN T1,"-" ; allow hyphens
JRST GTALP1 ; ..
CAIE T1,"." ; also dots
CAIN T1,"" ; and ctrl-Vs
JRST GTALP1 ; ..
CAIL T1,"A" ; uppercase alphabetic?
CAILE T1,"Z" ; ..
SKIPA ; no, check some more
JRST GTALP1 ; yes, pass it through
CAIL T1,"a" ; lowercase?
CAILE T1,"z" ; ..
SKIPA ; no, check more cases
JRST [ TRZ T1,40 ; yes, upperize it
JRST GTALP1] ; and pass it
CAIL T1,"0" ; digit?
CAILE T1,"9" ; ..
JRST [ SETZ T1, ; no, indicate nonalphanumeric
RETSKP]
GTALP1: PUSH P,T1 ; save this char
CALL GETCHR ; char OK, skip to next
JFCL ; next call will catch this
POP P,T1 ; return char skipped
RETSKP
;Here to read mail from net connection into BIGBUF.
;
; Returns +1: problem (timeout, data error, etc.)
; +2: OK, PTR and CNT inited
RDMAIL: HRROI PTR,BIGBUF ; init byte pointer,
SETZB CNT,P2 ; count, and error flag
MOVEI P1,^D240 ; Allow one minute to read each chunk
RDMAL1: MOVE T1,NETJFN ; any input for us?
SIBE ; ..
JRST [ MOVEI P1,^D240 ; reset timer
MOVE T3,T2 ; put count in right AC for SIN
JRST RDMAL2] ; and go snarf some text
MOVEI T1,^D250 ; wait one-fourth of a second
DISMS
SOJG P1,RDMAL1 ; go try again
CALL DTSTMP
LOG <Timeout after >
MOVE T1,LOGJFN
MOVE T2,CNT ; Report progress
MOVX T3,^D10
NOUT
JFCL
LOG < bytes.>
CALL LGCRLF
CALL DTSTMP
ETMSG <Timed out waiting for input>
CALL LGCRLF
RET ; failure return
RDMAL2: ADD CNT,T3 ; keep track of byte count
CAMLE CNT,[BBFLEN*5] ; buffer overflow pending?
CALL [ HRROI PTR,BIGBUF ; just suck bits
SKIPE P2 ; noticed this already yet?
RET ; yes, just rejoin loop
PUSH P,T3 ; preserve count
CALL DTSTMP ; log the failure
ETMSG <Mail too long> ; and tell the other end
CALL LGCRLF
SETO P2, ; remember that the error occurred
POP P,T3 ; restore byte count
RET] ; rejoin main loop
MOVE T1,NETJFN ; net link JFN
MOVE T2,PTR ; current byte pointer
SETZ T4, ; don't stop on any special byte
SIN ; read some stuff
ERJMP [CAIE T1,IOX4 ; end of file?
DIE <Data error reading from net link> ; *** should be fancier
JRST RDMAL3] ; yes, all done
MOVE PTR,T2 ; update byte pointer
LDB T1,T2 ; get terminating byte
CAIE T1,177 ; rubout?
JRST RDMAL1 ; no, go for more input
; ..
; ..
RDMAL3: SKIPE P2 ; any errors?
JRST R ; yes, failure return
SETZ T1, ; insure ASCIZ
DPB T1,T2 ; ..
SUBI CNT,1 ; don't count last character
MOVE PTR,[POINT 7,BIGBUF,6]
MOVEM CNT,BYTCNT ; save byte count
RETSKP ; good return
;Here to actually append the mail to users' MAIL.TXT files
; Also types annoying messge on their terminal if they're logged in
;
; Returns +1: problems of some sort
; +2: OK
MAILIT: HRROI T1,FRMMSG ; build annoying msg to splat across screens
HRROI T2,[ASCIZ /
[You have a message from /]
SETZB T3,T4
SOUT
HRROI T2,FRMBUF ; name of sender
SOUT
HRROI T2,[ASCIZ /]
/]
SOUT
SETZB P1,P2 ; init index and failure flag
MAILT1: MOVE T1,ULIST(P1) ; get next recipient
JUMPE T1,MAILT2 ; end of list
CALL SENDIT ; use code stolen from MAILER to do it
SETO P2, ; remember that a failure occured
AOJA P1,MAILT1
MAILT2: SKIPE P2 ; any problems?
JRST RSKP ; yes, don't log success then
SKIPN P1 ; anything sent?
JRST [ SKIPN PAPERF ; No, any paper printed?
DIE <No mail sent at MAILIT> ; No, complain
CALL DTSTMP ; Yes, log lack of local users
LOG <No local electronic recipients>
CALL LGCRLF
JRST .+1]
CALL DTSTMP ; log success
NTMSG <sent OK> ; Send reassuring message to other end
CALL LGCRLF ; CRLF the log file
RETSKP
;Append mail to user's mail file
; Call with user number of recipient in T1
SENDIT: ACVAR <W6>
STKVAR <<GETSIZ,2>,EOFPTR,USRNO,<TEMP1,50>,TEMP2>
MOVEM T1,USRNO ;save recipients user number
HRROI T1,TEMP1 ;where to build filespec string
HRROI T2,[ASCIZ /PS:</] ;prefix
SETZB T3,T4
SOUT
JERR <SOUT failed at SENDR>
MOVE W6,T1 ;preserve string pointer
CALL DTSTMP ;time stamp log file
HRROI T1,[ASCIZ /Sending to /]
CALL LOGMSG
MOVE T1,LOGJFN ;write username to log file
MOVE T2,USRNO ; ..
DIRST
JERR <DIRST failure to log file>
CALL LGCRLF ; CRLF to the log file
MOVE T1,W6 ;restore string pointer
MOVE T2,USRNO ;get user number
DIRST ;add user name
JERR <DIRST failure>
HRROI T2,[ASCIZ />MAIL.TXT/]
SETZB T3,T4
SOUT
JERR <SOUT failed at SENDR 2>
MOVX T1,GJ%SHT!GJ%DEL+1
HRROI T2,TEMP1 ;where filespec lives
GTJFN
JERR <Can't GTJFN MAIL.TXT>
MOVEI W6,^D40 ; Number of 1/2 sec. intervals to wait
; JRST SENDT1
SENDT1: MOVE T2,[070000,,300000]
MOVE T3,T1 ; Preserve JFN in case OPENF loses
OPENF
ERJMP [CAIN T1,OPNX9 ; Message file busy?
SOJGE W6,[MOVEI T1,^D500 ; Yes, if not too long,
DISMS ; Wait 1/2 second
MOVE T1,T3 ; Restore JFN
JRST SENDT1] ; And retry OPENF
CALL DTSTMP
ETMSG <Can't OPENF MAIL.TXT
>
RET] ; Failure return
MOVE T2,[2,,.FBBYV] ;GET 2 WORDS
MOVEI T3,GETSIZ ;WHERE TO GET IT
GTFDB ;READ FILE DATA
LOAD T3,FB%BSZ,GETSIZ ;GET FILE BYTE SIZE
CAIN T3,7 ;ALREADY HAVE 7-BIT BYTE COUNT?
JRST [ MOVE T2,1+GETSIZ ;YES, FETCH IT
JRST SENDT2] ;SKIP FANCY GYRATIONS
MOVEI T2,44 ;NO, GET BITS PER WORD
IDIVI T2,0(T3) ;COMPUTE TOTAL BYTES PER WORD
EXCH T2,1+GETSIZ ;GET BYTES IN B
IDIV T2,1+GETSIZ ;COMPUTE WORDS
IMULI T2,5 ;NOW COMPUTE # OF CHARACTERS
SENDT2: MOVEM T2,EOFPTR ;SAVE IT
SFPTR ;SET TO EOF
JFCL
; ..
; ..
CALL CAPOFF ; disable caps, so quota is checked
SETOM T2 ;GET DATE AND TIME
MOVSI T3,(OT%TMZ) ;IN THIS FORM
ODTIM
ERJMP OVRQTA ;ERROR
MOVEI T2,","
BOUT ;SEPARATE TIME FROM COUNT
ERJMP OVRQTA ;ERROR
RFPTR ;READ POSITION IN FILE
JFCL
ADDI T2,6 ;AT LEAST 6 DIGITS FOR COUNT
IDIVI T2,5 ;GET PART OF WORD IN C
MOVNS T3 ;GET NEGITIVE OF REMAINDER
ADDI T3,5+6 ;GET WIDTH OF COUNT FIELD
HRL T3,T3 ;GET IN RIGHT POSITION FOR NOUT
TXO T3,NO%LFL!NO%ZRO ;PUT IN LEADING ZEROS
MOVE T2,BYTCNT ;NUMBER OF CHARS
HRRI T3,12 ;IN DECIMAL
NOUT
ERJMP OVRQTA ;ERROR
HRROI T2,[ASCIZ /;000000000000
/]
MOVEI T3,0 ;PUT ON THE FLAG FIELD
SOUT
ERJMP OVRQTA ;ERROR
MOVE T2,MSGPTR ;Pointer to message
MOVN T3,BYTCNT ;GET NEGATIVE byte COUNT
SOUT ;WRITE ALL WORDS
ERJMP OVRQTA ;ERROR
CALL CAPON ; enable again
CALL UPDFIL ; Update file pages
HRLI T1,.FBCTL ;CHANGE STATUS BITS
MOVX T2,FB%DEL ;CHANGE DELETED BIT
SETZ T3, ;MAKE IT A ZERO(UNDELETE)
TXO T1,CF%NUD ;DONT'T UPDATE DIR (SFUST/CLOSF WILL)
CHFDB ;DO IT
MOVX T2,FB%PRM ;CHANGE PERMANENT BIT
MOVX T3,FB%PRM ;TO BE SET
CHFDB
MOVEI T1,(T1) ;JFN ONLY
MOVE W6,T1 ; preserve JFN
HRROI T1,TEMP1 ; where to put string
CALL QUOTE ; move ctrl-V'ed "from" string to TEMP1
MOVE T1,W6 ; restore JFN
HRLI T1,.SFLWR ;set last writer
HRROI T2,TEMP1 ;sender string
SFUST
JERR <SFUST failure>
MOVEI T1,(T1) ;JFN only
CLOSF ;CLOSSE THE OUTPUT FILE
JFCL
; ..
;ROUTINE TO SEND MESSAGES TO ANY LOGGED IN USERS
SETZ W6, ;INIT JOB NUMBER FOR SCAN
TOPDIR: MOVEI T1,0(W6) ;JOB NUMBER
MOVE T2,[-<.JICPJ-.JITNO+1>,,GTINF] ;GET VALUES FROM MONITOR
MOVEI T3,.JITNO ;GET TERM # AND LOGGED IN DIR
GETJI ;GET THEM
ERJMP [ CAIN T1,GTJIX3 ;OUT OF RANGE?
JRST RSKP ;yes, all done -- success return
AOJA W6,TOPDIR] ;NO. DO NEXT ONE THEN
SKIPL <.JICPJ-.JITNO>+GTINF ;IS THIS A PTY?
AOJA W6,TOPDIR ;YES. SKIP IT THEN
DMOVE T1,GTINF ;GET GETJI DATA IN REGS
JUMPL T1,[AOJA W6,TOPDIR] ;IF DETACHED, GO ON.
CAME T2,USRNO ;IS THIS LOGGED INTO THE SAME DIR?
AOJA W6,TOPDIR ;NO. SKIP IT THEN
TRO T1,(1B0) ; MAKE IT A DEVICE DESIGNATOR
RFMOD ; GET MODE BITS
TXNE T2,TT%DAM ; IS HE IN ASCII?
TXNN T2,TT%ALK ; IS HE ACCEPTING?
AOJA W6,TOPDIR ;NO. DON'T TELL HIM THEN
MOVEI T2,.MORNT ;SEE IF HE WANTS MESSAGES
MTOPR
JUMPN T3,INCDIR ;JUMP IF NO MESSAGE
HRROI T2,FRMMSG ;GET MESSAGE BLOCK
TTMSG ;SEND TO THIS USER
INCDIR: AOJA W6,TOPDIR ;DO ALL JOBS
;Here to copy (and quote) "from" string into area pointed to by T1
; Quotes all characters (to save trouble of checking need for it)
QUOTE: MOVE T2,[POINT 7,FRMBUF]
TLC T1,-1 ; lh of byte pointer all ones?
TLCN T1,-1 ; ..
HRLI T1,(POINT 7,) ; yes, make real byte pointer
MOVEI T4,<24*5>-1 ; maximum characters allowed in string
QUOTE1: MOVEI T3,"" ; quote character
IDPB T3,T1 ; stuff it
ILDB T3,T2 ; next char of source string
IDPB T3,T1 ; stuff it
JUMPE T3,[MOVNI T2,1 ; if zero, back up over last ctrl-V
ADJBP T2,T1 ; ..
DPB T3,T1 ; wipe it out with null
RET] ; and return
SOJGE T4,QUOTE1 ; insure no overflow
DIE <QUOTE overflow>
;Routine to force write of pages just written in case of crash
;Call: T1/ JFN
;Return +1: always
UPDFIL: RFBSZ ; Get byte size
JFCL
MOVEI T3,^D36 ; Bits in a word
IDIVI T3,(T2) ; Compute bytes in a word
MOVEM T3,TEMP2 ; Save for later
RFPTR ; Get EOF pointer
JFCL
IDIV T2,T3 ; Compute words in file
SKIPN T3 ; Even number of words?
SUBI T2,1 ; Yes, don't cross over to nonex. page
MOVE T3,EOFPTR ; Get original EOF pointer
IDIV T3,TEMP2 ; Compute original word count
LSH T2,-^D9 ; Compute page number just written
LSH T3,-^D9 ; Compute original last page number
MOVE T4,T2 ; Copy page no. just written
SUBI T4,(T3) ; Pages written
ADDI T4,1 ; Plus one for partial page
HRLZS T1 ; JFN in LH for UFPGS
HRRI T1,(T3) ; First page to update
MOVEI T2,(T4) ; Page count
TXO T2,UF%NOW ; Don't block
UFPGS ; Write these pages to disk
JERR <UFPGS failure>
HLRZS T1 ; Restore T1 to good state
RET ; and return
;HERE ON QUOTA ERROR
; T1/ JFN
OVRQTA: CALL CAPON ; re-enable caps
RFBSZ ;get current byte size
JFCL
MOVEI T3,^D36 ;compute bytes per word
IDIVI T3,(T2) ; ..
MOVEM T3,TEMP2 ; save
RFPTR ;GET CURRENT EOF POINTER
JFCL
IDIV T2,TEMP2 ;compute words
LSH T2,-11 ;MAKE IT A PAGE NUMBER
MOVE T3,EOFPTR ;GET ORIGINAL POINTER
IDIV T3,TEMP2 ;compute word number
LSH T3,-11 ;GET PAGE NUMBER
SUB T2,T3 ;compute no. of pages added
JUMPE T2,OVRQT2 ;if none added, all set
EXCH T3,T2 ;put count in proper register
TXO T3,1B0 ;REPEAT COUNT FOR PMAP
HRL T2,T1
ADDI T2,1 ;STARTING PAGE
SETOM T1
PMAP ;ZAP THE FILE PAGES
HLRZ T1,T2 ;JFN AGAIN
;Extra pages now deleted. Set byte count in FDB
OVRQT2: HRLI T1,.FBBYV ;word containing byte size
MOVX T2,FB%BSZ ;set byte size
MOVX T3,FLD(7,FB%BSZ) ; to 7 bits
CHFDB ;if failed, quit now
ERJMP OVRQT0
HRLI T1,.FBSIZ ;set size of file
SETO T2, ; entire word
MOVE T3,EOFPTR ; to original count
CHFDB ;zap
JFCL
OVRQT0: CLOSF ;CLOSE THE FILE
JFCL
CALL DTSTMP ; pretty up the log file
MOVX T1,.FHSLF ;Get last TOPS20 error
GETER ; ..
HRRZS T2 ;Error code only please
CAIN T2,IOX11 ;Over quota?
JRST OVRQT1 ;Yes, go handle that
CAIN T1,IOX34 ;Disk structure completely full?
JRST OVRQT3 ;Yes, go handle that
ETMSG <Unexpected JSYS error: >
MOVE T1,LOGJFN ;Where error string will go
HRLOI T2,.FHSLF ;Most recent error
SETZ T3,
ERSTR
JFCL
JFCL
NTMSG <
>
CALL DTSTMP
ETMSG <Error occurred sending to user >
MOVE T1,NETJFN
MOVE T2,USRNO
DIRST
JERR <DIRST failure>
MOVE T1,LOGJFN
DIRST
JERR <DIRST failure>
NTMSG <
>
RET
OVRQT1: ETMSG <User >
MOVE T1,NETJFN
MOVE T2,USRNO
DIRST
JERR <DIRST failure>
MOVE T1,LOGJFN
DIRST
JERR <DIRST failure>
NTMSG < over quota, not sent.
>
RET
OVRQT3: NTMSG <%Disk structure completely full, try again later
>
RET
;Open log file
OPNLOG: MOVX T1,GJ%SHT ; Try logical name first
HRROI T2,[ASCIZ /DECNET-LOG:NMAIL.LOG/]
GTJFN
ERCAL [MOVX T1,GJ%SHT ; failed, write onto SYSTEM:
HRROI T2,[ASCIZ /SYSTEM:NMAIL.LOG/]
GTJFN
ERJMP OPNERR
RET]
MOVX T2,<070000,,0>+OF%APP
OPENF ; Open for append
ERJMP OPNERR
HRRZ T1,T1 ; Return JFN only
RET
OPNERR: HRROI T1,[ASCIZ /NMAIL: Can't open log file because: /]
ESOUT
MOVX T1,.PRIOU
HRLOI T2,.FHSLF
ERSTR
JFCL
JFCL
JRST FATAL
;Time stamp log file
DTSTMP: MOVE T1,LOGJFN
SETO T2, ; current time
SETZ T3, ; default format
ODTIM
ERJMP [HRROI T1,[ASCIZ /NMAIL: ODTIM failed: /]
ESOUT
MOVX T1,.PRIOU
HRLOI T2,.FHSLF
ERSTR
JFCL
JFCL
TMSG <
DTSTMP called from >
MOVX T1,.PRIOU ; type PC of caller on terminal
HRRZ T2,(P)
MOVX T3,^D8 ; in octal
NOUT
JFCL
JRST FATAL] ; go fire up the world again
MOVEI T2," " ; space
BOUT
RET
;Write ASCIZ string pointed to by T1 to log file
LOGMSG: MOVE T2,T1 ; copy string pointer
MOVE T1,LOGJFN
SETZB T3,T4
SOUT
JERR <Can't write to log file>
RET
;CRLF to log file
LGCRLF: MOVE T1,LOGJFN
MOVEI T2,15
BOUT
JERR <Can't write to log file>
MOVEI T2,12
BOUT
JERR <Can't write to log file>
RET
;Write statistics to log file
LSTATS: STKVAR<ELPTM0>
CALL DTSTMP
MOVE T1,LOGJFN
MOVE T2,ELPTIM ; elapsed time for mail receipt
FLTR T2,T2 ; float it
FDVR T2,[100000.0] ; compute seconds
MOVX T3,<1B1+FL%ONE+FL%PNT+4B23+3B29>
FLOUT ; type seconds
JFCL
MOVEM T2,ELPTM0 ; save time
LOG < seconds, >
MOVE T1,LOGJFN
MOVE T2,BYTCNT ; byte count
MOVX T3,^D10 ; base 10
NOUT
ERJMP [HALTF]
LOG < chars,>
MOVE T1,LOGJFN
FLTR T2,BYTCNT ; float byte count
FDVR T2,ELPTM0 ; compute bytes per second
MOVX T3,<1B1+FL%ONE+FL%PNT+5B23+3B29>
FLOUT
JERR <FLOUT failure>
LOG < chars/sec.
>
RET
;Close net connection and reopen it. Re-enable for interrupts
; on connect initiate messages
CLZNET: MOVEI T1,^D4000 ; Give pipe four seconds to empty
DISMS ; ..
MOVE T1,NETJFN ; normal close
CLOSF
; JERR <Badness while closing NET connection>
ERJMP [CALL DTSTMP ; We should complain about these
LOG <%Close error for net link: >
MOVE T1,LOGJFN
HRLOI T2,.FHSLF
ERSTR
JFCL
JFCL
CALL LGCRLF
MOVE T1,NETJFN
TXO T1,CZ%ABT ; Try real hard to close it
CLOSF ; so we don't eat all job 0 JFNs
JFCL
MOVE T1,NETJFN
RLJFN
JFCL
JRST .+1]
CALL OPNLSN ; open connection again
RET ; return
;Open the net connection and listen for connect initiates
OPNLSN: MOVX T1,GJ%SHT
HRROI T2,[ASCIZ /SRV:201/] ;magic number for MAIL server
GTJFN
JERR <Can't get net JFN for server>
MOVX T2,OF%RD!OF%WR!<070000,,0> ;7-bit bytes
OPENF
JERR <Can't open net JFN>
MOVEM T1,NETJFN ;save it
MOVX T2,.MOACN ;enable for PSI on network transitions
MOVX T3,0B8+<.MOCIA>B17+<.MOCIA>B26 ;channel zero
MTOPR
MOVX T1,.FHSLF
MOVX T2,1B0 ;activate channel zero
AIC
RET
;Log name of foreign host
T4NHST: TRVAR <<HSTNM,2>> ;host names should be short
SETZM HSTNM ;zero this string
SETZM 1+HSTNM ; ..
MOVE T1,NETJFN ;get net JFN
MOVX T2,.MORHN ;return host name
HRROI T3,HSTNM ;where to put it
MTOPR
ERJMP [HRROI T1,[ASCIZ /UNKNOWN-HOST/]
CALL LOGMSG
RET] ;log confusion
HRROI T1,HSTNM ;copy name to log file
CALL LOGMSG ; ..
RET
;Set up to time out if network too slow
TIMEIT: MOVE T1,[.FHSLF,,.TIMEL]
MOVE T2,[TIMEN] ; Milliseconds to allow
MOVEI T3,1 ; channel one
TIMER
JERR <Can't time myself>
MOVX T1,.FHSLF ; activate timer channel
MOVX T2,<1B1>
AIC
RET
;Cancel above timer request
CNCLTM: MOVE T1,[.FHSLF,,.TIMAL] ; remove all pending TIMER requests
MOVEI T3,1 ; for this channel
TIMER
JERR <Can't remove pending TIMER request>
RET
;Here on timeout
TIMOUT: CALL DTSTMP
ETMSG <Mail taking too long to come in>
JRST FATAL
;Here if net link dies while outputting to it
DMPLNK: CIS ; Zap things
CALL LGCRLF ; log a CRLF
MOVEI T1,^D4000 ; Wait four seconds (seems like a good idea)
DISMS
MOVX T1,CZ%ABT ; abort the net JFN
HRR T1,NETJFN ; ..
CLOSF ; ..
JFCL ; don't care
CALL DTSTMP
LOG <----Connection aborted
>
MOVX T1,.FHSLF ; deactivate connect initiate channel
MOVX T2,<1B0> ; ..
DIC ; ..
CALL CNCLTM ; cancel pending timer requests
MOVE T1,LOGJFN ; Close log file
CLOSF
JFCL
JRST NMAIL0 ; go wait for new mail
;Here on fatal wipeout (JSYS which can't fail does, for instance)
FATAL: MOVX T1,.FHSLF
DIR ; disable interrupts
CIS ; clear interrupts
MOVE T1,NETJFN ; type a record to force net buffers out
HRROI T2,[ASCIZ /
?NMAIL internal error/]
SETZB T3,T4 ; add question mark so mail isn't requeued
SOUTR ; ..
ERJMP .+1
MOVEI T1,^D5000 ; wait five seconds
DISMS
MOVX T1,.FHSLF ; abort all JFNs
CLZFF ; ..
CALL OPNLOG ; reopen log file
MOVEM T1,LOGJFN
CALL LGCRLF
CALL DTSTMP
LOG <Error restart...
>
TMSG <NMAIL error restart...
>
MOVEI T1,^D5000 ; wait some more
DISMS
JRST NMAIL ; and fire up the world again
;Copy line pointed to by T1 into space pointed to by T2
CPYLIN: LDB T3,T1 ; get a byte
JUMPE T3,R ; return if null found
CAIE T3,15 ; quit on CR or LF
CAIN T3,12
JRST R
IDPB T3,T2 ; stuff this one
IBP T1 ; next byte
JRST CPYLIN
;Disable capabilities so quota-checking happens
CAPOFF: PUSH P,T1 ; don't clobber
MOVX T1,.FHSLF ; get my caps
RPCAP
MOVEM T3,CAPENB ; remember for later
SETZ T3, ; no caps at all
EPCAP
POP P,T1 ; restore
RET
;Re-enable caps
CAPON: PUSH P,T1 ; no clobberage
MOVX T1,.FHSLF
MOVE T3,CAPENB ; caps we had before
EPCAP
POP P,T1
RET
; Local modes:
; Mode: MACRO
; Comment col:40
; Comment begin:;
; End:
END NMAIL