Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_SRC_3_19910112
-
mm-dom/qdmail.mac
There are 2 other files named qdmail.mac in the archive. Click here to see a list.
TITLE QDMAIL List queued mail files
SUBTTL Written by Tom Rindfleisch
SEARCH MACSYM,MONSYM ;System definitions
SALL ;Suppress macro expansions
.DIRECTIVE FLBLST ;Sane listings for ASCIZ, etc.
.REQUIRE HSTNAM ;Host name routines
.REQUIRE SYS:MACREL ;MACSYM support routines
.TEXT "/NOINITIAL" ;Suppress loading of JOBDAT
.TEXT "QDMAIL/SAVE" ;Save as QDMAIL.EXE
.TEXT "/SYMSEG:PSECT:CODE" ;Put symbol table and patch area in CODE
VWHO==0 ;Who last edited (0=developers)
VMAJOR==5 ;Same as release of TOPS-20
VMINOR==3
VQDML==^D14 ;QDMAIL's version number
; *******************************************************************
; * *
; * QDMAIL is a program to scan the connected directory for various *
; * queued mail files and to print out the file type and *
; * destination host. It is adapted from MMAILR. *
; * *
; *******************************************************************
; Routines invoked externally
EXTERN $GTLCL
SUBTTL Conditional Assembly
IFNDEF FTOMLR,<FTOMLR==1> ; Non-zero to process old queue files
IFNDEF DATORG,<DATORG==1000> ;Data on page 1
IFNDEF CODORG,<CODORG==10000> ;Code on page 10
IFNDEF PAGORG,<PAGORG==30000> ;Paged data on page 30
IFNDEF FREORG,<FREORG==40000> ;Free storage starts at page 40
SUBTTL Definitions
F==0
A=1
B=2
C=3
D=4
E=5
T=6
TT=7
M=10
N=11
O=12
X=14
Y=15
Z=16
P=17
; Character definitions
.CHDEL==177 ;Delete
EOL=.CHCUN ;End of line for PRINT UUO
; Local UUO's
OPDEF PRINT [1B8]
OPDEF UTYPE [2B8]
OPDEF UETYPE [3B8]
OPDEF UERR [4B8]
; The following print macros do output only if PRINTP is set
DEFINE TYPE (X)
< UTYPE [ASCIZ /X/] ; Just type string
>
DEFINE CTYPE (X)
< UTYPE 10,[ASCIZ /X/] ; Do crlf and type string
>
DEFINE CITYPE (X)
< UTYPE 1,[ASCIZ /X/] ; Conditional crlf and type string
>
DEFINE ETYPE (X)
< UETYPE [ASCIZ /X/] ; Type string (fmt codes)
>
DEFINE CETYPE (X)
< UETYPE 10,[ASCIZ /X/] ; Do crlf and type string (fmt codes)
>
DEFINE CIETYP (X)
< UETYPE 1,[ASCIZ /X/] ; Conditional crlf and type str (fmt codes)
>
DEFINE DEFERR (X,Y)
< DEFINE X (Z)
< IFIDN <Z>,<>,<UERR Y,0>
IFDIF <Z>,<>,<UERR Y,[ASCIZ /Z/]>
>
>
DEFERR WARN,3
DEFERR JWARN,7
DEFERR FATAL,12
DEFERR JFATAL,16
SUBTTL Impure storage
LOC 41
JSR UUOH
.PSECT PAGDAT,PAGORG ;Declare PAGDAT PSECT
.ENDPS
.PSECT FRESTG,FREORG ;Declare FRESTG PSECT
FSPAG==FREORG/1000
.ENDPS
.PSECT DATA,DATORG ;Enter data area
CORBEG==. ;Start of core initialized at startup
PRINTP: BLOCK 1 ;If messages should print out
NPDL==177 ;Size of stack
PDL: BLOCK NPDL ;Pushdown list
MPP: BLOCK 1 ;Saved stack ptr for SAVACS/RSTACS
SAVEP: BLOCK 1 ;Place to save stack ptr in local rtns
PGTBLL==<1000-FSPAG+^D35>/^D36
PAGTBL: BLOCK PGTBLL ;Bit table
FREPTR: BLOCK 1 ;Tail,,head for free block list
PLINBP: BLOCK 2 ;Start of line in parser
PWSPBP: BLOCK 2 ;Byte pointer of start of line after whitespace
PCLNBP: BLOCK 2 ;Where there was a colon
PDELBP: BLOCK 2 ;Where there was a rubout
PDELB2: BLOCK 2 ;Where it ends
;;; Structure of a mail file set up block
DEFINE DFMBLK(SYM)<
SYM==MSGLEN
MSGLEN==MSGLEN+1
>;End DEFINE
MSGLEN==0 ;Initialize length of block
DFMBLK(MSGPAG) ;Starting -# pgs,,starting core page
DFMBLK(MSGJFN) ;File JFN
DFMBLK(MSGWRT) ;Time msg was queued
DFMBLK(MSGRXM) ;Time to attempt network retransmissions
DFMBLK(MSGNTF) ;Time to tell sender of delivery status
DFMBLK(MSGDEQ) ;Time to dequeue the msg -- dead letter
MSGBLK: BLOCK MSGLEN
DIRNUM: BLOCK 1 ;Directory being hacked
FILIDX: BLOCK 1 ;File tbl index for queued file type
IFN FTOMLR,<
OMLRBF: BLOCK 20 ;Buffer for address strings (old MAILER)
>;IFN FTOMLR
INUUO: BLOCK 1 ;Safety check to prevent recursive UUO's
TEMPAC: BLOCK 1 ;Temp ac storage
NUPDL==20 ;Size of UUO PDL
UUOPDL: BLOCK NUPDL ;Pushdown list for processing UUO's
UUOACS: BLOCK 20 ;ACs saved over UUO
INTPC: BLOCK 1 ;Interrupt PC
INTACS: BLOCK 4 ;ACs saved over interrupt
LHOST: BLOCK 1 ;Address of site entry for local host
NCKNMF: BLOCK 1 ;Non-zero if host name was a nickname
HSTBFL==30
HSTBUF: BLOCK HSTBFL ;Put string of a host here
STRBUF: BLOCK 1000 ;String buffer, used globally
STRBF1: BLOCK 1000 ;Alternative string buffer, used locally
COREND==.-1 ;End of core initialized at startup
DEBUG: 0 ;If debugging
;; Routine to save AC's
SAVACS: 0 ;JSR here to save all ACs on stack
JRST [ PUSH P,MPP
ADJSP P,17
MOVEM P,MPP
MOVEM 16,(P)
MOVEI 16,-16(P)
BLT 16,-1(P)
JRST @SAVACS]
;; Routine to restore AC's
RSTACS: 0 ;JSR here to restore ACs
JRST [ MOVSI 16,-16(P)
BLT 16,16
ADJSP P,-17
POP P,MPP
JRST @RSTACS]
.ENDPS
SUBTTL Pure storage
.PSECT CODE,CODORG
BITS:
...BIT==0
REPEAT <^D36>,<
1B<...BIT>
...BIT==...BIT+1
>;REPEAT <^D36>
; Following are definitions and a table of file names/processing
; functions to handle delivery of various queued mail formats:
DEFINE FILXX(GSTR,PSTR,PRCHDR,PRCTXT,FLGS)<
FL%STR==0
[ASCIZ `GSTR`],,[ASCIZ `PSTR`] ; File group name string and
; printing descriptor
FL%PRC==1
PRCHDR,,PRCTXT ; Setup routines for processing
; header/text
FL%FLG==2
FLGS
FL%LEN==3
>;DEFINE FILXX
; Control flags for processing names
FF%OML==1B0 ;Old style queue file (adr in extension)
FILTBL:
FILXX(<[--QUEUED-MAIL--].NEW*>,<Queued Mail [New]:>,GQUEKY,GQUEH1,0)
FILXX(<[--QUEUED-MAIL--].NETWORK>,<Queued Mail [Network]:>,GQUEKY,GQUEH1,0)
FILXX(<[--QUEUED-MAIL--].RETRANSMIT>,<Queued Mail [Retransmit]:>,GQUEKY,GQUEH1,0)
FILXX(<[--RETURNED-MAIL--].>,<Nondelivery Reply:>,GQUEKY,GQUEH1,0)
FILXX(<[--RETURNED-MAIL--].NETWORK>,<Nondelivery Reply [Network]:>,GQUEKY,GQUEH1,0)
FILXX(<[--RETURNED-MAIL--].RETRANSMIT>,<Nondelivery Reply [Retransmit]:>,GQUEKY,GQUEH1,0)
FILXX(<[--BAD-QUEUED-MAIL--].>,<Bad Mail:>,GQUEKY,GQUEH1,0)
FILXX(<[--BAD-QUEUED-MAIL--].RETRANSMIT>,<Bad Mail [Retransmit]:>,GQUEKY,GQUEH1,0)
FILXX(<[--BAD-RETURNED-MAIL--].>,<Bad Nondelivery Reply:>,GQUEKY,GQUEH1,0)
FILXX(<[--BAD-RETURNED-MAIL--].RETRANSMIT>,<Bad Nondelivery Reply [Retransmit]:>,GQUEKY,GQUEH1,0)
IFN FTOMLR,<
FILXX(<[--UNSENT-MAIL--].*>,<Queued Mail [Old Style]:>,GQUEUN,GQUEH0,FF%OML)
FILXX(<]--UNSENT-NEGATIVE-ACKNOWLEDGEMENT--[.*>,<Nondelivery Reply [Old Style]:>,GQUEUN,GQUEH0,FF%OML)
FILXX(</UNDELIVERABLE-MAIL/.>,<Dead Mail [Old Style]:>,GQUEUN,GQUEH0,FF%OML)
>;IFN FTOMLR
NFTBL==<.-FILTBL>/FL%LEN
LCLNAM: ASCIZ/TOPS-20/ ;Gets clobbered at initialization time
BLOCK LCLNAM+20-.
LCLNME==. ;End of local name (for padding purposes)
SUBTTL Main program
; Definition of program entry vector
ENTVEC: JRST GO ; Normal entry
JRST GO ; REENTER
BYTE(3)VWHO(9)VMAJOR(6)VMINOR(18)VQDML ;QDMAIL version
ENTVCL==.-ENTVEC
GO: RESET
MOVE F,[A,,B] ;Clear out ACs (paranoia)
SETZ A,
BLT A,P
MOVE P,[IOWD NPDL,PDL] ;Set up stack
SETZB F,CORBEG ;Clear out impure storage
MOVE A,[CORBEG,,CORBEG+1]
BLT A,COREND
SETOM INUUO ;Init recursive UUO flag
MOVEI A,.FHSLF
RPCAP ;Get our capabilities
IOR C,B ;Enable everything we've got
EPCAP
HRROI A,LCLNAM ;Try to get local host name
CALL $GTLCL
WARN <No local hostname information>
GJINF
PUSH P,B ;Save connected directory
MOVE B,A ;Login user number
SETZ A, ;No flags
RCDIR ;Convert user number to directory number in C
SETOM PRINTP ;Print all messages
POP P,B ;Get back connected directory
CAMN B,C ;Login same as connected?
JRST DOLOGN ;Yes, just do it and stop
PUSH P,C
CALL DODIR ;Do connected first
TYPE <
>
POP P,B
DOLOGN: CALL DODIR ;Do login
CALL CLRPTB ;Unmap all remaining pages
MOVEI A,.FHSLF ;Clear all files
CLZFF
HALTF
JRST GO ;Restart totally if continue
; Here to scan files in a directory
DODIR: CIETYP <Trying %2U...>
MOVEM B,DIRNUM ;Save directory number
MOVE A,[-NFTBL,,FILTBL] ;Init file type index
TFLUP: MOVEM A,FILIDX
HRROI A,STRBUF ;Set up name string for file sought
MOVE B,DIRNUM
DIRST
JRST FILUPX ;No go, try next file type
MOVE B,FILIDX ;b =: ptr to current file type string
HLRZ B,FL%STR(B)
CALL MOVST0
MOVE A,[GJ%IFG!GJ%OLD!GJ%SHT+<0,,-3>]
HRROI B,STRBUF
GTJFN
JRST FILUPX ;No go, try next file type
MOVE X,A ;Save JFN
CALL CRIF
MOVE A,FILIDX ;a =: ptr to descriptor string
HRRZ A,FL%STR(A)
CETYPE <%1W>
FILUP: MOVEI A,(X) ;Print the file name
CALL DQFIL ;Print the file information
CIETYP < %1J ...can't map file> ;+1, can't map file
NOP ;+2, error processing file
MOVE A,X ;Step to next file in this group
GNJFN
JRST FILUPX ;No more, try next type
JRST FILUP
;; Here to step to the next file type
FILUPX: MOVE A,FILIDX ;No, a =: current file type index
ADDI A,FL%LEN-1 ;Step to next one
AOBJN A,TFLUP
TYPE <
> ;Don't do doubled CRLF
RET
SUBTTL Queued Mail File Handling
;;; Scan a queued mail file and print out relevant information
;;; about its queue status and destination.
; Entry: a = wild card jfn for file
; Call: CALL DQFIL
; Return: +1, error mapping file
; +2, error processing file
; +3, success
DQFIL: JSR SAVACS ;Save all ACs
MOVEI B,(A) ;Make copy of the name
HRROI A,STRBUF
SETZ C,
JFNS
HRROI B,STRBUF ;Must get another JFN
CALL MAPQFL
JRST RSTRET ;Failed, return
MOVEI M,MSGBLK ;m := pointer to msg block
MOVEM A,MSGJFN(M) ;Save JFN
MOVEM D,MSGPAG(M) ;Save starting copy
HLRZS D ;d := # pgs in file
CIETYP < %1J %4D pg%4P>
CALL PARINI ;Initialize parser (ptr to msg text)
SETZM MSGRXM(M) ;Clear default retransmission time
SETZM MSGNTF(M) ;Clear delivery status notification time
SETZM MSGDEQ(M) ;Clear default dequeue time for msg
HRRZ A,MSGJFN(M) ;Get file write date
CALL .GFWDT
MOVEM B,MSGWRT(M)
MOVE A,MPP ;Return at least +2 from here
AOS -20(A)
MOVE A,FILIDX ;a := current file type index
HLRZ A,FL%PRC(A) ;a := processing dispatch for header
JRST 0(A) ;Do it
IFN FTOMLR,<
;; Here to fake a header for xxx.<addressee> files
GQUEUN: PUSH P,X ;Save the current msg string info
PUSH P,Y
HRROI A,STRBUF ;a := buffer for the extension info
HRRZ B,MSGJFN(M) ;b := msg file JFN
MOVSI C,000100 ;Print extension only
JFNS
MOVE A,[POINT 7,STRBUF] ;Now scan the string for the host name
MOVE B,A
SETZB X,Y ;Init host ptr and string length
GQUEN0: ILDB C,B ;c := next char
JUMPE C,GQUEN1 ;Quit on null
CAIN C,.CHCNV ;^V?
JRST GQUEN0 ;Yes, ignore it
CAIN C,"@" ;Start of host?
JRST [ SETZ C, ;Yes, clobber the "@" with a null
IDPB C,A
MOVE X,A ;Save start of host string
JRST GQUEN0 ]
IDPB C,A ;Store the char
AOJA Y,GQUEN0 ;Count the char and do the next
; Here we have the end of the addressee string
GQUEN1: SKIPE X ;"@" seen?
CAMN A,X ;Yes, host null?
JRST [MOVE B,[POINT 7,LCLNAM] ;No, use local name
MOVE X,A ;Update host ptr (in case no "@")
JRST GQUEN0 ]
MOVE B,A ;OK, terminate edited string
IDPB C,B
; Now we create a fake header (as if [--QUEUED-MAIL--])
MOVE A,[POINT 7,OMLRBF] ;a := place to build it
MOVEI B,.CHFFD ;Start with ^L<host><crlf>
IDPB B,A
MOVE B,X ;b := ptr to host string
SETZ C,
SOUT ;(Have to SOUT - not word boundary)
MOVEI B,CRLF0
CALL MOVSTR
MOVEI B,STRBUF ;Add <addressee><crlf>
CALL MOVSTR
MOVEI B,CRLF0
CALL MOVSTR
MOVEI B,.CHFFD ;And finish with ^L<CRLF>
IDPB B,A
MOVEI B,CRLF0
CALL MOVST0
MOVE X,[POINT 7,OMLRBF] ;Now set to scan the string
ADDI Y,^D8+1 ;Account ^L's and <crlf>'s in length
;(and 1 so PARLIN thinks a msg follows)
JRST GQUEKY ;Make like it's [--QUEUED-MAIL--]
>;IFN FTOMLR
;; Parse the head of the file
GQUEKY: CALL PARLIN ;Get a line from the file
JRST QUEEOF ;Premature eof
TRNN F,FP%FF ;Was a formfeed seen?
JRST [ CETYPE < ?Invalid queued mail file format in line ">
JRST QUEBK0] ;Toss the losing file out
;; Now parse the message recipients
GQUERC: TRNE F,FP%EOL ;Empty line?
JRST [ TRNE F,FP%EQU ;Control parameter specification?
JRST QUEBPM ;Yes, must be error
TRNN F,FP%BKA ;Sender specification?
JRST GQUEHD ;No, must be start of actual message
MOVE A,[POINT 7,HSTBUF] ;Yes, substitute local name
MOVEI B,LCLNAM
CALL MOVST0
JRST GQURC0] ;Process it
TRNE F,FP%EQU ;Control parameter specification?
JRST [ MOVEI A,QUEPTB ;Yes, lookup in parameter keyword table
CALL PARKEY
JRST QUEBPM ;Bad luck...
JRST GQURC1] ;Got it, continue processing
CALL PARSTR ;Get pointers for this line
MOVE B,[POINT 7,HSTBUF]
DO.
ILDB A,C ;Make uppercase
IDPB A,B
CAIE A,.CHNUL ;Quit on null
SOJG D,TOP. ;Or count
ENDDO.
SETZ A, ;Fill out with nulls
DO.
IDPB A,B
TLNE B,760000
LOOP.
ENDDO.
GQURC0: MOVEI A,HSTBUF ;Now print it appropriately
TRNE F,FP%BKA ;Sender spec?
JRST [ CETYPE < From: %1W>
JRST GQURC1]
CETYPE < To: %1W>
;; Here to process the next input line...
GQURC1: CALL PARLIN ;Get a line
JRST QUEEOF ;Premature eof
TRNE F,FP%FF ;Started with form?
JRST GQUERC ;Yes, next host then
JRST GQURC1 ;Ignore it and try another line
;; Now finish up, remembering where the headers start
GQUEHD: MOVE A,FILIDX ;a := index to current file type
HRRZ A,FL%PRC(A) ;a := processing dispatch for msg
JRST 0(A) ;Do it
IFN FTOMLR,<
GQUEH0: POP P,Y ;Recover ptr info for msg text itself
POP P,X
>;IFN FTOMLR
GQUEH1: MOVE A,MSGWRT(M) ;Print date/time msg queued
CETYPE < Queued: %1T>
CALL RELQUE ;Release the file
JRST RSTSKP ;Skip return from it all
;;; Table of parameter keywords and processing routines
QUEPTB: -NQPRMS,,.+1
[ASCIZ/AFTER/],,QUEAFT ;Formerly RETRANSMIT
; [ASCIZ/DATA/],,QUEDAT
[ASCIZ/DELIVERY-OPTIONS/],,QUEDEL
[ASCIZ/DEQUEUE/],,QUEDEQ
[ASCIZ/DISCARD-ON-ERROR/],,QUEDER
[ASCIZ/ERROR/],,QUEERR
[ASCIZ/NET-MAIL-FROM-HOST/],,QUEHST
[ASCIZ/NOTIFY/],,QUENTF
[ASCIZ/RETURN-PATH/],,QUERPT
NQPRMS=.-QUEPTB-1
;;; Here to process (no-op) "NET-MAIL-FROM-HOST" line
QUEHST: RETSKP
;;; Here to fetch time to attempt network retransmissions
QUEAFT: CALL GQUTIM ;Decode the time value
RET ;No go
MOVEM B,MSGRXM(M) ;Save it
RETSKP ;And success return
;;; Here to fetch time to notify sender of transmission status
QUENTF: CALL GQUTIM ;Decode the time value
RET ;No go
MOVEM B,MSGNTF(M) ;Save it
RETSKP ;And success return
;;; Here to fetch time to notify sender of transmission status
QUEDEQ: CALL GQUTIM ;Decode the time value
RET ;No go
MOVEM B,MSGDEQ(M) ;Save it
RETSKP ;And success return
;;; Here to fetch return path
QUERPT: RETSKP
;;; Here to fetch return delivery options
QUEDEL: RETSKP
;;; Here to set flag for discarding msg without notifying sender if
;;; failed or dequeued (no-op)
QUEDER: RETSKP ;Success return
;;; Here to fetch error log file name
QUEERR: RETSKP
;;; Routine to decode a time value for a control parameter
;;; Return: +1, error
;;; +2, success - value in b
GQUTIM: DMOVE C,PCLNBP ;Rest of line after colon
CALL PARST1
MOVE A,[POINT 7,STRBF1] ;Temp buffer for time string
GQUTI0: ILDB B,C
CAIE B," " ;Skip starting spaces and tabs
CAIN B,.CHTAB
JRST [SOJG D,GQUTI0 ;Look some more
RET] ;Unless string exhausted
SKIPA
GQUTI1: ILDB B,C ;Next char
IDPB B,A ;Copy it
CAIN B,.CHNUL ;Quit on null
JRST GQUTI2
SOJG D,GQUTI1 ;If not end of string, continue
MOVEI B,0 ;Else end with null
IDPB B,A
GQUTI2: HRROI A,STRBF1 ;Now convert the time string
IDTIM
RET
RETSKP
;; Premature EOF
QUEEOF: CETYPE < ?Premature end of file>
QUEBRT: CALL RELQUE ;Free entry
JRST RSTRET ;Single return
;; Bad keyword
QUEBKY: CETYPE < ?Unrecognized keyword in line ">
QUEBK0: MOVEI A,101 ;Primary output
CALL PARSTR ;Set up line ptr/length
MOVE B,C
MOVN C,D
SOUT
SETZ C,
QUEBK1: HRROI B,[ASCIZ /"
/]
SOUT
JRST QUEBRT
;; Bad control parameter specification
QUEBPM: CETYPE < ?Bad control parameter in line ">
JRST QUEBK0
;;; Release storage from queue entry in M
RELQUE: HLRZ A,MSGPAG(M) ;a := # pages mapped
JUMPE A,RELQUR ;Quit if none touched
HRRZ B,MSGPAG(M) ;b := starting page
CALL PAGDAL ;Unmap the msg file pages
RELQUR: HRRZ A,MSGJFN(M) ;Close the file
CLOSF
JFATAL <RELQUE: >
RET
;;; Map in a file
; Entry: b = ptr to name
; Call: CALL MAPQFL
; Return: +1, error
; +2, success
; a = fresh file jfn
; b = starting core address
; c = # of bytes
; d = # pages,,starting core page
MAPQFL: PUSH P,[OF%RD!OF%PDT] ;Open read and leave access dates
MOVSI A,(GJ%OLD!GJ%SHT)
GTJFN
IFJER.
POP P,B
RET
ENDIF.
PUSH P,A ;Save the jfn
MOVE B,-1(P) ;Get OPENF flags and open the file
OPENF
JRST MPFLOE ;No go
SIZEF ;Fetch its size information
JRST MPFLSE ;No go
PUSH P,B ;Save number of bytes
MOVEI A,(C) ;Number of pages needed for whole file
CALL PAGALC ;Allocate them
JRST MPFLPE ;No go???
HRLZ A,-1(P) ;Start with page 0 of file
HRLI B,.FHSLF
HRLI C,(PM%CNT!PM%RD!PM%CPY)
PMAP
ERJMP MPFLPE ;???
MAPFI1: HRLI C,(B)
MOVS D,C ;d := # pgs,,starting pg
LSH B,9 ;b := core address of first page
POP P,C ;c := # of bytes
POP P,-1(P) ;Move the jfn down on the stack
POPA1J: POP P,A
RETSKP
;; Here on error preparing file map
MPFLPF: ADJSP P,-1 ;Clear page count from stack
MPFLPE: ADJSP P,-1 ;Clear byte count from stack
MPFLSE: POP P,A ;Close the file
CLOSF
NOP
MPFLSR: ADJSP P,-1 ;Clear OPENF bits
RET
;; Here if OPENF fails for file
MPFLOE: POP P,A ;Release the jfn
RLJFN
NOP
JRST MPFLSR ;Fail return
SUBTTL Parser
;;; Parser flags
FP%FF==1 ;Formfeed seen at start of line
FP%CLN==2 ;Colon seen
FP%EOL==4 ;Blank line (after any formfeed, that is)
FP%DEL==10 ;Rubout on line
FP%EQU==20 ;Equal sign seen (control parameter)
FP%BKA==40 ;Backarrow seen (sender spec)
FP%WSP==100 ;Whitespace at start
;;; Initialize parser, called with starting address in B, byte count in C
PARINI: HRLI B,(<POINT 7,0>)
DMOVE X,B
RET
;;; Parse a single line
PARLIN: TRZ F,FP%FF!FP%CLN!FP%EOL!FP%DEL!FP%WSP
SETZM PDELB2 ;Filter for malformed <del> pairs
PARLN0: DMOVEM X,PLINBP ;Save start of line
PARLN1: DMOVEM X,PWSPBP
SOJL Y,CPOPJ
ILDB D,X ;Get first character
CAIN D,.CHFFD ;Formfeed?
JRST [ TRO F,FP%FF
TRZ F,FP%BKA!FP%EQU ;Clear special flags
JRST PARLN0]
CAIN D,"=" ;Equal sign?
JRST [ TRO F,FP%EQU ;Yes
JRST PARLN0 ]
CAIN D,"_" ;Backarrow?
JRST [ TRO F,FP%BKA ;Yes
JRST PARLN0 ]
CAIE D,.CHTAB
CAIN D,.CHSPC
JRST [TRO F,FP%WSP
JRST PARLN1]
CAIE D,.CHCRT ;End of line?
JRST PARLN3 ;No, normal character
TRO F,FP%EOL
JRST PARLN4
PARLN2: SOJL Y,CPOPJ
ILDB D,X
CAIN D,.CHCRT
JRST PARLN4
PARLN3: CAIN D,.CHDEL
JRST PARLN5
CAIN D,":"
TROE F,FP%CLN
JRST PARLN2
DMOVEM X,PCLNBP ;Save pointers when got to colon
JRST PARLN2
PARLN4: SOJL Y,CPOPJ
ILDB D,X ;Skip lf too
SKIPG PDELB2 ;Matching <del> set?
TRZ F,FP%DEL ;No, ignore any seen
RETSKP
PARLN5: TROE F,FP%DEL ;Rubout within line is start of host
JRST [ SKIPE PDELB2 ;Matching pair?
JRST [ SETOM PDELB2 ;No, flag error
JRST PARLN2]
DMOVEM X,PDELB2
JRST PARLN2]
DMOVEM X,PDELBP
JRST PARLN2
PARLNE=. ;Bound for interrupt handling
;;; Parse a keyword from table in A
;;; Returns +1 failure, else calls routine pointed to by table
PARKEY: TRNE F,FP%CLN ;Line had a colon in it?
JRST [ MOVE D,PCLNBP ;Yes, use byte pointer of colon then
JRST PARKY1]
SETO D,
ADJBP D,X
PARKY1: LDB TT,D ;Get character that terminates atom
SETZ T,
DPB T,D ;Replace it with null
MOVE T,0(A) ;t := aobjn ptr to lookup table
PARKY2: HLRZ A,0(T) ;a := ptr to next table entry
HRLI A,(<POINT 7,0>)
MOVE B,PLINBP ;Start of line
CALL STRCMP ;Match?
AOBJN T,PARKY2 ;No, try the next
DPB TT,D ;Replace character
JUMPGE T,CPOPJ ;If no match, return
HRRZ A,(T) ;Get entry
JRST (A) ;Go call that routine
;;; get pointers for this line
PARSTR: DMOVE C,PLINBP
PARST1: SUB D,Y
SUBI D,2 ;Number of chars less CRLF
RET
RSTSKP: MOVE P,MPP ;Be sure stack is reset
AOSA -20(P) ;Skip return
RSTRET: MOVE P,MPP ;Be sure stack is reset
JSR RSTACS
RET
CPOP2J: AOS (P)
CPOP1J: AOS (P)
CPOPJ: RET
SUBTTL Core Allocation
;;; Bit table hacking, page number in A for all
PAGSBT: PUSH P,[IORM B,(A)] ;Set bit
JRST PAGHBT
PAGCBT: PUSH P,[ANDCAM B,(A)] ;Clear bit
JRST PAGHBT
PAGTBT: PUSH P,[TDNE B,(A)] ;Skip if bit clear
PAGHBT: PUSH P,A
PUSH P,B
SUBI A,FSPAG ;Make relative to start of bit table
IDIVI A,^D36
MOVEI A,PAGTBL(A) ;Point to right word
MOVE B,BITS(B) ;Get right bit
XCT -2(P)
SKIPA
AOS -3(P)
POP P,B
POP P,A
ADJSP P,-1
RET
;;; Allocate number of pages in A, returns +1 failure, +2 page number in B
PAGAL1: MOVEI A,1 ;Allocate one page
PAGALC: PUSH P,C
PUSH P,A ;Save number of pages we need
MOVEI B,FSPAG ;Starting free page
PAGALB: CALL PAGFFP ;Fast search for first free page
JRST POPACJ ;Failure, just return
MOVEI A,1(B)
MOVE C,(P) ;Get number of pages to hack again
PAGALL: SOJLE C,PAGALW ;Got enough, return address from b
CAIL A,1000 ;Page number too big?
JRST POPACJ ;Yes, fail
CALL PAGTBT ;Is this bit set?
JRST [ MOVEI B,1(A) ;Try for next free page
JRST PAGALB]
AOJA A,PAGALL ;Try for next match
PAGALW: MOVE C,(P)
MOVEI A,(B)
PAGAW1: CALL PAGSBT ;Allocate one page
SOJLE C,POPAC1
AOJA A,PAGAW1
POPAC1: AOS -2(P) ;Winning return
POPACJ: POP P,A
POP P,C
RET
;;; Deallocate pages, number in A, starting page in B
PAGDA1: MOVEI A,1 ;Deallocate one page
PAGDAL: PUSH P,A
PUSH P,B
PUSH P,C
EXCH A,B ;Setup for page number in A
PAGDA2: SOJL B,PAGDA3
CALL PAGCBT ;Clear one bit
AOJA A,PAGDA2
PAGDA3: SETO A,
MOVE B,-1(P) ;Starting page
HRLI B,.FHSLF
MOVE C,-2(P) ;Count
HRLI C,(PM%CNT)
PMAP ;Flush those pages
POPCBA: POP P,C
POPBAJ: POP P,B
CPOPAJ: POP P,A
RET
;;; Fast search for the first free bit, starting page in B
;;; Returns +1 failure, +2 with page number in B
PAGFFP: SUBI B,FSPAG ;Make relative to start of bit table
IDIVI B,^D36
SETCM A,PAGTBL(B) ;Get first word to check
LSH A,(C)
MOVNI C,(C)
LSH A,(C) ;Clear out random bits to left
SKIPA C,B ;Starting word index
PAGFF1: SETCM A,PAGTBL(C) ;Get word to check
JFFO A,PAGFF2 ;Got any ones?
CAIL C,PGTBLL ;No - beyond last word?
RET ;Failed
AOJA C,PAGFF1 ;No, search for next word
PAGFF2: IMULI C,^D36 ;Number of bits passed
ADDI B,FSPAG(C) ;Final winning page number
CAIL B,1000 ;Was page valid?
RET ;No
RETSKP
; Routine to unmap core buffer pages currently in use
; Entry: pagtbl = bitmap for pages in use
; Call: CALL CLRPTB
; Return: +1
CLRPTB: SETO A, ;Unmap special prebuffer pages
MOVSI B,.FHSLF
SETZ C,
MOVSI T,-PGTBLL ;t =: aobjn ptr to PAGTBL
CLRPT0: SKIPE A,PAGTBL(T) ;Any bits in this entry?
JFFO A,CLRPT1 ;Yes, scan for 1st one
AOBJN T,CLRPT0 ;No more, try next word
RET ;Done
; Here to unmap a page flagged in PAGTBL
; Entry: t = ptr to PAGTBL word for page
; b = count of flag bit position for page
CLRPT1: MOVEI C,0(T) ;c =: PAGTBL word index
IMULI C,^D36 ;c =: page count for prior wds in table
ADDI B,FSPAG(C) ;b =: core page number
CAIL B,1000 ;Legal page?
FATAL <CLRPTB: Invalid page table bit set>
CALL PAGDA1 ;Deallocate this page
JRST CLRPT0 ;Look for more to do
SUBTTL UUO Handler
; UUO enters here via JSR UUOH
UUOH: 0 ;Ret adr for JSR entry
AOSE INUUO ;Recursive call?
JRST [ MOVEM A,TEMPAC ;Yes???
HRROI A,[ASCIZ/Recursive UUO call illegal!/]
PSOUT
MOVE A,TEMPAC
JRST %FATAL]
MOVEM A,UUOACS+A ;Save an ac
MOVEM P,UUOACS+P ;And the stack
MOVE P,[IOWD NUPDL,UUOPDL] ;Set up local stack
PUSH P,UUOH ;Save the calling pc
PUSH P,[UUORTP] ;Put stack restore entry on
LDB A,[POINT 9,40,8] ;a := opcode field
JRST @UUOS(A) ;Dispatch to handler routine
; Here to save whole ac block and set up for RET to restore acs and
; return. Entered by JSR UUOSV
UUOSV: 0
MOVE A,UUOACS+A ;Restore entry a
MOVEM 16,UUOACS+16 ;Save all ACs (P done on entry)
MOVEI 16,UUOACS
BLT 16,UUOACS+15
PUSH P,[UUORT] ;Put ac restore entry on stack
JRST @UUOSV
; Here to restore ac block and return +1 to user.
UUORT: MOVSI 16,UUOACS ;Restore ACs
BLT 16,16
RET
; Here to restore single ac and return +1 to user.
UUOFRT: MOVE A,UUOACS+A ;Recover ac
RET
; Here to restore return adr and caller's stack ptr
UUORTP: POP P,UUOH ;UUOH := return adr
MOVE P,UUOACS+P ;p := caller's stack
SOS INUUO ;Reset the entry flag
JRST @UUOH
; UUO handler dispatch table
UUOS: 0
%PRINT
%TYPE
%ETYPE
%ERROR
;; Print a character
%PRINT: HRRZ A,40 ;Get byte
CAIN A,EOL ;PRINT EOL means do CRLF
JRST [ CALL CRLF ;Do it
JRST UUOFRT ]
PBOUT
JRST UUOFRT ;Take fast return
;; Type a string after crlf if needed
%TYPE: SKIPN PRINTP
JRST UUOFRT
CALL TYCRIF ;Check if we should do a CRLF
%TYPE0: HRRO A,40 ;Get string
PSOUT
JRST UUOFRT
;; Do a conditional crlf
TYCRIF: MOVE A,40 ;Get instruction
TLNE A,(<10,0>) ;Wants CRLF all the time?
JRST CRLF ;Yes
TLNE A,(<1,0>) ;Wants fresh line?
JRST CRIF ;Yes
RET
;; Do crlf if not at start of line currently
CRIF: PUSH P,A
PUSH P,B
CALL CRIF1 ;Do it
JRST POPBAJ
CRIF1: MOVEI A,.PRIOU
RFPOS
TRNE B,-1 ;If not at start of line,
CALL CRLF1 ;Type CRLF
RET
;; Do crlf unconditionally
CRLF: PUSH P,A
CALL CRLF1
JRST CPOPAJ
CRLF1: HRROI A,CRLF0
PSOUT
RET
CRLF0: ASCIZ/
/
;; Print error messages
%ERROR: JSR UUOSV ;Save the ac context
CALL CRIF ;Get a fresh line
MOVE B,40 ;Get instruction
TLNE B,(<10,0>) ;Wants %?
SKIPA A,["?"] ;No
MOVEI A,"%"
PBOUT
%ERR1: TRNN B,-1 ;Any message to print?
JRST %ERR2 ;No
CALL %ETYE0 ;Yes, print it out
MOVE B,40 ;And recover instruction
%ERR2: TLNN B,(<4, 0>) ;Wants JSYS error message?
JRST %ERR3
HRROI A,[ASCIZ / - /]
TRNE B,-1 ;If a previous message, type delimiter
PSOUT
MOVEI A,.PRIOU
HRLOI B,.FHSLF ;This fork
SETZ C,
ERSTR
NOP
NOP
%ERR3: CALL CRLF
LDB A,[POINT 2,40,12] ;Get low order bits of ac field
JRST %ERRS(A)
%ERRS: JRST %FATAL ;0 - not used
%ERRET: JRST %FATAL ;1 - not used
JRST %FATAL ;2 - return to EXEC
RET ;3 - return to user
;; Here on fatal error
%FATAL: HALTF
HRROI A,[ASCIZ /?Can't continue
/]
PSOUT
JRST %FATAL
;; Here to print a string, filling in escape sequences
%ETYPE: JSR UUOSV ;Save the ac context
SKIPN PRINTP
RET
CALL TYCRIF ;Type a CRLF maybe
%ETYE0: HRRZ N,40
%ETYS0: HRLI N,(<POINT 7,0>) ;Get byte pointer to string
%ETYP1: ILDB A,N ;Get char
JUMPE A,CPOPJ ;Done
CAIE A,"%" ;Escape code?
JRST %ETYP0 ;No, just print it out
SETZ O, ;Reset AC
%ETYP2: ILDB A,N
CAIL A,"0" ;Is it part of addr spec?
CAILE A,"7"
JRST %ETYP3 ;No
IMULI O,^D8 ;Yes, increment address
ADDI O,-"0"(A)
JRST %ETYP2
%ETYP3: CAIG A,"Z"
CAIGE A,"A"
JRST %ETYP0
CALL @%ETYTB-"A"(A) ;Do dep't thing
JRST %ETYP1
%ETYP0: PBOUT
JRST %ETYP1
%ETYTB: %ETYPA ;A - print time
%ETYPB ;B - print date
%ETYP0 ;C
%ETYPD ;D - print decimal
%ETYER ;E - error code
%ETYPF ;F - floating
%ETYP0 ;G
%ETYPH ;H - RH as octal
%ETYP0 ;I
%ETYPJ ;J - filename
REPEAT 4,<%ETYP0> ;K, L, M, N
%ETYPO ;O - octal
%ETYPP ;P - pluralizer
REPEAT 2,<%ETYP0> ;Q, R
%ETYPS ;S - string with escape sequences
%ETYPT ;T - date and time
%ETYPU ;U - user name
%ETYP0 ;V
%ETYPW ;W - string with no escapes
REPEAT 3,<%ETYP0> ;X, Y, Z
;; Print time only
%ETYPA: MOVSI C,(OT%NDA) ;No day, just time
JRST %ETYB0
;; Options for printing just day or date/time
%ETYPT: TDZA C,C ;Both date and time
%ETYPB: MOVSI C,(OT%NTM) ;No time, just day
%ETYB0: JUMPE O,.+2 ;If AC field spec'd
SKIPA B,UUOACS(O) ;Use it
SETO B, ;Else use now
MOVEI A,.PRIOU
ODTIM
RET
;; Print decimal and octal numbers
%ETYPD: SKIPA C,[^D10] ;Decimal
%ETYPO: MOVEI C,^D8 ;Octal
MOVE B,UUOACS(O) ;Get data
%ETYO0: MOVEI A,.PRIOU
NOUT
NOP
RET
;; Print string for specified error code
%ETYER: MOVEI A,.PRIOU
MOVSI B,.FHSLF ;This fork
HRR B,UUOACS(O) ;Get error code
SETZ C,
ERSTR
NOP
NOP
RET
;; Print floating point number
%ETYPF: MOVEI A,.PRIOU
MOVE B,UUOACS(O)
SETZ C,
FLOUT
NOP
RET
;; Print RH of number in octal
%ETYPH: MOVEI C,^D8
HRRZ B,UUOACS(O)
JRST %ETYO0
;; Print file name from jfn
%ETYPJ: MOVEI A,.PRIOU
HRRZ B,UUOACS(O)
MOVE C,[001110,,1]
JFNS
RET
;; Add "S" depending on the value of a number
%ETYPP: MOVEI A,"s"
MOVE B,UUOACS(O)
CAIE B,1
PBOUT ;Make plural unless just one
RET
;; Recursive string output with escape sequence handling
%ETYPS: PUSH P,N
SKIPE N,UUOACS(O)
CALL %ETYS0 ;Recursive call
CPOPNJ: POP P,N
RET
;; Print directory or user name
%ETYPU: MOVEI A,.PRIOU
MOVE B,UUOACS(O)
DIRST
NOP
RET
;; String output without further escape sequence handling
%ETYPW: MOVE A,UUOACS(O)
TLNN A,-1
HRLI A,(<POINT 7,0>)
PSOUT
RET
SUBTTL Utility Routines
; Here to step to the next available directory number
; Entry: a = flags
; b = wild card string (always PS:<*>)
; c = current directory number
; Call: CALL STPDIR
; Return: +1, a = flags, c = new directory number
STPDIR: RCDIR
RET
; Routine to fetch the write date/time of a file
; Entry: a = file JFN
; Call: CALL .GFWDT
; Return: +1, b = file write date/time
.GFWDT: PUSH P,C ;Save an ac
MOVEI B,B ;Answer into b
MOVEI C,1 ;Only the write date/time
RFTAD
POP P,C ;Recover ac
RET
;;;Move a string from B to A
MOVSTR: HRLI B,(<POINT 7,0>)
MOVST1: ILDB D,B
JUMPE D,MOVST3
IDPB D,A
JRST MOVST1
;;;Move string and terminating null
MOVST0: HRLI B,(<POINT 7,0>)
MOVST2: ILDB D,B
IDPB D,A
JUMPN D,MOVST2
MOVST3: RET
; Routine to compare two strings ignoring case differences
; Entry: a,b = ptrs to strings
; Call: CALL STRCMP
; Return: +1, match failed
; +2, strings match
STRCMP: PUSH P,C ; Save some ac's
PUSH P,D
STRCM0: ILDB C,A ; c := next char from a
CAIL C,"a" ; Raise it if necessary
CAILE C,"z"
CAIA
SUBI C,"a"-"A"
ILDB D,B ; d := next char from b
CAIL D,"a" ; Raise it if necessary
CAILE D,"z"
CAIA
SUBI D,"a"-"A"
CAME C,D ; Same?
JRST STRCM1 ; No
JUMPN C,STRCM0 ; If not end of strings, continue
AOS -2(P) ; Match, return +2
STRCM1: POP P,D ; Recover ac's
POP P,C
RET
...LIT: XLIST
LIT
LIST
END <ENTVCL,,ENTVEC> ; Set up entry vector
; Local Modes:
; Mode: MACRO
; Comment Start:;
; Comment Begin:;
; End: