Google
 

Trailing-Edge - PDP-10 Archives - BB-BT99T-BB_1990 - 10,7/acct/actdae.mac
There are 9 other files named actdae.mac in the archive. Click here to see a list.
TITLE	ACTDAE - Accounting Daemon for TOPS10
SUBTTL B.A.HUIZENGA/BAH/Tarl/LWS/DPM/RCB	14-Nov-89

	SEARCH	ACTPRM,QSRMAC,ORNMAC
	MODULE	(ACTDAE)

	.REQUIR	ACTRCD		;USAGE RECORDS

;COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1979,1988,1990.
; ALL RIGHTS RESERVED.
;
;
;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED
;ONLY  IN  ACCORDANCE  WITH  THE  TERMS  OF  SUCH LICENSE AND WITH THE
;INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE OR ANY  OTHER
;COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
;OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF THE  SOFTWARE  IS  HEREBY
;TRANSFERRED.
;
;THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT  NOTICE
;AND  SHOULD  NOT  BE  CONSTRUED  AS A COMMITMENT BY DIGITAL EQUIPMENT
;CORPORATION.
;
;DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY  OF  ITS
;SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.

	AC.VER==2	;VERSION NUMBER
	AC.EDT==162	;EDIT NUMBER
	AC.WHO==0	;WHO EDITED LAST
	AC.MIN==3	;MINOR VERSION NUMBER

	%%.ACV==:<VRSN.	(AC.)>
;AC DEFINITIONS OTHER THAN GLXLIB
DEFADR==15	;CURRENT ADDRESS OF DEFUS DATA ITEM BEING WORKED UPON.
		; NEEDS TO BE PRESERVED OVER DATFIL ROUTINES.
LENGTH==16	;LENGTH OF CURRENT DATA ITEM BEING WORKED UPON.
		; NEEDS TO BE PRESERVED OVER CONVERT ROUTINES.

;CUSTOMER CHANGABLE PARMETERS

IFNDEF	FILPRO,<FILPRO==177>	;USAGE/FAILURE FILE PROTECTION
IFNDEF	FILBSZ,<FILBSZ==^D7>	;USAGE/FAILURE FILE BYTE SIZE FOR NEW FILES
IFNDEF	WTOINT,<WTOINT==^D5>	;INTERVAL TIME (MINUTES) BETWEEN RETRY WTO'S
IFNDEF	RMGINT,<RMGINT==^D120>	;INTERVAL TIME (SECONDS) BETWEEN RMS MESSAGES
IFNDEF	SLPSEC,<SLPSEC==^D15>	;SECONDS TO SLEEP BETWEEN RETRIES
				;SHOULD EVENLY DIVIDE ^D60 OR WTOINT WON'T
				;BE CALCULATED CORRECTLY. MAXIMUM IS ^D60.
IF1,<IFG <SLPSEC-^D60>,<PRINTX ?SLPSEC GREATER THAN ^D60.>>

IFNDEF	CHKINT,<CHKINT==^D10>	;INTERVAL FOR CHECKPOINTING USAGE FILES
				;DONE ON THE FULL MINUTE SO MUST BE
				;EVENLY DIVISIBLE INTO 60. I.E. WITH 15
				;MINUTE INTERVAL, CHECKPOINTING WILL BE
				;DONE AT xx:00, xx:15, xx:30, AND xx:45

COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1979,1990. ALL RIGHTS RESERVED.
\;END COPYRIGHT MACRO


	LOC	137
	EXP	%%.ACV
	RELOC
	SUBTTL	EDIT HISTORY

;1)	Implement Account validation
;2)	Convert to use GLXLIB
;3)	Use extended channels and the new FILOP. for I/O
;4)	Implement Checkpointing for active jobs
;5)	Take action when LOGIN sends LOGIN, SESSION, and ATTACH messages
;6)	When disk space is too low for allocating checkpoint files, limit
;	the number of jobs to be logged in
;7)	Implement Usage file handling code
;10)	Validation routine doesn't do enough checking for whom to validate
;11)	Verify routine didn't handle PPNs less than the first
;	PPN in PROJCT.SYS.  Also, verify couldn't handle more than one
;	block of validation data in PROJCT.SYS
;12)	Make ACTDAE know about ACCT.SYS.  Build a table of first PPNs
;	in every block and search ACCT.SYS for any PPN validation error
;13)	Teach ACTDAE about the 7.01 MOnitor LOGOUT message from [SYSTEM]GOPHER
;14)	Since the 70017 monitor starts up the ACTDAE via FRCLIN, the job
;	must run detached.  Also teach ACTDAE about wild card ppns in ACCT.SYS.
;15)	Write MAKENT routine that generally writes an entry
;	given an entry number and address of DEFUS data.
;16)	Imement the QUEUE. UUO validation function.
;17)	Implement the QUEUE. UUO make an entry function.
;20)	Use PSI for IPCF traffic and begin timer code
;21)	Complete Job checkpoint code and timers for running same.
;22)	Make Session entries for jobs after a reload or ACTDAE restart
;23)	Implement code for closing sessions and the usage file
;24)	Implement device checkpoint files and entries.
;25)	Implement Disk Usage accounting from BACKUP
;26)	ORIONs messages for SET USAGE FILE/BILLING commands changed
;27)	Would you believe ORIONs message changed again
;30)	Wild-card in Account string support missing, add it (req PROJCT V1(2)).
;	Also, could only find first account for wild-card'ed ppns, fix that.
;31)	Space wasted by PHASE/DEPHASE assembly, also bum SKIPT/$RETF pairs.
;32)	$FATAL doesn't type on OPR if detached, define $BOMB that always uses OPR
;33)	LOGIN sends -1 if no Account or Remark specified at LOGIN time
;34)	Physical device name not provided in magtape and DECtape entries
;35)	Bad BLT for text fields for STRUCTURE/MAGTAPE/DECTAPE mount IPCF messages.
;36)	GLXLIB will DETACH if on FRCLIN, remove code from ACTDAE
;37)	Add code and range checks for user defined entries (5000-9999)
;40)	Make system restart a little faster
;41)	Fix problems with finding entries in PROJCT.SYS
;42)	Bad "Make An Entry" messages caused lost free space
;43)	Do case conversion when verifying account if FTCASECONVERT is on.
;	Return account string (may be modified if FTCASECONVERT is on) in IPCF
;	message or in response block of QUEUE. on successful validation.
;44)	Implement /NO-SESSION-ENTRIES on the SET USAGE FILE-CLOSURE command to
;	over-ride the implicit SET USAGE BILLING-CLOSURE that occurs when the
;	file is closed.
;45)	Edit 37 broke Disk Utilization records, fix that.
;46)	Use assembly parameter PRJWPB for PROJCT.SYS instead of 200s
;47)	Implement Default account strings from PROJCT.SYS.  The default is
;	used if asked to verify a null account string and a default exists.
;	The default is returned in the IPCF message or QUEUE. response block.
;50)	Allow PPN entries to cross block boundries in PROJCT.SYS.  This change
;	becomes format version 2.  Implement code such that version 1 PROJCT.SYS
;	still works.  With this change, making PRJWPB larger simply becomes a
;	performance gain.
;51)	Jobs doing QUEUE. UUO to make an entry hang in EW if not privileged or
;	specify an unknown entry type.  Un-stick them.
;52)	Implement QUEUE. UUO subfunctions UGACC$ and UGOUP$
;	for access control requests.
;53)	Complete implementation of Date/Time change
;54)	Assume account string is NOT required if a ppn isn't in ACCT.SYS
;55)	Random fixes from random QARs
;56)	Remove our definition of the job field in the LOGOUT message and
;	use QUASAR's definition from QSRMAC.
;57)	Correct sense of check in ACTLIN so we dont send a bogus error
;	message to the OPR if the PID we send to is gone.
;60)	Check for PROJCT.SYS being created from a zero-length PROJCT.ACT
;	and assume [*,*]=*
;61)	Always call ALLDEV on LOGOUT to insure nnnDEV.BIN gets deleted.
;Start version 2 here, to be shipped with TOPS10 7.03.
;
;This version implements SYS:ACCT.SYS version 5, now ACT:ACCT.ACT (See
;	ACTCIO.MAC). Only ACTDAE should be reading/writing ACCT.ACT.
;	ACTDAE depends on the fact that only it can change the file.
;
;100)	Up the version number to 2. Start massive changes. /Tarl
;
;101)	Add "context limit" and "idle contexts page limit" UGCHG$
;	subfunctions. /LWS
;
;102)	Add new "profile flag" word and last access UDT word. Fix various
;	bugs in ACCTIO from page boundary problems to  miscalculating
;	number of customer defined words and program to run words. /LWS
;
;103)	Add rudimentary "validation failure log" file support. Fix bugs
;	in ACCTIO dealing with page boundaries. /LWS
;
;104)	Add LOCK and UNLOCK user account file functions. /LWS
;
;105)	Rewrite FNDIDX in ACCTIO and add IDXINI. /LWS
;
;106)	Add support for synonym-to-account string translation.  This is
;	useful for sites which frequently change account strings and do
;	not want to inconvience their users by making them change their
;	SWITCH.INIs, control files, or MIC file.  A file called SYN.ACT
;	resides on ACT: and contains one or more lines equating synonym
;	strings with account strings.  The format is synonym=account.
;	Every time we validate an account string, we first check to see
;	if the string in question is a synonym.  If it is, then the
;	string is changed to the appropriate account string.
;
;	For example, the account string for user [10,56] changes on a
;	monthly basis, but the user has "TOPS10-DEVELOPMENT" as his
;	synonym.  The system administrator may change the entry in SYN.ACT
;	for TOPS10-DEVELOPMENT at any time, and the user remains unaffected.
;	After the synonynm translation occurs, the final account string is
;	still checked for legality.
;
;	Note:  This feature is controlled by the setting of SYNONYM, and
;	is normally turned off.
;
;107)	Allow LOGIN, SESSION, and ATTACH messages via QUEUE. UUO  /DPM
;
;110)	Fix bug where error messages were being sent back with various
;	incorrect lengths. Preserve "T" ACs in LOGUSR. /LWS
;
;111)	Username storage was only 1 word. Copying a maxium of 39 8-bit
;	characters there would clobber what ever followed.
;	QAR #868037 /LWS
;
;112)	Rip out the ACCTIO interface and replace with ACTRMS. /TL
;
;113)	Add UGVUP$ function, "validate account string and return user
;	profile. /LWS
;
;114)	Move calls to SCDINI and SCDCLS from ACTRMS to ACTDAE. Add code
;	to support new function UGSCD$. User account file is now
;	SYS:ACTDAE.SYS and  PROJCT.SYS now should reside on SYS:. /LWS
;
;115)	Fix bug where CHKACT was calling GETPRO without PPN in T1. /LWS
;
;116)	Turn on code that supports a password along with profile to
;	insert. REACT now knows how to do it. /LWS
;
;117)	Remove VERSET to set version. ACTRMS must be loaded before ACTDAE. /LWS
;
;120)	Add code to prevent password changes if AE.PCP is set in the
;	profile flag word (.AEFLG).
;	23-Jul-85  /DPM
;
;121)	Implement administrative priveleges.
;	24-Jul-85  /DPM
;
;122)	Let owner obtain his/her own profile and not require a password
;	to change unpriv'ed fields in the profile.  If the PPN or project
;	number (depending of the setting of INDPPN) of the sender matches
;	that of the profile, then access is granted.
;	15-Aug-85  /DPM
;
;123)	Move all PPN and user name wildcarding out of REACT and into ACTDAE
;	(actually ACTRMS).  Support new wildcard message UGWLD$ either from
;	a QUEUE. UUO or direct IPCF.  See ACTSYM for a definition of the
;	message offsets (UW$xxx).
;	26-Aug-85  /DPM
;
;124)	Move CHGTAB and friends into ACTCHG module of ACTLIB.  Make the
;	writing of 8-bit usage/failure files an option.  Set FILBSZ to
;	the desired byte size (default is 7-bit).
;	29-Aug-85  /DPM
;
;125)	Convert all old-style calls for a [next] profile to internal
;	wildcard calls.  Start converting error ACKing to use ACTLIB's
;	error facilities.  Other miscellaneous changes made to conform
;	to changes in the RMS-10 interface module of ACTLIB.
;	 6-Sep-85  /DPM
;
;126)	Convert more error ACKs.  Almost done!
;	13-Sep-85  /DPM
;
;127)	Add support for new accounting function UGMAP$ to map PPNs to user
;	names.
;	16-Sep-85  /DPM
;
;130)	Change format of FAILUR.LOG to record ANF/DECNET/LAT node/line
;	number on a LOGIN failure.
;	15-Oct-85  /CJA
;
;131)	Fix ACTMAP to always update name.  Since user may have supplied
;	a unique abbreviation, he might be interested in the complete
;	name too.
;
;132)	PUSH and POP of .AEPPN isn't enough in ACTMAP.  Copy PPN and
;	name to temporary storage.
;
;133)	Update UGCUP$ for profile format version 6.  This also changes
;	the format of select blocks.  Also start to enable setting
;	ACOPRV via the function code, and turning on the new acking
;	code via the function code bits.  The UG.xxx symbols of the
;	old UGCUP$ are going away sometime.  If possible, we'll make
;	.UGPRV go away too.
;	18-Nov-85  /RCB
;
;134)	Fix some bugs with the defaulting code.
;	24-Nov-85  /RCB
;
;135)	Fix undeserved invalid account string problems.
;	 4-Dec-85  /DPM
;
;136)	Fix some minor bugs in UGCUP$ and UGMAP$.
;	17-Dec-85  /RCB
;
;137)	Fix up some bugs in UGCUP$.
;	13-Jan-86  /RCB
;
;140)	Fix up password change time when changing a password even if the
;	.AEPCT value was orginally defaulted.
;	 7-Mar-86  /RCB
;
;141)	If an attempt is made to insert a profile with a duplicate
;	name, return a meaningful error message instead of the usual
;	"unexpected RMS error xxxxxx" cruft.
;	15-Apr-86  /DPM
;
;142)	Allow * to match null account strings like the documentation
;	and the usage spec claims will work.   Also match double quotes
;	in invalid account error message.
;	17-Apr-86  /DPM
;
;143)	Implement PSI on date/time change so that entrys have correct times
;	and connect time if the system has USAGE FILE CLOSURES set for DAILY.
;	2-Jun-86  /BAH
;
;144)	Make debugging easier by not using a job-wide pid.
;	3-Jul-86  /RCB
;
;145)	Invent a bunch of error codes, and use them in the coded error acks and
;	in the failure file.
;	28-Oct-86  /RCB
;
;146)	Convert to use the new STOPCD macro in GLXMAC, rather than $STOP.
;	1-Dec-86  /RCB
;
;147)	Fix privilege checking for AF.PRV.
;	3-Dec-86  /RCB
;
;150)	Range-check program-supplied job numbers on various usage entries.
;	SPR 10-35687
;	11-Feb-87  /RCB
;
;151)	Fix the (unsupported) NCRYPT algorithm in ALGCUS so that it
;	decrypts correctly (thus matching the original LOGIN routine).
;	SPR 10-35694
;	19-Feb-87  /JJF
;
;152)	Fix selection testing of .AEAUX entries to allow for wildcarded
;	structures as well as wild quotas and bits.
;	15-May-87  /RCB
;
;153)	Set UC$PRF accordingly if UGVUP$ function.
;	SPR 10-36011
;	7-Sep-87  /LWS
;
;154)	Try not to hang jobs in EW for EV.IPC.  Always ACK all [SYSTEM]GOPHER
;	messages, whether we like them or not.
;	29-Sep-87  /RCB
;
;155)	Fix bug in checking for password change allowed.  No change required
;	is not the same as an expired password.
;	16-Jun-88  /RCB
;
;156)	Correct problems with unique (10,#) PPNs.
;	SPR 10-35597
;	 5-Jan-89  /DPM
;
;157)	MAPLGL doesn't fixup PPN/NAME block pointers correctly when
;	processing an IPCF (not QUEUE. UUO) message to map PPNs to
;	names.
;	10-Jan-89  /DPM
;
;160)	Check for fatal RMS errors (ER$BUG and ER$UDF).  When these
;	errors are encountered, send messages to the operator and to
;	the user.
;	30-Aug-89 /KDO
;
;161)	Fix account ownership checking for [1,2].  It shouldn't be able
;	to do so much without admin privs.
;	14-Nov-89  /RCB
;
;162)	Don't start virtual timer traps until after we're done in ACTINI.
;	We thrash too badly while reading the old USEJOB.BIN etc.  Defer
;	going virtual until we start accepting IPCF messages.
;	14-Nov-89  /RCB
;
;	End of Revision History
;LOOSE ENDS

REPEAT 0,<
	NONE
>
	SUBTTL	DEFINITIONS AND DATA STORAGE

;GENERAL DEFINITIONS

ACTLNG==100			;LENGTH OF PUSH DOWN LIST

ND	SYNONYM,0		;DEFAULT SYNONYM TO ACCOUNT STRING TRANSLATION
				; 0 = OFF, 1 = ON
SYNFLG:	EXP	SYNONYM		;SYNONYM FLAG
SYNIFN:	BLOCK	1		;IFN FOR SYNONYM FILE
SYNLIN:	BLOCK	1		;SYNONYM FILE LINE NUMBER
SYNTAB:	BLOCK	1		;POINTER TO SYNONYM TABLE
SYNTMP:	BLOCK	10		;TEMPORARY SYNONYM STRING STORAGE
SYNARG:	BLOCK	1		;SYNONYM-ADDR,,ACCOUNT-STRING-ADDR

ACKEFL:	BLOCK	1		;NON-ZERO IF NEW-STYLE ACKS WANTED
ACKETX:	BLOCK	1		;ADDRESS OF ACK TEXT ON USER ERRORS
ACKITX:	BLOCK	1		;ADDRESS OF ACK ITEXT BLOCK

MVBFLG:	BLOCK	1		;FLAG TO MOVE WHOLE VALIDATION BLOCK
STATE2:	BLOCK 1			;SECOND STATES WORD (CONTAINS VALIDATION FLAG)
ACTDEV:	BLOCK 1			;USUALLY ERSATZ DEVICE ACT:,  DSK: IF DEBUGGING
				; (E.G., .JBOPS CONTAINS NON-ZERO)
PRJDEV:	BLOCK 1			;FOR PROJCT.SYS NOW USE SYS: OR DSK: IF DEBUG
ACNDEV:	BLOCK	1		;DEVICE FOR ACTDAE.SYS (SYS: OR DSK: IF DEBUG)
ACTUSE:	BLOCK 2			;USETI/O BLOCK FOR POSITIONING ANY FILE
ACTPDL:	BLOCK ACTLNG		;PUSH DOWN LIST

ACCTFN:	BLOCK	16		;STORAGE FOR ASCIZ ACCOUNTING FILE NAME

;INITIALIZATION BLOCK

IB:	$BUILD	(IB.SZ)		;INITIALIZATION BLOCK
$SET	(IB.PIB,,PIB)		;ADDRESS OF PID BLOCK
$SET	(IB.PRG,,%%.MOD)	;PROGRAM NAME
$SET	(IB.INT,,ACTPSI)	;INTERRUPT VECTOR BASE
$SET	(IB.FLG,IB.NPF,1)	;DON'T DO VIRTUAL TIMER TRAPS UNTIL ALL SET UP
	$EOB

PIB:	$BUILD	(PB.MXS)	;PID BLOCK
$SET	(PB.HDR,PB.LEN,PB.MXS)	;LENGTH OF THIS BLOCK
$SET	(PB.FLG,IP.JWP,1)	;JOB-WIDE PID
$SET	(PB.FLG,IP.SPF,1)	;BE A SYSTEM PID
$SET	(PB.FLG,IP.SPB,1)	;SEE IF SENDER SET IP.CFP
$SET	(PB.FLG,IP.PSI,1)	;CONNECT THIS PID TO PSI SYSTEM
$SET	(PB.INT,IP.SPI,SP.ACT)	;SPECIAL PID INDEX [SYSTEM]ACCOUNT
$SET	(PB.INT,IP.CHN,<IPCPSI-ACTPSI>)	;CHANNEL
$SET	(PB.SYS,IP.MNP,1)	;ONLY 1 PID ALLOWED FOR THIS JOB
$SET	(PB.SYS,IP.SQT,-1)	;INFINITE SEND QUOTA
$SET	(PB.SYS,IP.RQT,-1)	;INFINITE RECEIVE QUOTA
	$EOB

;IPCF SEND/RECEIVE MESSAGE DEFINITIONS

IPS.BL:	BLOCK SAB.SZ		;IPCF SEND BLOCK
IPR.BL:	BLOCK MDB.SZ		;IPCF RECEIVE BLOCK
MDBADR:	BLOCK 1			;MESSAGE DESCRIPTOR BLOCK OF LAST RECEIVE
MMSADR:	BLOCK 1			;RECEIVE DATA ADDRESS
DATADR:	BLOCK 1			;ADDRESS OF DATA FOR ADTDAE FUNCIONS
SABADR:	BLOCK 1			;ADDRESS OF SEND DATA PAGE
SABFLG:	BLOCK	1		;FLAG TO CHECK IF ACK STILL NEEDED

;PSI INTERRUPT BLOCKS

ACTPSI:
IPCPSI:	EXP	IPCTRP
	BLOCK	3

DTCPSI:	EXP	DTCTRP
	BLOCK	3

DTCTRP:	$BGINT	1,
	PUSHJ	P,ACTCHD	;ADJUST ALL TIMES AND CHECKPOINT
	$DEBRK

IPCTRP:	$BGINT	1,
	$CALL	C%INTR
	$DEBRK
	SUBTTL ERROR CODE GENERATOR

;USAGE:
; DEFINE LOCAL ERROR CODES, ERRA%=0, ERRB%=1, ...
; DEFINE LOCAL LABELS WHICH CORRESPOND TO EACH ERROR CODE.
; FOR EACH ERROR CODE, LABEL PAIR, ERCODE(LABEL,CODE)

ECDMAX==11		;MAXIMUM NUMBER OF ERROR TYPES IN ACTDAE

DEFINE	ERCODE(NAME,CODE)<
	.DIRECTIVE .XTABM
	ERRCOD	(NAME,CODE,\CODE)
	.DIRECTIVE .ITABM>

DEFINE	ERRCOD(NAME,CODE,ACODE)<
	IFG <CODE+1-ECDMAX>,<
		PRINTX %ECOD'ACODE is undefined, define ECDMAX in ACTDAE.MAC to be ACODE+1>
	NAME==ECOD'ACODE>

DEFINE	ERCALC(N)<
.N==0
REPEAT	N,<ERRJSP (\.N)
.N=.N+1>
ECOD:	SUBI	T1,ECOD0+1
	HRRZS	T1
	JRST	ERRPRO>

DEFINE	ERRJSP(N)<
ECOD'N:	JSP	T1,ECOD>


	ERCALC	(ECDMAX)
	SUBTTL	MACROS

;DEFINE A MACRO TO PUT STANDARD ACTDAE HEADER AROUND A MESSAGE TO THE OPERATOR

DEFINE	$WTOXX	(TEXT),<
	$WTO	(<Message from the Accounting System>,<TEXT>,,<$WTFLG(WT.SJI)>)
>


;DEFINE A MACRO TO OUTPUT FATAL ERRORS TO OPERATOR THEN STOP

DEFINE	$BOMB	(TEXT),<
	JRST	[$WTO(<Fatal error in the Accounting System>,<TEXT>)
		 $FATAL(<TEXT>)]
>
	SUBTTL	ACTDAE - PRIMARY MODULE FOR TOPS10 ACCOUNTING DAEMON

ACTDAE:	RESET
	MOVE	P,[IOWD ACTLNG,ACTPDL]	;SET UP OUR PDL
	MOVX	S1,IP.JWP	;[144] JOB-WIDE PID FLAG
	SKIPE	DEBUGW		;[144] IF DEBUGGING,
	ANDCAM	S1,PIB+PB.FLG	;[144] USE ONLY INIT-CLASS PID
	MOVEI	S1,IB.SZ	;LOAD SIZE OF INITIALIZATION BLOCK (IB)
	MOVEI	S2,IB		;LOAD ADDRESS OF IB
	$CALL	I%INIT		;INITIALIZE THE WORLD OF GLXLIB
	$CALL	I%ION		;TURN ON THE PSI SYSTEM
	MOVX	T2,.PCDTC	;DATE/TIME CHANGE PSI CONDITION
	MOVSI	T3,<DTCPSI-ACTPSI>
	SETZM	T4		;NO PRIORITY
	MOVX	T1,<PS.FAC+T2>	;SET UP CALL
	PISYS.	T1,
	$WTOXX	(<Date/time changes will not be adjusted in the USAGE files.
Please report PISYS. error >,^O/T1/)
	PUSHJ	P,ACTINI	;INITIALIZE THE WORLD
	MOVE	S1,[.STTVM,,^D1000] ;SETUP TO ATTEMPT VIRTUAL TIMER TRAPS
	SETUUO	S1,		;EVERY SECOND OF RUN TIME
	  TRN			;BUT I DON'T MIND IF NEVER GO VIRTUAL!

ACTDA1:	$CALL	C%RECV
	JUMPF	ACTDA2		;NO MESSAGES YET. WAIT FOR AN EVENT TO HAPPEN
	MOVEM	S1,MDBADR	;SAVE THE MESSAGE DESCRIPTOR ADDRESS
	MOVE	T1,MDB.MS(S1)	;GET THE ADDRESS OF THE MESSAGE
	ANDX	T1,MD.ADR	;MASK OUT THE ADDRESS
	MOVEM	T1,MMSADR	;SAVE IT FOR LATER
	MOVEM	T1,DATADR	;DATA MESSAGE IF NOT FROM A QUEUE. UUO
	MOVEM	T1,SABADR	;AND IN CASE OF ERROR MESSAGES
	SETZM	SABFLG		;NOT YET ACK'ED
	SETZM	RMGCOD		;NO FATAL RMS ERRORS SO FAR
	PUSHJ	P,QUECHK	;CHECK IF MESSAGE CAME FROM QUEUE.
	JUMPF	[PUSHJ P,IGNORE
		 JRST ACTDA1]
	MOVE	T1,DATADR	;USE DATADR IN CASE IT WAS A QUEUE. MESSAGE
	HRRZ	T1,UV$TYP(T1)	;GET THE IPCF MESSAGE TYPE
	ANDI	T1,AF.FUN	;MASK IT DOWN
	CAILE	T1,IPCMAX	;IS IT A LEGAL MESSAGE?
	SETZ	T1,		;NO, MAKE IT 0 = ILLEGAL
	HLRZ	T2,ACTDSP(T1)	;GET PRIV CHECKING ROUTINE
	SKIPE	T2		;SKIP IF NO CHECKING NEEDED
	PUSHJ	P,(T2)		;CHECK PRIVILEGES
	HRRZ	T2,ACTDSP(T1)	;GET MESSAGE PROCESSOR ROUTINE
	PUSHJ	P,(T2)		;DISPATCH
	SKIPN	SABFLG		;DID WE EVER SEND THE ACK?
	PUSHJ	P,IGNORE	;NO--MAKE SURE
	JRST	ACTDA1

ACTDA2:	PUSHJ	P,CHKIVL	;COMPUTE TIME UNTIL NEXT CHECKPOINT
	$CALL	I%SLP		;AND SLEEP THAT LONG
	JRST	ACTDA1		;GO BACK AND SEE WHAT WOKE US UP
;DISPATCH TABLE FOR ACCOUNT DAEMON EVENTS

ACTDSP:	XWD	PRVOPR,IGNORE	;(0)  ILLEGAL
	XWD	0,ACTVER	;(1)  REQUEST FOR ACCOUNT VALIDATION
	XWD	PRVOPR,ACTLIN	;(2)  USER IS LOGGING IN
	XWD	PRVOPR,ACTSES	;(3)  USER TYPED A SESSION COMMAND
	XWD	PRVOPR,ACTATT	;(4)  USER TYPED AN ATTACH COMMAND
	XWD	PRVOPR,IGNORE	;(5)  SET DATE/TIME MESSAGE FROM DAEMON
	XWD	PRVOPR,IGNORE	;(6)  RESPONSE TO A VALIDATION MESSAGE
	XWD	PRVOPR,USGMAK	;(7)  MAKE A USAGE ENTRY (QUEUE. UUO only)
	XWD	PRVOPR,DOUBC	;(10) DO BILLING CLOSURE
	XWD	PRVOPR,DOUFC	;(11) DO FILE CLOSURE
	XWD	PRVOPR,ACTFDM	;(12) USER FILE STRUCTURE MOUNT MESSAGE
	XWD	PRVOPR,ACTFDD	;(13) USER FILE STRUCTURE DISMOUNT MESSAGE
	XWD	PRVOPR,ACTMGM	;(14) USER MAGTAPE MOUNT MESSAGE
	XWD	PRVOPR,ACTMGD	;(15) USER MAGTAPE DISMOUNT MESSAGE
	XWD	PRVOPR,ACTDTM	;(16) USER DECTAPE MOUNT MESSAGE
	XWD	PRVOPR,ACTDTD	;(17) USER DECTAPE DISMOUNT MESSAGE
	XWD	PRVOPR,ACTSPM	;(20) DISK PACK SPINDLE SPIN-UP MESSAGE
	XWD	PRVOPR,ACTSPD	;(21) DISK PACK SPINDLE SPIN-DOWN MESSAGE
	XWD	PRVOPR,IGNORE	;(22) ACK (SENT FROM ACTDAE, NEVER RECEIVED)
	XWD	PRVOPR,ACTDUE	;(23) DISK USAGE DATA
	XWD	PRVOPR,ACTACC	;(24) ACCESS CONTROL CHECK (QUEUE. UUO only)
	XWD	0,ACTOUP	;(25) OBTAIN USER PROFILE (QUEUE. UUO only)
	XWD	PRVOPR,IGNORE	;(26) UNDEFINED
	XWD	0,ACTOUT	;(27) A LOGOUT UUO WAS DONE
	XWD	0,ACTACC	;(30) ACC BUT RETURN PROFILE (QUEUE. UUO only)
	XWD	0,ACTCUP	;(31) CHANGE USER PROFILE (QUEUE. UUO only)
	XWD	PRVOPR,ACTPSW	;(32) VALIDATE PASSWORD (QUEUE. UUO only)
	XWD	PRVADM,ACTLOK	;(33) LOCK USER ACCOUNT FILE (QUEUE. UUO only)
	XWD	PRVADM,ACTUNL	;(34) UNLOCK USER ACCOUNT FILE (QUEUE. UUO only)
	XWD	PRVOPR,ACTVER	;(35) VALIDATE ACCOUNT AND RETURN PROFILE
	XWD	PRVADM,ACTSCD	;(36) CLOSE AND REOPEN SCDMAP.INI (QUEUE. UUO only)
	XWD	0,ACTWLD	;(37) GET POSSIBLY WILDCARDED PPN/NAME
	XWD	0,ACTMAP	;(40) MAP PPNS/NAMES
IPCMAX==:.-ACTDSP-1


IGNORE:	SKIPE	GFRFLG		;IF THIS CAME FROM [SYSTEM]GOPHER,
	JRST	QUEACK		;RELEASE THE JOB
	SKIPN	SABFLG		;NO. DO WE NEED TO RELEASE THE MSG?
	PUSHJ	P,IPCREL	;YES. DO IT AND REMEMBER IT
	$RETF

IPCREL:	MOVE	S1,MDBADR	;GET THE MDB ADDRESS
	SKIPE	MDB.MS(S1)	;HAS IT ALREADY BEEN RELEASED?
	$CALL	C%REL		;NO. DO IT NOW
	SETOM	SABFLG		;REMEMBER THAT WE CALLED C%REL
	POPJ	P,		;RETURN
SUBTTL	PRIVILEGE CHECKING


PRVADM:	PUSHJ	P,CHKADM	;CHECK ADMINISTRATIVE PRIVS
	JUMPF	NOPRV		;NO ACCESS ALLOWED
	$RETT			;RETURN

PRVOPR:	PUSHJ	P,CHKOPR	;CHECK FOR [1,2] OR JACCT PRIVS
	JUMPF	NOPRV		;NO ACCESS ALLOWED
	$RETT			;RETURN

; CHECK FOR ADMINISTRATIVE PRIVS OR JACCT
CHKADM:	MOVE	T2,MDBADR	;POINT TO MESSAGE DESCRIPTOR
	LOAD	T3,MDB.PV(T2),MD.PJB ;GET JOB NUMBER
	HRLZS	T3		;PUT IN LH
	HRRI	T3,.GTPRV	;INCLUDE GETTAB TABLE
	GETTAB	T3,		;GET PRIV WORD
	  SETZ	T3,		;FAILED?
	MOVE	T4,MDB.PV(T2)	;GET SENDER'S PRIVS
	TXNN	T4,MD.PWH	;JACCT'ED JOB?
	TXNE	T3,JP.ADM	;ADMINISTRATIVE PRIVS?
	$RETT			;YES OR YES, RETURN GOODNESS
	$RETF			;NO TO BOTH, FAIL
				; ([1,2] DOESN'T WIN FOR FREE)

; CHECK FOR [1,2] OR JACCT PRIVS
CHKOPR:	MOVE	T2,MDBADR	;POINT TO MESSAGE DESCRIPTOR
	MOVE	T3,MDB.PV(T2)	;GET SENDER'S PRIVS AND JOB NUMBER
	MOVE	T4,MDB.SD(T2)	;GET SENDER'S PPN
	CAME	T4,MYPPN	;SAME AS US (NORMALLY [1,2])
	TXNE	T3,MD.PWH	;NO--JACCT'ED JOB?
	$RETT			;YES
	$RETF			;NO ACCESS ALLOWED

; CHECK FOR OWNER
CHKOWN:	SKIPE	ACOPRV		;IF PRIV'ED
	$RETT			;THEN AN OWNER
	MOVE	T2,MDBADR	;POINT TO MESSAGE DESCRIPTOR
	MOVE	T3,MDB.SD(T2)	;GET SENDER'S PPN
	CAMN	T3,S1		;IS THIS AN EXACT MATCH?
	$RETT			;YES, HE OWNS IT
	$SAVE	T1		;PRESERVE AN AC
	MOVE	T2,S1		;GET TARGET PPN
	CAMN	T3,MYPPN	;IF [1,2],
	TRCA	T3,1		;THEN FOOL THE CHKACC UUO,
	TRNA			;(NOT [1,2])
	TRC	T2,1		;BUT GIVE THE RIGHT ANSWER
	MOVE	T1,[.ACCPR,,<777>B26+<077>B35] ;SETUP FOR CHKACC
	MOVEI	T4,T1		;POINT TO UUO ARG BLOCK
	CHKACC	T4,		;TRY IT
	  $RETF			;SICK MONITOR
	CAIE	T4,0		;IS HE A WINNER?
	$RETF			;NO
	$RETT			;OR YES


NOPRV:	SKIPN	GFRFLG		;DID THIS COME FROM [SYSTEM]GOPHER?
	JRST	NOPRV1		;NO, JUST IGNORE (CAN'T TRUST USERS DATA)
	PUSHJ	P,M%GPAG	;BUT CAN TRUST THE MONITOR, GET A MESSAGE
	MOVEM	S1,SABADR	;STORE WHERE COMMON ROUTINES CAN FIND IT
	PUSHJ	P,ERROR3	;SAY "JOB NOT PRIVILEGED"
	PUSHJ	P,FIXQUE	;MOVE ACK CODE AND FORMAT THE MESSAGE
	PUSHJ	P,RSPSAB	;SEND RESPONSE FROM SABADR TO THE MONITOR
NOPRV1:	SETZ	T1,		;RETURN FUNCTION = 0 = ILLEGAL
	$RETF			;AND PITCH IT
	SUBTTL	ACTDAE - INITIALIZATION


ACTINI:	MOVEI	S1,'ACT'	;ACTDAE PREFIX
	MOVEI	S2,ERRACK	;ROUTINE TO ACK USER ERRORS
	PUSHJ	P,A$ERRI##	;INIT ERROR PROCESSOR
	PUSHJ	P,ACGTAB	;AREA TO DO ALL THE GENERAL GETTABS
	MOVX	S1,ACTFIL	;GET ACCOUNTING FILE NAME
	$TEXT	(<-1,,ACCTFN>,<^W/ACNDEV/:^W/S1/.SYS^0>) ;GENERATE ACCT FILESPEC
	PUSHJ	P,USGSAU	;APPEND TO USAGE.OUT
	PUSHJ	P,SYSINI	;SYSTEM RESTARTED. DO PRELIMINARY WORK
	PUSHJ	P,ACDINI	;INITIALIZE ALL DISK STUFF
	MOVE	T1,STATE2	;GET STATES WORD
	TXNN	T1,ST%ACV	;ACCOUNT VALIDATION REQUIRED?
	$WTOXX	(<Account validation is not required>)
	PUSHJ	P,SYNFIL	;READ SYNONYM FILE
	SETOM	CHKNDX		;FORCE A CHECKPOINT WHEN IPCF QUEUE IS EMPTY
	POPJ	P,

;ACGTAB - ROUTINE TO DO ALL GENERAL GETTABS AND STORE THE RESULTS FOR
;	LATER USE.

ACGTAB:	SKIPE	.JBOPS		;ARE WE DEBUGGING?
	SKIPA	T1,['   DSK']	;YES. LOOK IN OUR OWN AREA
	MOVEI	T1,'ACT'	;NO. USE ERSATZ DEVICE
	MOVEM	T1,ACTDEV	;SAVE IT
	SKIPE	.JBOPS		;ARE WE DEBUGGING?
	SKIPA	T1,['   DSK']	;YES. LOOK IN OUR OWN AREA
	MOVEI	T1,'SYS'	;NO. USE SYS: FOR PROJCT.SYS
	MOVEM	T1,PRJDEV	;SAVE IT
	MOVSM	T1,ACNDEV	;SETUP FOR ACTDAE.SYS
	MOVX	T1,%CNTIC	;GET NUMBER OF JIFFIES/SECOND ON THIS MACHINE
	GETTAB	T1,
	  MOVEI	T1,^D60		;DEFAULT TO 60
	MOVEM	T1,JIFSEC
	MOVX	T1,%CNST2
	GETTAB	T1,
	  MOVEI	T1,0		;ERROR. DON'T DO VALIDATION.
	MOVEM	T1,STATE2	;SAVE IT
	TXNN	T1,ST%ERT	;IS THERE EBOX/MBOX RUNTIME?
	JRST	ACGTA1		;NO. SKIP KL-ONLY STUFF
	MOVX	T1,%CVETJ	;GET CPU0'S EBOX TICKS/JIFFY
	GETTAB	T1,
	  MOVEI	T1,0
	MOVEM	T1,ETICKS
	MOVX	T1,%CVNTJ	;GET CPU0'S MBOX TICKS/JIFFY
	GETTAB	T1,
	  MOVEI	T1,0
	MOVEM	T1,MTICKS
ACGTA1:	MOVX	T1,%CNSJN	;GET NUMBER OF JOBS FROM MONGEN
	GETTAB	T1,
	  MOVEI	T1,^D201	;ERROR. DEFAULT TO A 200 JOB MONITOR
	MOVEI	T1,-1(T1)	;REMOVE NULL JOB
	MOVEM	T1,JOBMAX	;ONLY ALLOW THIS MANY JOBS TO LOGIN
	GETPPN	T1,		;GET OUR PPN
	  JFCL			;SILLY SKIP
	MOVEM	T1,MYPPN	;STORE FOR JUNK MAIL CHECK
	POPJ	P,
;SYSINI - ROUTINE CALLED AT SYSTEM STARTUP TO MAKE INCOMPLETE SESSION ENTRIES

SYSINI:	$CALL	.SAVE1		;GET A WORKING (AND SAFE) AC
	PUSHJ	P,CPJSAU	;OPEN THE CHECKPOINT FILE
	MOVE	T1,JOBMAX	;GET NUMBER OF JOBS ALLOWED TO LOGIN
	IMULI	T1,CPJIOB	;*BLOCKS PER CHECKPOINT AREA FOR EACH JOB
	ADDI	T1,1+JBOFFS	;ADJUST + ACCOUNT FOR GENERAL BLOCK
	MOVE	T2,CPJCHN	;THE CHANNEL NUMBER
	PUSHJ	P,AUSETO	;CREATE/EXTEND THE FILE, ZEROING AS WE GO
	JUMPT	SYSIN1		;SO FAR SO GOOD
	TXNN	T1,IO.BKT	;OUT OF DISK SPACE
	$BOMB	<ACTECE Error (^O/T1/) while Creating or Extending the checkpoint file>
	PUSHJ	P,CPJCLS	;CLOSE THE FILE
	PUSHJ	P,CPJSAU	;RE-OPEN
	MOVE	T1,USEJOB+.RBSIZ ;AMOUNT THAT GOT CREATED
	ADDI	T1,177		;CONVERT TO BLOCKS
	LSH	T1,-7		;...
	SUBI	T1,JBOFFS	;ACCOUNT FOR THE GENERAL BLOCK
	IDIVI	T1,CPJIOB	;COMPUTE NUMBER OF JOBS THAT CAN BE DESCRIBED
	SKIPG	T1		;HOPE SO
	SETZ	T1,		;WHOOPS?
	MOVEM	T1,JOBMAX	;SAVE AS MAX JOBS WE WILL ALLOW
	$WTOXX	<Disk is too full, only ^D/JOBMAX/ jobs will be allowed to log in>
SYSIN1:	PUSHJ	P,READJG	;READ IN THE FILE HEADER
	PUSHJ	P,DATIM		;SET UP CURRENT DATE/TIME
	SKIPN	USGOSZ		;IF ZERO, THE WE JUST CREATED USAGE.OUT
	PUSHJ	P,MAKUFH	; SO MAKE A USAGE FILE HEADER RECORD
	PUSHJ	P,MAKRES	;MAKE A SYSTEM RESTART ENTRY
	MOVN	P1,CPJGEN+FILMJB ;MAXIMUM NUMBER OF JOBS IN THE FILE
	JUMPE	P1,SYSIN4	;DONE IF JUST CREATED THE FILE
	MOVE	T1,CPJGEN+FILBPJ ;GET NUMBER OF BLOCKS REQUIRED FOR EACH JOB
	MOVE	T2,CPJGEN+FILBPD ;GET NUMBER OF BLOCKS REQUIRED FOR DEVICES
	CAIN	T2,CPDIOB	;BETTER MATCH
	CAIE	T1,CPJIOB	;BETTER MATCH
	$BOMB	<ACTCFF Checkpoint File Format doesn't match this version of ACTDAE>
	HRLZS	P1		;FORM AOBJN
	HRRI	P1,1		;SKIP THE NULL JOB
	SETZM	SYSINC		;CLEAR COUNT OF JOBS STILL AROUND (ACTDAE RESTART)
SYSIN2:	HRRZM	P1,JOBNUM	;STORE JOB NUMBER
	PUSHJ	P,READJP	;READ IN THE CHECKPOINT INFORMATION
	SKIPN	CPJBUF+CJOB	;DO WE HAVE DATA FOR THIS JOB
	JRST	SYSIN3		;NO, TRY THE NEXT JOB
	HRL	T1,P1		;THE JOB NUMBER AGAIN
	HRRI	T1,.GTJLT	;JOB LOGIN TIME
	GETTAB	T1,		;THIS MIGHT BE AN ACTDAE RESTART INSTEAD OF
	  SETZ	T1,		; A SYSTEM RESTART, ONLY SOME WILL GET INCOMPLETE
	JUMPE	T1,SYSIN5	;JOB NOT THERE IF NO LOGIN TIME
	CAMN	T1,CPJBUF+CJLGTM ;THIS JOB STILL AROUND
	JRST	[AOS SYSINC	;YES, COUNT IT TO INDICATE ACTDAE RESTART
		 JRST SYSIN3]	;WILL CATCH THE JOB AT LOGOUT
SYSIN5:	PUSHJ	P,MKISES	;MAKE AN INCOMPLETE SESSION ENTRY
	SKIPE	CPJBUF+CDEVFL	;ANY DEVICES FOR THIS JOB
	PUSHJ	P,SYSIND	;GENERATE DEVICE ENTRIES
	PUSHJ	P,CBJZER	;CLEAR OUT THE JOB INFORMATION
	PUSHJ	P,CPJCOP	;IN BOTH PLACES
	PUSHJ	P,WRITJP	;CLEAN FILE NOW THAT WE ARE DONE WITH THIS JOB
SYSIN3:	AOBJN	P1,SYSIN2	;DO ALL POSSIBLE JOBS
	SETZM	JOBNUM		;JOB NUMBER 0 = SPINDLE  ENTRIES
	SKIPN	SYSINC		;IF ACTDAE RESTART, DONT DO SPINDLES HERE
	PUSHJ	P,SYSIND	;DO THEM NOW
SYSIN4:	PUSHJ	P,CPJCLS	;CLOSE OUT THE FILE NOW
	POPJ	P,		;AND ALL DONE WITH RESTART

SYSINC:	BLOCK	1		;COUNT/FLAG FOR ACTDAE-RESTART RATHER THAN SYSTEM RESTART

SYSIND:	HRROI	T1,INIDVS	;POINT TO ROUTINE
	PUSHJ	P,ALLDEV	;AND CALL IT FOR ALL DEVICES
	POPJ	P,		;AND RETURN

;FOLLOWING CO-ROUTINE CALLED BY ALLDEV FOR EACH DEVICE IN THE jjjDEV.BIN FILE
;P1 = THE DEVICE TYPE INDEX

INIDVS:	PUSHJ	P,@[EXP MAKFSR,MAKMAG,MAKDEC,MAKSPN]-1(P1) ;MAKE THE ENTRY
	PUSHJ	P,CBDZER	;CLEAR THE BLOCK
	PUSHJ	P,CPDCOP	;BOTH HALVES
	PUSHJ	P,WRITDP	;ZAP THE DISK AREA
	POPJ	P,		;AND RETURN FOR THE NEXT
;ACDINI - ROUTINE TO DO INITIALIZATION OF DISK (E.G., OPENING THE FILES
;	PROJCT.SYS, USEJOB.BIN, ACTDAE.SYS

ACDINI:	MOVE	T1,STATE2	;GET SECOND STATES WORD
	TXNN	T1,ST%ACV	;IF VALIDATION IS NOT REQUIRED DON'T DO
	JRST	ACDIN1		; PROJCT.SYS INITIALIZATION
	PUSHJ	P,PRJRED	;PROJCT.SYS INITIALIZATION
	SKIPF			;WHOOPS
	PUSHJ	P,BLDPRJ	;VERIFY VERSIONS, SIZES, BUILD TABLES
ACDIN1:	PUSHJ	P,INITIO##	;INIT FILE I/O (RMS-10 INTERFACE)
	SKIPT			;CHECK FOR ERRORS
	STOPCD	(IOF,HALT,,<File I/O interface initialization failure>)
	MOVEI	S1,ACCTFN	;GET FILENAME FOR RMS TO OPEN
	MOVEI	S2,1		;WANT TO WRITE THE FILE
	PUSHJ	P,OPNA##	;OPEN FILE "A" FOR I/O
	SKIPT			;MAKE SURE IT WORKED
	STOPCD	(AFF,HALT,,<Accounting file initialization failure>)
	PUSHJ	P,A$ISCD##	;INITIALIZE CLASS SCHEDULER MAPPING
	PUSHJ	P,CPJSAU	;INITIALIZE THE PRIMARY JOB CHECKPOINT FILE
	POPJ	P,
	SUBTTL	ACTDAE - GENERAL ROUTINES

;AUSETI - ROUTINE TO POSITION A FILE WHICH IS OPEN ON THE CHANNEL STORED IN T2
;	TO THE BLOCK NUMBER STORED IN T1 FOR INPUT
;CALL:	MOVE	T1,BLOCK NUMBER
;	HRL	T2,CHANNEL NUMBER (PUT CHANNEL NUMBER IN LEFT HALF)
;	PUSHJ	P,AUSETI
;	ONLY RETURN (TRUE OR FALSE)

AUSETI:	MOVEM	T1,ACTUSE+1	;STORE THE BLOCK #
	HRRI	T2,.FOUSI	;GET THE USETI FUNCTION CODE
	MOVEM	T2,ACTUSE	;STORE IT
	MOVE	T1,[2,,ACTUSE]
	FILOP.	T1,
	  $RETF			;ERROR. GIVE A FALSE RETURN
	$RETT			;GIVE A TRUE RETURN

;AUSETO - ROUTINE TO POSITION A FILE WHICH IS OPEN ON THE CHANNEL STORED IN T2
;	TO THE BLOCK NUMBER STORED IN T1 FOR OUTPUT
;CALL:	MOVE	T1,BLOCK NUMBER
;	HRL	T2,CHANNEL NUMBER (PUT CHANNEL NUMBER IN LEFT HALF)
;	PUSHJ	P,AUSETO
;	ONLY RETURN (TRUE OR FALSE)

AUSETO:	MOVEM	T1,ACTUSE+1	;STORE THE BLOCK #
	HRRI	T2,.FOUSO	;GET THE USETO FUNCTION CODE
	MOVEM	T2,ACTUSE	;STORE IT
	MOVE	T1,[2,,ACTUSE]
	FILOP.	T1,
	  $RETF			;ERROR. GIVE A FALSE RETURN
	$RETT			;GIVE A TRUE RETURN
	SUBTTL	ACTQUE - MODULE FOR QUEUE. UUO ROUTINES

;GENERAL DEFINITIONS

MYPPN:	BLOCK 1			;MY PPN FOR PRIV CHECKING
QUECNT:	0			;COUNT OF QUEUE ARGUMENT BLOCKS LEFT
QUEFLG:	0			;SET IF MESSAGE WAS FROM A QUEUE. UUO
QUEBLK:	0			;ADDRESS OF NEXT QUEUE. ARGUMENT BLOCK
QUEEND:	0			;ADDRESS JUST OFF THE END OF QUEUE. BLOCK
GFRFLG:	0			;FLAG FOR [SYSTEM]GOPHER AS THE SENDER
RMGUDT:	0			;UNIVERSAL DATE/TIME OF LAST RMS MESSAGE
RMGCOD:	0			;FATAL RMS ERROR CODE

;***DESCRIPTOR
ACONMD:	EXP -1			;THIS IS A NAME
	EXP ACOUSR		;POINT TO NAME BUFFER
;***

;****DESCRIPTOR
ACOPPD:	EXP 0			;THIS IS A PPN
ACOPPN:	BLOCK 1			;PPN WE ARE TALKING ABOUT
;****

ACOPRV:	BLOCK	1		;INDICATES USER IS PRIVED

VALZER:				;BEGINNING OF BLOCK TO ZERO (MAINTAIN ORDER)
VALBLK:	BLOCK	UV$ACE+1	;REBUILT QUEUE. VALIDATION MESSAGE
VALACT:	BLOCK	1		;FLAG THAT .UGACT BLOCK WAS GIVEN
ACOPSW:	BLOCK	.APWLW		;PASSWORD FOR ACCESS CONTROL
ACOTYP:	BLOCK	1		;TYPE OF ACCESS (0 = REGULAR, NON-0 = SPRINT)
ACOUXP:	BLOCK	1		;INDICATES USERNAME VS PPN BEING USED
ACOUSR:	BLOCK	.AANLW		;NAME WE ARE TALKING ABOUT
ACOPTR:	BLOCK	1		;POINTER TO A WILDCARD BLOCK
ACOWLD:	BLOCK	UW$MIN		;INTERNAL WILDCARD BLOCK
ACOACK:	BLOCK	.AANLW		;WILDCARD ACK BLOCK
ACOMAP:	BLOCK	1		;ADDRESS OF PPN/NAME MAPPING BLOCK
TMPMAP:	BLOCK	UU$LEN		;TEMPORARY MAPPING BLOCK
TMPLEN:	BLOCK	1		;LENGTH OF SUPPLIED NAME
TMPCNT:	BLOCK	1		;NUMBER OF MAPPING BLOCKS SUPPLIED

;***ACTRMS INTERFACE
ACOPRO:	BLOCK	.AEMAX		;BLOCK FOR USER PROFILE
ACODEF:	BLOCK	.AEMAX		;DEFAULT PROFILE STORAGE
ACODWL:	BLOCK	UW$MIN		;WILDCARD BLOCK FOR DEFAULTING
ACOBIT:	BLOCK	.AMPLW		;DEFAULTING BIT MAP
ACOBMP:	BLOCK	1		;POINTER INTO BIT MAP
;***

VALZND==.-1			;LAST WORD TO ZERO FOR VALIDATION REQUESTS

QUEADR:	BLOCK 1			;ADDRESS OF THE REBUILT DEFUS LIST FOR MAKING AN ENTRY
QUELEN:	BLOCK 1			;LENGTH OF THE CONTENTS OF QUEADR
QEXTRA==7			;EXTRA WORDS NEEDED TO COMPLETE AN ENTRY'S
				; INTERNAL DEFUS LIST BUILT AT THE CONTENTS OF
				; QUEADR:.  1) DISPATCH (UGENT$)  2. ENTRY TYPE
				; 3. TERMINATED WITH A ZERO WORD AND 4-7. PROVIDE
				; DEFUS'S FOR ACTDAE VERSION NUMBER AND THE DATE
				; AND TIME THE ENTRY IS MADE. SEE QUEENT ROUTINE.
	SUBTTL	ACTQUE - GENERAL ROUTINES FOR QUEUE. UUO SECTION

;QUECHK - ROUTINE TO CHECK IF MESSAGE IF FROM A QUEUE. UUO.  IF SO, MESSAGE
;	HAS A DIFFERENT FORMAT THAN THE MESSAGES DEFINED IN ACTSYM.MAC.

QUECHK:	SETZM	QUEFLG		;INITALIZE
	SETZM	GFRFLG		;ASSUME NOT FROM GOPHER
	SETZM	QUEBLK		;NO POINTER YET
	MOVE	T1,MDBADR	;MESSAGE DESCRIPTOR BLOCK
	MOVE	T2,MDB.FG(T1)	;GET THE FLAGS
	TXNE	T2,IP.CFE!IP.CFM ;CHECK FOR ERRORS OR RETURNED MAIL
	$RETF			;PITCH THE MESSAGE
	SETZM	ACOPRV		;ASSUME NO DESIRE FOR PRIVS
	SETZM	ACKEFL		;ASSUME WANTS OLD ERROR ACKS
	MOVE	T2,MDB.SI(T1)	;GET THE SYSTEM INDEX WORD
	TXNN	T2,SI.FLG	;IS THIS FROM A SYSTEM PID?
	JRST	QUECH7		;NO. CHECK FUNCTION FOR FLAGS
	LOAD	T2,T2,SI.IDX	;YES. FETCH THE S-PID INDEX
	CAXE	T2,SP.GFR	;IS IT FROM [SYSTEM]GOPHER?
	JRST	QUECH7		;NO. JUST CHECK FUNCTION FOR FLAGS
	SETOM	GFRFLG		;YES. REMEMBER THAT IT'S FROM GOPHER
	MOVE	T1,MMSADR	;MESSAGE ADDRESS
	MOVE	T2,.MSTYP(T1)
	ANDX	T2,MS.TYP	;MESSAGE TYPE
	CAIE	T2,.IPCQU	;IS THIS FROM A QUEUE. UUO?
	$RETT			;NO. ASSUME ANOTHER FORMAT (E.G., LOGOUT MESSAGE)
	SETOM	QUEFLG		;REMEMBER IT'S A QUEUE. UUO
;	SETZM	QUEBLK		;INDICATE THE START OF NEW MESSAGE
QUECH1:	MOVE	T1,MMSADR	;MESSAGE ADDRESS
	PUSHJ	P,GETBLK	;GET NEXT QUEUE BLOCK
	JUMPF	.POPJ		;NO MORE BLOCKS, ASSUME BAD MESSAGE
	CAIE	T1,.QBFNC	;IS THIS THE FUNCTION BLOCK?
	JRST	QUECH1		;NO. LOOP UNTIL IT'S FOUND
	MOVE	T1,(T3)		;GET THE FUNCTION
	CAIN	T1,.QUVAL	;IS IT A VALIDATION MESSAGE?
	PJRST	QUEVAL		;YES. SET UP THE MESSAGE AND VALIDATE
	CAIE	T1,.QUMAE	;IS IT SOME KIND OF ACCOUNTING MESSAGE?
	$RETF			;NO. ASSUME ILLEGAL MESSAGE
QUECH2:	MOVE	T1,MMSADR	;GET THE NEXT QUEUE. BLOCK
	PUSHJ	P,GETBLK	; FOR ACCOUNTING SUBFUNCTION
	JUMPF	.POPJ		;ILLEGAL ACCOUNTING MESSAGE
	CAIE	T1,.QBAFN	;IS THIS THE RIGHT BLOCK?
	$RETF			;NO. DECLARE IT ILLEGAL ACCOUNTING MESSAGE
	MOVE	T1,(T3)		;GET THE SUBFUNCTION
	PUSHJ	P,QUECH8	;ANALYZE THE FLAGS
	$RETIF			;PROPAGATE FAILURE
	MOVSI	S1,-QUESIZ	;AOBJN POINTER
QUECH3:	HLRZ	S2,QUETAB(S1)	;GET ACCT MSG TYPE CODE
	CAIE	S2,(T1)		;A MATCH?
	AOBJN	S1,QUECH3	;SEARCH THE TABLE
	JUMPGE	S1,.RETF	;NO SUCH BEAST
	HRRZ	S2,QUETAB(S1)	;GET QUEUE. UUO CONVERSION ROUTINE ADDR
	PJRST	(S2)		;DISPATCH

QUECH7:	MOVE	T3,MMSADR	;GET MESSAGE ADDRESS
	LOAD	T1,(T3),MS.TYP	;GET FUNCTION CODE
QUECH8:	TRZE	T1,AF.CEA	;WANT NEW ERROR ACKS?
	SETOM	ACKEFL		;YES, REMEMBER THAT
	TRZN	T1,AF.PRV	;WANT PRIVS?
	JRST	QUECH9		;NO, SKIP IT
	PUSH	P,T1		;SAVE BITS
	PUSHJ	P,PRVADM	;CHECK FOR PRIVS
	POP	P,T1		;RESTORE
	$RETIF			;PROPAGATE FAILURE
	SETOM	ACOPRV		;YES, HE GETS PRIVS
QUECH9:	TRNN	T1,^-AF.FUN	;ANY RESERVED BITS ON?
	$RETT			;NO, HE WINS
	$RETF			;YES, HE LOSES
QUETAB:	UGVAL$,,QUEVLX		;VALIDATION
	UGLGN$,,QUELGN		;LOGIN
	UGSES$,,QUESES		;SESSION
	UGATT$,,QUEATT		;ATTACH
	UGENT$,,QUEENT		;MAKE AN ENTRY
	UGOUP$,,QUEACC		;ACCESS CONTROL
	UGACC$,,QUEACC		;ACCESS CONTROL
	UGCUP$,,QUECUP		;CHANGE USER PROFILE
	UGVRP$,,QUEACC		;CHANGE USER PROFILE
	UGPSW$,,QUEACC		;VERIFY PASSWORD
	UGLOK$,,QUEACC		;LOCK ACCOUNTING FILE
	UGUNL$,,QUEACC		;UNLOCK ACCOUNTING FILE
	UGSCD$,,QUEACC		;REREAD SCDMAP.INI
	UGWLD$,,QUEACC		;GET POSSIBLY WILDCARDED PPN OR NAME
	UGMAP$,,QUEACC		;MAP PPN/NAMES
QUESIZ==.-QUETAB		;LENGTH OF TABLE
; QUEATT - ATTACH MESSAGE
QUEATT:	MOVEI	T1,UA$ACK	;ACK CODE OFFSET
	PJRST	QUECOM		;ENTER COMMON CODE

; QUELGN - LOGIN MESSAGE
QUELGN:	MOVEI	T1,UL$ACK	;ACK CODE OFFSET
	PJRST	QUECOM		;ENTER COMMON CODE

; QUESES - SESSION MESSAGE
QUESES:	MOVEI	T1,US$ACK	;ACK CODE OFFSET
;	PJRST	QUECOM		;ENTER COMMON CODE


; QUECOM - COMMON ROUTINE TO CONVERT QUEUE. UUO MESSAGES TO NORMAL IPCF FORMAT
; CALL:	MOVE	T1, ACK CODE OFFSET IN QUEUE MSG
;	PUSHJ	P,QUECOM
;
; TRUE RETURN:	MESSAGE CONVERTED
; FALSE RETURN:	JUNK MESSAGE

QUECOM:	PUSH	P,T1		;SAVE ACK CODE OFFSET
	PUSHJ	P,GETBLK	;GET THE NEXT QUEUE. BLOCK
	JUMPF	QUECMF		;CHECK FOR ERRORS
	CAIE	T1,.QBAET	;ACCOUNTING ENTRY BLOCK?
	JUMPF	QUECMF		;BAD MESSAGE FORMAT
	MOVE	T1,DATADR	;POINT TO MESSAGE
	MOVE	T1,.MSCOD(T1)	;GET ACK CODE
	EXCH	T1,(P)		;SWAP ACK CODE WITH OFFSET
	ADDI	T1,(T3)		;INDEX INTO MESSAGE
	POP	P,(T1)		;MOVE ACK CODE
	MOVSI	T1,(T3)		;POINT TO DATA PORTION OF MESSAGE
	HRR	T1,DATADR	;AND TO START OF ACTUAL MESSAGE
	ADD	T2,DATADR	;COMPUTE END OF BLT
	BLT	T1,-1(T2)	;SLIDE MSG UP SO IT'S LIKE AN IPCF MSG
	$RETT			;AND RETURN

QUECMF:	POP	P,(P)		;PRUNE STACK
	$RETF			;RETURN
; ACK A QUEUE. UUO
; CALL:	PUSHJ	P,QUEACK
;
; TRUE RETURN:	OLD MESSAGE RELEASED, ACK SENT
; FALSE RETURN:	OLD MESSAGE RELEASED, GLXLIB ERROR CODE IN AC S1

QUEACK:	SKIPN	SABFLG		;STILL NEED TO DO THIS?
	PUSHJ	P,IPCREL	;YES. DO IT AND REMEMBER IT
	MOVE	T1,ACKCOD	;GET ACK CODE
	MOVEM	T1,ACKMSG+.MSCOD ;SAVE
	MOVEI	S1,SAB.SZ	;SEND ARGUMENT BLOCK LENGTH
	MOVEI	S2,IPS.BL	;SEND ARGUMETN BLOCK ADDRESS
	MOVE	T1,MDBADR	;GET MESSAGE DISCRIPTOR BLOCK
	MOVE	T1,MDB.SP(T1)	;SENDER'S PID
	MOVEM	T1,SAB.PD(S2)
	MOVEI	T1,ACKLEN	;LENGTH OF MESSAGE
	MOVEM	T1,SAB.LN(S2)
	MOVEI	T1,ACKMSG	;MESSAGE ADDRESS
	MOVEM	T1,SAB.MS(S2)
	$CALL	C%SEND		;ACK THE USER
	$RETIT			;RETURN IF NO ERRORS
	MOVE	T1,MDBADR	;POINT TO MDB
	$WTOXX	<^I/ACKTXT/>
	$RETF			;GIVE UP

ACKMSG:	$BUILD	(.OHDRS+ARG.SZ)		;SIZE OF MESSAGE
	  $SET	(.MSTYP,MS.CNT,ACKLEN)	;LENGTH OF MESSAGE
	  $SET	(.MSTYP,MS.TYP,.OMTXT)	;TEXT MESSAGE
	  $SET	(.MSFLG,MF.NOM,1)	;NO DATA IN MESSAGE (JUST AN ACK)
	  $SET	(.MSCOD,,0)		;ACK CODE (FILLED IN LATER)
	  $SET	(.OHDRS+ARG.HD,AR.LEN,2);TWO WORDS OF DATA
	  $SET	(.OHDRS+ARG.HD,AR.TYP,.CMTXT) ;TYPE OF DATA (TEXT)
	  $SET	(.OHDRS+ARG.DA,,0)	;NO TEXT
	$EOB				;END OF BLOCK
ACKLEN==.-ACKMSG			;LENGTH OF MESSAGE


ACKTXT:	ITEXT	(<Cannot ACK job ^D/MDB.PV(T1),MD.PJB/ ^U/MDB.SD(T1)/
Error: ^E/S1/>)
;QUEVAL - ROUTINE TO CONVERT A QUEUE. UUO VALIDATION MESSAGE INTO A FORMAT THE
;	ACCOUNT DAEMON AND FRIENDS ALREADY KNOW ABOUT.
;CALL:	MDBADR/ADDRESS OF MESSAGE DESCRIPTOR BLOCK
;	MMSADR/ADDRESS OF IPCF MESSAGE DATA
;	QUEBLK/POINTS TO NEXT QUEUE. ARGUMENT BLOCK TO BE READ

QUEVAL:	MOVEI	T1,UGVAL$	;TYPE OF ACCOUNT MESSAGE
	PUSHJ	P,PREVAL	;PREPARE VALIDATION BLOCKS
QUEVA1:	MOVE	T1,MMSADR	;MESSAGE ADDRESS
	PUSHJ	P,GETBLK	;GET THE NEXT QUEUE. BLOCK
	JUMPF	QUEVA3		;END OF BLOCK, BEGIN VALIDATION
	CAIE	T1,.QBOID	;IS IT A PPN?
	JRST	QUEVA2		;NO. SEE IF IT'S AN ACCOUNT BLOCK
	MOVE	T3,(T3)		;GET THE PPN
	MOVEM	T3,VALBLK+UV$PPN ;STORE THE PPN
	JRST	QUEVA1		;LOOK AT NEXT BLOCK
QUEVA2:	CAIE	T1,.QBACT	;IS THIS BLOCK AN ACCOUNT?
	JRST	QUEVA1		;NO. SEE IF WE'RE DONE
	HRLZ	S1,T3		;BLT THE ACCOUNT INTO VALBLK
	HRRI	S1,VALBLK+UV$ACT
	CAILE	T2,UV$ACE+1-UV$ACT ;ONLY MOVE MAXIMUM AMOUNT
	MOVEI	T2,UV$ACE+1-UV$ACT ;QUEUE. UUO ALREADY DIS-ALLOWED 0 LENGTH
	BLT	S1,VALBLK+UV$ACT-1(T2) ;MOVE THE ACCOUNT STRING
	JRST	QUEVA1		;LOOK AT NEXT BLOCK
QUEVA3:	MOVEI	T1,VALBLK
	MOVEM	T1,DATADR	;THE VALIDATION ROUTINE LOOKS HERE FOR DATA
	$RETT

;SUBROUTINE TO PREPARE VALIDATION BLOCKS FOR QUEUE. FUNCTIONS
;CALL:	T1/MESSAGE TYPE TO FILL INTO THE BLOCK

PREVAL:	SETZM	VALZER		;ZERO THE BLOCKS
	MOVE	S1,[VALZER,,VALZER+1]
	BLT	S1,VALZND	;CLEAR THE WHOLE THING
	MOVEM	T1,VALBLK+UV$TYP ;STORE TYPE OF MESSAGE
	POPJ	P,		;AND RETURN
;QUEACC - ROUTINE TO EXTRACT ACCESS CONTROL INFORMATION FROM THE QUEUE. BLOCKS
;CALL:	T1/ FUNCTION CODE
;	MDBADR/ADDRESS OF MESSAGE DESCRIPTOR BLOCK
;	MMSADR/ADDRESS OF IPCF RECEIVE MESSAGE DATA
;	QUEBLK/POINT SO NEXT QUEUE. ARGUMENT BLOCK TO BE READ

QUEACC:
	PUSHJ	P,PREVAL	;PREPARE THE INTERNAL BLOCK
QUEACT:	MOVE	T1,MMSADR	;MESSAGE ADDRESS
	PUSHJ	P,GETBLK	;GET THE NEXT QUEUE. BLOCK
	JUMPF	QUEACX		;END OF BLOCK, BEGIN VALIDATION
QUEAC0:	CAIE	T1,.UGTYP	;IS THIS BLOCK THE ACCESS TYPE
	JRST	QUEAC1		;NO, LOOK AT NEXT BLOCK
	MOVE 	T3,(T3)		;GET TYPE ARGUMENT
	MOVEM	T3,ACOTYP	;STORE
	JRST	QUEACT		;LOOK AT THE NEXT BLOCK
QUEAC1:	CAIE	T1,.UGACT	;IS THIS BLOCK AN ACCOUNT?
	JRST	QUEAC2		;NO. SEE IF ITS A PPN BLOCK
	HRLZ	S1,T3		;BLT THE ACCOUNT INTO VALBLK
	HRRI	S1,VALBLK+UV$ACT
	CAILE	T2,UV$ACE+1-UV$ACT ;ONLY MOVE MAXIMUM AMOUNT
	MOVEI	T2,UV$ACE+1-UV$ACT ;QUEUE. UUO ALREADY DIS-ALLOWED 0 LENGTH
	BLT	S1,VALBLK+UV$ACT-1(T2) ;MOVE THE ACCOUNT STRING
	SETOM	VALACT		;NOTE THAT WE GOT HERE
	JRST	QUEACT		;LOOK AT NEXT BLOCK
QUEAC2:	CAIE	T1,.UGPPN	;IS IT A PPN?
	JRST	QUEAC3		;NO. SEE IF IT'S A PASSWORD BLOCK
	MOVEM	T1,ACOUXP	;INDICATE WE GOT A PPN
	MOVE	T3,(T3)		;GET THE PPN
	MOVEM	T3,VALBLK+UV$PPN ;STORE THE PPN
	MOVEM	T3,ACOPPN	;STASH HERE TOO
	JRST	QUEACT		;LOOK AT NEXT BLOCK
QUEAC3:	CAIE	T1,.UGPSW	;IS THIS BLOCK THE PASSWORD
	JRST	QUEAC4		;NO. SEE IF ITS A USERNAME
	HRLZ	S1,T3		;BLT THE PASSWORD INTO ACOPSW
	HRRI	S1,ACOPSW
	CAILE	T2,.APWLW	;MAKE SURE NOT LONGER THAN MAX
	MOVEI	T2,.APWLW	;MAKE MAXIMUM LENGTH IF SO
	BLT	S1,ACOPSW-1(T2)	;COPY THE PASSWORD
	JRST	QUEACT		;LOOK AT NEXT BLOCK
QUEAC4:	CAIE	T1,.UGUSR	;IS THIS BLOCK THE USERNAME
	JRST	QUEACT		;NOPE, IGNORE IT THEN
	MOVEM	T1,ACOUXP	;INDICATE WE GOT A USERNAME
	HRLZ	S1,T3		;BLT THE USERNAME INTO ACOPSW
	HRRI	S1,ACOUSR
	CAILE	T2,.AANLW	;MAKE SURE NOT LONGER THAN MAX
	MOVEI	T2,.AANLW	;MAKE MAXIMUM LENGTH IF SO
	BLT	S1,ACOUSR-1(T2)	;COPY THE NAME
	JRST	QUEACT

QUEACX:	MOVEI	T1,VALBLK
	MOVEM	T1,DATADR	;THE VALIDATION ROUTINE LOOKS HERE FOR DATA
	$RETT
;QUECUP - ROUTINE TO CONVERT A QUEUE. UUO UGCUP$ MESSAGE TO A NORMAL ONE

QUECUP:	MOVE	T2,QUEBLK	;POINTER TO NEXT DATA BLOCK
	SUBI	T2,.OHDRS	;BACK UP BY GALACTIC HEADER
	MOVE	T3,QUECNT	;NUMBER OF ARG BLOCKS REMAINING
	MOVEM	T3,.OARGC(T2)	;SETUP AS IF ORION HAD TOLD US
	HRRM	T1,(T2)		;STORE THE FUNCTION CODE
	MOVE	T3,DATADR	;GET MESSAGE ADDRESS
	MOVE	T3,1(T3)	;GET ACK CODE
	MOVEM	T3,1(T2)	;SAVE HERE
	SETZM	.OFLAG(T2)	;NO FLGS
	MOVEM	T2,DATADR	;SAVE FOR OTHERS
	$RETT			;RETURN
;QUEENT - ROUTINE TO CONVERT A QUEUE. UUO 'MAKE AN ENTRY' MESSAGE INTO A
;	FORMAT THE ACCOUNT DAEMON ALREADY KNOWS ABOUT (FORMAT IS CALLED A
;	DEFUS DATA LIST).
;CALL:	MDBADR/ADDRESS OF MESSAGE DESCRIPTOR BLOCK
;	MMSADR/ADDRESS OF IPCF RECEIVE MESSAGE DATA
;	QUEBLK/POINT SO NEXT QUEUE. ARGUMENT BLOCK TO BE READ

QUEENT:	$CALL	.SAVE1
	MOVE	T1,MMSADR	;GET THE TOTAL NUMBER OF QUEUE. BLOCKS
	MOVE	S1,.OARGC(T1)	;REBUILT MESSAGE WILL HAVE 2 WORDS/BLOCK,
	IMULI	S1,2
	ADDI	S1,QEXTRA	;SEE COMMENTS AT QEXTRA DEFINITION FOR EXTRA
				; SPACE NEEDED
	$CALL	M%GMEM		;GET SPACE NEEDED TO BUILD AN INTERNAL DEFUS LIST
	MOVEM	S1,QUELEN	;STORE THE LENGTH FOR RELEASING MEMORY
	MOVEM	S2,QUEADR	;STORE THE ADDRESS
	MOVEM	S2,DATADR
	MOVE	P1,S2
	MOVEI	T1,UGENT$	;"MAKE AN ENTRY" DISPATCH VALUE FOR ACTDSP
	MOVEM	T1,(P1)
	AOS	P1
	MOVE	T1,MMSADR	;GET THE NEXT QUEUE. BLOCK
	PUSHJ	P,GETBLK
	JUMPF	QUEEN3		;ILLEGAL MESSAGE IF NO MORE
	CAIE	T1,.QBAET	;BLOCK TYPE MUST BE ENTRY TYPE BLOCK
	JRST	QUEEN3		;OTHERWISE DECLARE IT AN ILLEGAL MESSAGE
	MOVE	T1,(T3)		;GET THE ENTRY TYPE
	MOVEM	T1,(P1)		;STORE IT
	AOS	P1		;STEP TO THE NEXT WORD
QUEEN1:	MOVE	T1,MMSADR
	PUSHJ	P,GETBLK	;GET THE NEXT QUEUE. BLOCK
	JUMPF	QUEEN2		;NO MORE BLOCKS. FINISH UP AND RETURN
	CAIN	T1,.USTAD	;CURRENT DATE/TIME IS ALWAYS PROVIDED BY ACTDAE
	JRST	QUEEN1		;GO READ NEXT BLOCK
	CAIN	T1,.USAMV	;AS IS THE ACCOUNT DAEMON'S VERSION NUMBER
	JRST	QUEEN1		;READ THE NEXT BLOCK
	TXO	T1,1B0		;HANDLE THE ZERO DEFUS CASE
	MOVEM	T1,(P1)		;STORE THE DEFUS NUMBER
	MOVEM	T3,1(P1)	;THIS IS THE ADDRESS WHERE THE DATA IS FOUND
	ADDI	P1,2		;COUNT THE TWO WORDS JUST FILLED
	JRST	QUEEN1		;READ THE NEXT QUEUE. BLOCK
QUEEN2:	PUSHJ	P,QEXFIL	;PROVIDE THE ACTDAE-ONLY DATA
	SETZM	(P1)		;MUST TERMINATE WITH A ZERO WORD
	$RETT
QUEEN3:	MOVE	S1,QUELEN	;MUST RETURN SPACE WE GOT FOR THE MESSAGE
	MOVE	S2,QUEADR	;...
	$CALL	M%RMEM		;GIVE IT BACK IF THE MESSAGE IS BAD
	$RETF			;AND PITCH THE MESSAGE
;QUEVLX - ROUTINE TO CONVERT VALIDATION MESSAGES
;CALL:	MDBADR/ADDRESS OF MESSAGE DESCRIPTOR BLOCK
;	MMSADR/ADDRESS OF IPCF MESSAGE DATA
;	QUEBLK/POINTS TO NEXT QUEUE. ARGUMENT BLOCK TO BE READ

QUEVLX:	MOVEI	T1,UGVAL$	;TYPE OF ACCOUNT MESSAGE
	PUSHJ	P,PREVAL	;PREPARE VALIDATION BLOCKS
	PUSHJ	P,GETBLK	;GET THE NEXT QUEUE. BLOCK
	$RETIF			;CHECK FOR ERRORS
	CAIE	T1,.QBAET	;ACCOUNTING ENTRY BLOCK?
	$RETF			;BAD MESSAGE FORMAT
	HRLZ	T2,T3		;POINT TO VALIDATION BLOCK IN MESSAGE
	HRRI	T2,VALBLK	;MAKE A BLT POINTER
	BLT	T2,VALBLK+UV$ACE ;COPY
	MOVEI	T1,VALBLK	;POINT TO OUR VALIDATION BLOCK
	MOVEM	T1,DATADR	;SAVE ADDRESS
	SETOM	MVBFLG		;REMEMBER TO MOVE THE WHOLE BLOCK
	$RETT			;RETURN
;QEXFIL - ROUTINE TO PROVIDE ACCOUNT-DAEMON-ONLY DATA IN THE INTERNAL DEFUS LIST.

QEXFIL:	MOVEI	T1,.USTAD	;DATE/TIME ENTRY IS MADE IS ALWAYS GIVEN BY ACTDAE
	MOVEM	T1,(P1)		;STORE ITS DEFUS NUMBER
	PUSHJ	P,DATIM		;FILL IN CURRENT DATE AND TIME
	MOVEI	T1,CURDTM	;ADDRESS WHERE THE DATE/TIME WILL BE STORED
	MOVEM	T1,1(P1)
	ADDI	P1,2		;ADJUST P1 FOR NEXT ITEM
	MOVEI	T1,.USAMV	;THE ACCOUNTING MODULE'S VERSION NUMBER
	MOVEM	T1,(P1)		;STORE THE DEFUS NUMBER
	MOVEI	T1,.JBVER	;ADDRESS WHERE IT'S STORED
	MOVEM	T1,1(P1)
	ADDI	P1,2		;ADJUST P1
	POPJ	P,
;GETBLK - ROUTINE TO FIND THE NEXT QUEUE. ARGUMENT BLOCK (MATCHES A$GBLK IN QSRADM.MAC)
;CALL:	T1/ THE MESSAGE ADDRESS
;RETURN	T1/ THE BLOCK TYPE
;	T2/ THE LENGTH OF THE DATA IN THE BLOCK
;	T3/ THE ADDRESS OF THE DATA IN THE BLOCK
;	FALSE IF NO MORE BLOCKS

GETBLK:	SKIPE	S1,QUEBLK	;GET THE BLOCK ADDRESS IF THERE IS ONE
	JRST	GETBL1		;NOT FIRST TIME THROUGH,,SO SKIP INITLZN
	MOVE	S1,.OARGC(T1)	;GET THE MESSAGE BLOCK COUNT
	MOVEM	S1,QUECNT	;AND SAVE IT
	LOAD	S1,.MSTYP(T1),MS.CNT ;GET THE MESSAGE LENGTH
	ADDI	S1,(T1)		;POINT OFF THE END
	MOVEM	S1,QUEEND	;SAVE FOR LIMIT COMPUTATIONS
	MOVE	S1,MDBADR	;GET DESCRIPTOR BLOCK ADDRESS
	LOAD	T2,MDB.MS(S1),MD.CNT ;GET MESSAGE SIZE
	LOAD	S1,MDB.MS(S1),MD.ADR ;AND ADDRESS
	ADD	S1,T2		;A DIFFERENT IDEA OF THE END
	CAMGE	S1,QUEEND	;IS THIS MORE RESTRICTIVE?
	MOVEM	S1,QUEEND	;YES, DON'T ILL MEM REF
	MOVEI	S1,.OHDRS+ARG.HD(T1)	;IF NOT,,GET THE FIRST ONE
GETBL1:	CAMGE	S1,QUEEND	;DON'T ADVANCE PAST THE END
	SOSGE	QUECNT		;CHECK THE BLOCK COUNT
	$RETF			;NO MORE,,JUST RETURN
	LOAD	T1,ARG.HD(S1),AR.TYP	;GET THE BLOCK TYPE
	LOAD	T2,ARG.HD(S1),AR.LEN	;GET THE BLOCK LENGTH
	MOVEI	T3,ARG.DA(S1)	;POINT TO THE ACTUAL DATA
	ADD	S1,T2		;POINT TO THE NEXT BLOCK
	MOVEM	S1,QUEBLK	;SAVE IT FOR THE NEXT TIME AROUND
	CAMG	S1,QUEEND	;BEYOND THE LIMIT?
	SOJA	T2,.RETT	;T2 NOW ONLY REFLECTS DATA LENGTH, GOOD RETURN
	$RETF			;MESSAGE IS BAD

;GETBLF - ROUTINE TO RETURN BLOCK TYPE & FLAGS
;CALL:	LIKE GETBLK
;RETURN:	T1 HAS THE BLOCK TYPE, MASKED DOWN TO A PROFILE ENTRY OFFSET
;		S2 HAS THE SELECTION FLAGS THAT WERE MASKED OFF OF T1

GETBLF:	PUSHJ	P,GETBLK	;GET THE BLOCK INFO
	$RETIF			;PROPAGATE FAILURE
	MOVE	S2,T1		;COPY BLOCK TYPE
	ANDI	T1,AF.OFS	;KEEP ONLY OFFSET HERE
	TRZ	S2,AF.OFS	;AND ONLY BITS HERE
	POPJ	P,		;RETURN GOODNESS


;FNDBLK - ROUTINE TO FIND ANY QUEUE. ARGUMENT BLOCK IN A MESSAGE
;CALL:	T1/ THE MESSAGE ADDRESS
;	T2/ THE TYPE OF BLOCK WE WANT
;RETURN	T1/ THE ADDRESS WHERE THE BLOCK STARTS (OR FALSE IF NOT FOUND)

FNDBLK:	$CALL	.SAVE2		;SAVE P1,P2
	LOAD	P1,.OARGC(T1)	;GET THE MESSAGE ARGUMENT COUNT
	MOVE	P2,T2		;SAVE THE BLOCK TYPE
	MOVEI	S1,.OHDRS(T1)	;POINT TO THE FIRST BLOCK
	LOAD	TF,.MSTYP(T1),MS.CNT	;GET THE MESSAGE LENGTH
	CAXLE	TF,PAGSIZ	;CAN'T BE GREATER THEN A PAGE
	$RETF			;ELSE THATS AN ERROR
	ADD	TF,T1		;POINT TO THE END OF THE MESSAGE
FNDBL1:	LOAD	S2,ARG.HD(S1),AR.TYP	;GET THIS BLOCK TYPE
	CAMN	S2,P2		;IS IT THE BLOCK HE WANTS ???
	JRST	[MOVE T1,S1	;YES, MOVE ADDRESS TO RETURN AC
		 $RETT]		;AND RETURN GOOD
	LOAD	S2,ARG.HD(S1),AR.LEN	;NO,,GET THIS BLOCKS LENGTH
	ADD	S1,S2		;POINT TO THE NEXT BLOCK
	CAIG	TF,0(S1)	;ARE WE STILL IN THE MESSAGE ???
	$RETF			;NO,,RETURN BLOCK NOT FOUND
	SOJG	P1,FNDBL1	;CONTINUE TILL DONE
	$RETF			;NOT FOUND
	SUBTTL	ACTVER - GENERAL DEFINITIONS FOR ACCOUNT VALIDATION MODULE


;ACCOUNT VALIDATION MODULE DEFINITIONS
PPN:	BLOCK 1			;PPN TO BE VALIDATED
PRJCHN:	BLOCK 1			;LH=CHANNEL # FOR PROJCT.SYS, RH=0
PRJBLK:	BLOCK 10		;FILOP. BLOCK FOR PROJCT.SYS
PROJCT:	BLOCK 36		;LOOKUP BLOCK
PRJBUF:	BLOCK PRJWPB		;BUFFER FOR READING PROJCT.SYS IN DUMP MODE
IOLIST:	BLOCK 2			;I/O LIST FOR READING PROJCT.SYS

Z.DATE:	BLOCK 1		;CREATION DATE/TIME OF PROJCT.SYS WHICH TABLE WAS BUILT FOR
Z.TADR:	BLOCK 1		;ADDRESS OF FIRST WORD OF TABLE IN LOW SEGMENT
Z.TLEN:	BLOCK 1		;LENGTH OF TABLE LAST READ (IN WORDS)
BLKNUM:	BLOCK 1		;LOCATION TO SAVE LAST BLOCK NUMBER READ FROM PROJCT.SYS
LSTBLK:	BLOCK 1		;BLOCK NUMBER OF LAST BLOCK WHERE ENTRIES ARE FOUND IN PROJCT.SYS
PRJIOW:	BLOCK 1		;NEGATIVE WORDS PER LOGICAL DISK BLOCK IN PROJCT.SYS
PRJMUL:	BLOCK 1		;MULTIPLIER FOR LOGICAL TO REAL DISK BLOCKS
PRJCON:	BLOCK 1		;CONSTANT FOR COMPUTING PHYS BLOCK FROM LOGICAL BLOCK
PRJVRS:	BLOCK 1		;VERSION NUMBER OF PROJCT.SYS WE ARE READING
ACKCOD:	BLOCK 1		;ACKNOWLEDGMENT CODE USED BY THE REQUESTOR
	SUBTTL	ACTVER - FORMAT OF PROJCT.SYS


BLKOFS==1	;OFFSET INTO THE BLOCK WHERE THE FIRST ENTRY IS
PPNOFS==1	;OFFSET INTO THE ENTRY WHERE PPN CAN BE FOUND
CNTOFS==2	;OFFSET FROM THE PPN WHERE THE CHARACTER COUNT OF THE
		; ACCOUNT STRING CAN BE FOUND
ACTOFS==3	;OFFSET FROM THE PPN WHERE THE ACCOUNT STRING CAN BE FOUND

;FOLLOWING IS THE FORMAT OF THE FIRST BLOCK OF PROJCT.SYS.  THIS BLOCK
;	CONTAINS FILE AND DATA INFORMATION.  THIS TOTAL BLOCK HAS BEEN
;	RESERVED SPECIFICALLY FOR THIS PURPOSE.

A.VERS==0	;(0) VERSION # OF FORMAT.  MUST AGREE WITH ACVERS
A.TLEN==1	;(1) LENGTH OF TABLE IN STORED IN PROJCT.SYS
A.FBLK==2	;(2) BLOCK NUMBER OF TABLE IN FILE
A.WPBL==3	;(3) NUMBER OF WORDS PER LOGICAL DISK BLOCK (PRJWPB)


;***************************************************************
;	This is the format of the first block of PROJCT.SYS
;***************************************************************

;	!=======================================================!
;	!            Version # of PROJCT.SYS format             !
;	!-------------------------------------------------------!
;	!         Length of table stored in PROJCT.SYS          !
;	!-------------------------------------------------------!
;	!          Block number of table in PROJCT.SYS          !
;	!-------------------------------------------------------!
;	!        Number of words per logical disk block         !
;	!=======================================================!

;***************************************************************
;	End of the first block of PROJCT.SYS
;***************************************************************



;***************************************************************
;	Format of table pointed to by third word of first block
;***************************************************************


;	!=======================================================!
;	!          First PPN found in first data block          !
;	!-------------------------------------------------------!
;	!               Block number where PPN is               !
;	!-------------------------------------------------------!
;	!         First PPN found in second data block          !
;	!-------------------------------------------------------!
;	!               Block number where PPN is               !
;	!-------------------------------------------------------!
;	\                                                       \
;	\                                                       \
;	\                                                       \
;	!-------------------------------------------------------!
;	!          First PPN found in last data block           !
;	!-------------------------------------------------------!
;	!               Block number where PPN is               !
;	!=======================================================!


;***************************************************************
;	End of table format
;***************************************************************



;***************************************************************
;	Format of a block of data pointed to by entries in the table
;***************************************************************


;	!=======================================================!
;	!          Number of words used in this block           !
;	!=======================================================!
;	!    Length of PPN entry    !  Length of account entry  !
;	!-------------------------------------------------------!
;	!                          PPN                          !
;	!-------------------------------------------------------!
;	!                    Wild card mask                     !
;	!-------------------------------------------------------!
;	!Character count in account !           Flags           !
;	!-------------------------------------------------------!
;	\                    Account string                     \
;	\                                                       \
;	\                                                       \
;	!-------------------------------------------------------!
;	!             0             !  Length of account entry  !
;	!-------------------------------------------------------!
;	!                          PPN                          !
;	!-------------------------------------------------------!
;	!                    Wild card mask                     !
;	!-------------------------------------------------------!
;	!Character count in account !           Flags           !
;	!-------------------------------------------------------!
;	\                    Account string                     \
;	\                                                       \
;	!=======================================================!
;	!    Length of PPN entry    !  Length of account entry  !
;	!-------------------------------------------------------!
;	!                          PPN                          !
;	!-------------------------------------------------------!
;	!                    Wild card mask                     !
;	!-------------------------------------------------------!
;	!Character count in account !           Flags           !
;	!-------------------------------------------------------!
;	\                    Account string                     \
;	\                                                       \
;	!-------------------------------------------------------!
;	!             0             !  Length of account entry  !
;	!-------------------------------------------------------!
;	!                          PPN                          !
;	!-------------------------------------------------------!
;	!                    Wild card mask                     !
;	!-------------------------------------------------------!
;	!Character count in account !           Flags           !
;	!-------------------------------------------------------!
;	\                    Account string                     \
;	\                                                       \
;	!=======================================================!



;***************************************************************
;	End of format of a block of data
;***************************************************************
	SUBTTL	ACTVER - MAIN VALIDATION ROUTINE

ACTVER:	$CALL	M%GPAG		;GET A PAGE FOR THE RESPONSE MESSAGE
	MOVEM	S1,SABADR	;STORE THE ADDRESS OF THE PAGE/PACKET
	MOVE	T2,DATADR	;GET THE ADDRESS OF THE MESSAGE DATA
	MOVE	T1,UV$ACK(T2)	;GET THE ACK CODE FOR THE REQUESTOR
	MOVEM	T1,ACKCOD	; IN CASE HE IS ASYNCRONOUS (USUALLY QUASAR)
	MOVE	T1,UV$PPN(T2)	;GET THE PPN TO BE VALIDATED
	MOVEM	T1,PPN		;SAVE IT FOR LATER
	JUMPG	T1,ACTVCM	;IS IT A LEGAL PPN?
	PUSHJ	P,ERROR2	;NO.
	JUMPF	ACTVE1		;ERROR MESSAGE IS ALREADY BUILT IN IPCF MESSAGE
ACTVCM:	MOVE	T1,STATE2	;GET THE SECOND STATES WORD
	TXNN	T1,ST%ACV	;IS VALIDATION REQUIRED?
	JRST	ACTVE1		;NO. GIVE A SUCCESSFUL RETURN
	MOVE	T2,MDBADR	;ADDRESS OF MESSAGE DESCRIPTOR BLOCK
	MOVE	T1,MDB.FG(T2)	;FLAGS OF THE MESSAGE
	TXNE	T1,IP.CFP	;HAS THE SENDER SET THE PRIV BIT?
	JRST	ACTVE5		;YES. ASSUME HE'S TRYING TO BREAK THE SYSTEM
	MOVE	T1,MDB.PV(T2)	;GET THE SENDER'S CPABILITIES
	TXNE	T1,MD.PWH!MD.POP ;IS THE SENDER J.ACCT'D OR A SYSTEM OPERATOR?
	JRST	ACTVE4		;YES. ALLOW ALL KINDS OF VALIDATION
	MOVE	T1,MDB.SD(T2)	;GET THE SENDER'S PPN
	CAMN	T1,PPN		;DOES HE WANT TO VALIDATE FOR HIMSELF?
	JRST	ACTVE4		;YES. ALLOW ONLY THAT
ACTVE5:	PUSHJ	P,ERROR3	;UNPRIVILEGED USER CANNOT VALIDATE FOR OTHER PPNS
	JUMPF	ACTVE8		;[153]
ACTVE4:	MOVE	T1,DATADR	;GET ADDRESS OF DATA
	ADDI	T1,UV$ACT	;BEGINNING ADDRESS OF ACCOUNT TO BE VALIDATED
	LDB	T1,[POINT 7,(T1),6]	;GET THE FIRST CHARACTER OF ACCOUNT
	JUMPN	T1,ACTVE6	;ALWAYS VALIDATE A NON-NULL ACCOUNT
	PUSHJ	P,CHKDEF	;SEE IF DEFAULT EXISTS FOR NULL ACCOUNT, THIS PPN
	JUMPT	ACTVE1		;THERE IS, MUST BE VALID, RETURN IT TO CALLER
	PUSHJ	P,CHKACT	;NO DEFAULT, IS VALIDATION REQUIRED?
	JUMPF	ACTVE1		;NOT REQUIRED

ACTVE6:	PUSHJ	P,CHKPRJ	;VALIDATE THE ACCOUNT
	SKIPF			;[153]
ACTVE1:	SKIPA	T3,[UGTRU$]	;[153] GET SUCCESS CODE
ACTVE8:	MOVEI	T3,UGFAL$	;[153] GET FAILURE CODE
	SKIPE	QUEFLG		;WAS THIS FROM A QUEUE. UUO?
	JRST	[JUMPF ACTVXT
		MOVE T1,VALBLK+UV$TYP ;WHAT DOES HE WANT BACK?
		CAIN T1,UGVRP$	;USER PROFILE?
		SKIPA T1,[-2]	;YES, "ERROR" -2, MOVE USER PROFILE
		SETO T1,	;GET A -1 (NOT REALLY AN ERROR NUMBER)
		AOSN  MVBFLG	;MOVE WHOLE VALIDATION BLOCK?
		MOVNI T1,3	;YES
		PUSHJ P,ERRQUE	;GENERATE NEGATIVE "ERROR"
		JRST	ACTVXT]	;RELEASE OLD MSG AND SEND QUEUE UUO RESPONSE
	MOVE	T2,SABADR	;[153] GET MESSAGE PAGE ADDRESS
	MOVEM	T3,UC$RES(T2)	;[153] SAVE VALIDATION RESPONSE
	MOVE	S1,DATADR	;GET DATA ADDRESS
	MOVEI	T1,UGVUP$	;GET VALIDATE ACCOUNT AND RETURN PROFILE CODE
	CAME	T1,UV$TYP(S1)	;WAS IT?
	JRST	ACTVE3		;[153] NO
	MOVEI	T1,UGFAL$	;[153] YES, ASSUME CANNOT GET PROFILE
	MOVEM	T1,UC$PRF(T2)	;[153]
        MOVE	T1,PPN		;YES, GET PPN
	PUSHJ	P,GETPRO	;FETCH PROFILE
	JUMPF	ACTVE3		;[153]
	MOVEI	T1,UGTRU$	;[153] GET SUCCESS CODE
	MOVEM	T1,UC$PRF(T2)	;[153] SAY THERE'S A PROFILE
	MOVEI	T1,UC$PRO(T2)	;GET TARGET ADDRESS
	HRLI	T1,ACOPRO	;GET START OF PROFILE
	BLT	T1,UC$PRE(T2)	;COPY PROFILE
ACTVE3:	CAIE	T3,UGTRU$	;[153] ACCOUNT VALIDATION SUCCESSFUL?
	JRST	ACTVXT		;[153] NO
	MOVE	T1,DATADR	;INCOMING MESSAGE
	HRLI	T1,UV$ACT(T1)	;ORIGINAL (OR MODIFIED) ACCOUNT STRING
	HRRI	T1,UC$ACT(T2)	;THE RESPONSE FIELD OF THE IPCF MESSAGE
	BLT	T1,UC$ACE(T2)	;RETURN IT TO THE SENDER
	CAIA			;DONE

ACTVXX::PUSHJ	P,UPDDSK	;PASWORD WAS (NOT) VALIDATED, SAVE DATE/TIME
ACTVXT::SKIPE	QUEFLG		;WAS THIS FROM A QUEUE. UUO?
	JRST	[PUSHJ P,FIXQUE	;YES, BUILD THE REST OF THE MESSAGE
		JRST ACTVE7]	;RELEASE MESSAGE AND SEND RESPONSE
	MOVE	T2,SABADR	;GET THE PAGE ADDRESS WE WANT TO SEND
	MOVEI	T1,UGVAC$	;GET THE MESSAGE TYPE
	MOVEM	T1,UC$TYP(T2)
	MOVE	T1,ACKCOD	;GET THE ACK CODE
	MOVEM	T1,UC$ACK(T2)	;STORE IT FOR THE REQUESTOR
ACTVE7:	SKIPN	SABFLG		;STILL NEED TO RELEASE THE MESSAGE?
	PUSHJ	P,IPCREL	;YES. DO IT AND REMEMBER IT

;HERE TO SEND RESPONSE POINTED TO BY SABADR TO THE USER OR THE MONITOR

RSPSAB:	MOVE	T1,MDBADR	;GET THE ADDRESS OF THE MDB
	MOVE	T1,MDB.SP(T1)	;GET THE PID OF THE SENDER
	MOVEM	T1,IPS.BL+SAB.PD;STORE IT IN THE SEND ARGUMENT BLOCK
	MOVEI	T1,1000		;MAXIMUM LENGTH OF MESSAGE
	MOVEM	T1,IPS.BL+SAB.LN
	MOVE	T1,SABADR	;ADDRESS OF DATA
	MOVEM	T1,IPS.BL+SAB.MS
	MOVEI	S1,SAB.SZ	;LENGTH OF SEND ARGUMENT BLOCK
	MOVEI	S2,IPS.BL	;ADDRESS OF SEND ARGUMENT BLOCK
	$CALL	C%SEND		;SEND THE MESSAGE
	JUMPT	.POPJ		;RETURN IF SEND WENT OK
	MOVE	T1,MDBADR	;MESSAGE DESCRIPTOR BLOCK
	MOVE	T2,MDB.SD(T1)	;SENDER'S PPN
	MOVE	T3,MDB.PV(T1)
	ANDX	T3,MD.PJB	;JOB NUMBER OF SENDER
	$WTOXX	<Error (^E/S1/) sending response to job ^D/T3/ user ^P/T2/>
	MOVE	S1,SABADR	;ADDRESS OF PAGE TO RETURN TO POOL
	$CALL	M%RPAG		;RETURN THE PAGE
	$RETT

;SUBROUTINE TO MAKE THE MESSAGE POINTED TO BY SABADR INTO A QUEUE. UUO RESPONSE

FIXQUE:	MOVE	T2,SABADR	;GET ADDRESS OF MESSAGE TO SEND
	LOAD	T1,.MSTYP(T2),MS.CNT	;GET THE CURRENT WORD COUNT
	ADDI	T1,.OHDRS	;ADD IN THE MESSAGE HEADER LENGTH
	STORE	T1,.MSTYP(T2),MS.CNT
	MOVEI	T1,.OMTXT	;MESSAGE TYPE TO RESPOND TO QUEUE. UUO
	STORE	T1,.MSTYP(T2),MS.TYP
	MOVE	T3,MMSADR	;ADDRESS OF MESSAGE QUEUE. UUO SEND US
	MOVE	T3,.MSCOD(T3)	;GET THE ACK CODE SEND TO US
	MOVEM	T3,.MSCOD(T2)	;MAKE SURE THE RIGHT USER GETS IT
	$RETT


	;ROUTINE TO UPDATE DISK WITH RECORD IN ACOPRO TO INDICATE THAT
	;PASSWORD VALIDATION DID (NOT) SUCCEED.  BE SURE WE GET HERE ONLY
	;IF BUFFER IS VALID, ELSE RMS WILL HAPPILY REPLACE THE CURRENT REC...

UPDDSK:	MOVEI	S1,ACOPRO	;POINT AT THE PROFILE
	PUSHJ	P,UPDA##	;UPDATE FILE "A"
	$RET			;NOT MUCH WE CAN DO
ACTACC:	SKIPN	QUEFLG		;ONLY DEFINED FROM QUEUE. UUO
	JRST	IGNORE		;WASN'T. RELEASE MESSAGE AND IGNORE IT
	$CALL	M%GPAG		;GET A PAGE FOR THE RESPONSE MESSAGE
	MOVEM	S1,SABADR	;STORE THE ADDRESS OF THE PAGE/PACKET
	PUSHJ	P,CVTWLD	;CONVERT TO WILDCARD BLOCK (S2 GETS ADDR)
	MOVEI	S1,ACOPRO	;WHERE TO READ THE RECORD
	PUSHJ	P,PROFIL	;FETCH PROFILE
	JUMPF	[SKIPN	S1,RMGCOD ;CHECK FOR FATAL RMS ERRORS
		 JRST	ACTACA	;NOT FATAL--NO SUCH USER
		 PJRST	RMGERX]	;FATAL--SEND RESPONSE
	MOVEI	S2,ACOPRO	;POINT TO THE RETURNED PROFILE
	MOVE	S1,.AEPPN(S2)	;GET THE PPN
	MOVEM	S1,ACOPPN	;SAVE IN PPN PLACE
	MOVE	T2,DATADR	;GET THE ADDRESS OF THE MESSAGE DATA
	MOVE	T1,UV$ACK(T2)	;GET THE ACK CODE FOR THE REQUESTOR
	MOVEM	T1,ACKCOD	; IN CASE HE IS ASYNCRONOUS (USUALLY QUASAR)
	MOVE	T1,ACOPPN	;GET THE PPN TO BE VALIDATED
	MOVEM	T1,UV$PPN(T2)	;STORE IT HERE
	MOVEM	T1,PPN		;SAVE IT FOR LATER
	JUMPG	T1,ACTAC1	;IS IT A LEGAL PPN?

ACTACA:	PUSHJ	P,ERROR2	;NO.
	JUMPF	ACTVXT		;ERROR MESSAGE IS ALREADY BUILT IN IPCF MESSAGE

ACTAC1:	MOVEI	T1,ACOPRO	;POINT TO PROFILE
	MOVE	S1,ACOTYP	;GET TYPE OF CHECK TO MAKE
	CAIN	S1,UG.SPV	;SPRINT TYPE CHECK?
	SKIPA	T2,[AE.PRB]	;SPRINT, GET PASSWORD FOR BATCH BIT
	MOVX	T2,AE.PRT	;NORMAL, GET PASSWORD FOR TS BIT
	TDNN	T2,.AEREQ(T1)	;SEE IF PASSWORD IS REQUIRED
	PJRST	ACTVCM		;NO, SKIP THIS, GO CHECK ACCOUNT STRING
	MOVEI	S1,ACOPSW	;POINT TO THE PASSWORD
	MOVE	S2,T1		;POINT TO BUFFER
	$CALL	CHKPSW##	;CHECK THE PASSWORD
	MOVEI	S1,ACOPRO	;POINT TO THIS PROFILE
	PUSHJ	P,FIXVLD	;UPDATE VALIDATION STATUS
;	JUMPT	ACTVCM		;PASSWORD MATCHES, CHECK ACCOUNT STRING
	JUMPT	[PUSHJ	P,UPDDSK;UPDATE DISK RECORD
		 JRST	ACTVCM]	;DONE
ILLPSW:	MOVEI	T1,ACPSW%	;CALL WITH PROPER ERROR CODE
	PUSHJ	P,LOGUSR	;LOG INITIAL USER STUFF
	$TEXT	(LOGFAI,<^O6R0/ACOPPN,LHMASK/^O6R0/ACOPPN,RHMASK/>)
	PUSHJ	P,FAIOUT	;FORCE BUFFERS OUT
	MOVE	S1,ACOPRO+.AEPPN ;GET PPN
	FATAL	(PSW,<Invalid password for ^U/S1/>,ACPSW%,ACTVXX)
	SUBTTL	PROCESSOR FOR "OBTAIN USER PROFILE" QUEUE. FUNCTION

ACTOUP:	SKIPN	QUEFLG		;ONLY DEFINED FROM QUEUE. UUO
	JRST	IGNORE		;WASN'T. RELEASE MESSAGE AND IGNORE IT
	$CALL	M%GPAG		;GET A PAGE FOR THE RESPONSE MESSAGE
	MOVEM	S1,SABADR	;STORE THE ADDRESS OF THE PAGE/PACKET
	PUSHJ	P,CVTWLD	;CONVERT TO WILDCARD BLOCK (S2 GETS ADDR)

ACTOU0:	MOVEM	S2,ACOPTR	;SAVE POINTER TO WILDCARD BLOCK
	MOVEI	S1,ACOPRO	;WHERE TO READ THE RECORD
	PUSHJ	P,PROFIL	;FETCH PROFILE
	MOVEI	S2,ACOPRO	;POINT TO THE RETURNED PROFILE
	MOVE	S1,.AEPPN(S2)	;GET THE PPN
	MOVEM	S1,ACOPPN	;STASH WHERE WE CAN FIND IT
	JUMPF	[SKIPN	S1,RMGCOD ;CHECK FOR FATAL RMS ERRORS
		 JRST	ACTOU1	;NOT FATAL--NO SUCH USER
		 PJRST	RMGERX]	;FATAL-SEND RESPONSE
	PUSHJ	P,CHKOWN	;IS OWNER REQUESTING PROFILE?
	JUMPT	ACTOU2		;JUMP IF WE FOUND THE PPN

ACTOU1:	MOVE	T1,ACOPTR	;GET ADDRESS OF WILDCARD BLOCK
	MOVE	T2,[POINT 8,ACOACK] ;AND TO ACK TEXT
	PUSHJ	P,A$WACK##	;GENERATE A WILDCARD ACK
	MOVE	T1,ACOPTR	;GET ADDR AGAIN
	MOVE	T2,[POINT 8,ACOACK] ;BYTE POINTER TO ACK TEXT
	SKIPE	UW$FND(T1)	;WAS AT LEAST ONE PROFILE FOUND?
	FATAL	(NAU,<No additional users matching ^Q/T2/>,ACNAU%,ACTVXT)
	MOVEI	T3,[ASCIZ /found/]
	HLRZ	T4,ACOWLD+UW$FND ;GET COUNT OF PROFILES FOUND
	PUSHJ	P,A$SWLD##	;GENERATE SUMMARY TEXT
	FATAL	(NUS,<^T/(S1)/>,ACNUS%,ACTVXT)

ACTOU2:	MOVEI	S2,ACOPRO	;SET UP POINTER TO PROFILE BLOCK
	MOVX	S1,AE.LOK	;GET FILE IS LOCKED BIT
	SKIPE	ACTLCK##	;IS FILE LOCKED?
	IORM	S1,ACOPRO+.AEFLG ;YES, LITE IN PROFILE
	MOVEI	S1,ACOPRO	;GET ADDRESS OF PROFILE
	PUSHJ	P,A$FSCD##	;GET SCHEDULAR CLASSES FROM SCDMAP
	MOVE	T2,DATADR	;GET THE ADDRESS OF THE MESSAGE DATA
	MOVE	T1,UV$ACK(T2)	;GET THE ACK CODE FOR THE REQUESTOR
	MOVEM	T1,ACKCOD	; IN CASE HE IS ASYNCRONOUS (USUALLY QUASAR)
	MOVE	T1,ACOPPN	;GET PPN WE WERE ASKED FOR
	MOVEM	T1,UV$PPN(T2)	;STASH HERE
	MOVEM	T1,PPN		;SAVE IT FOR LATER
	MOVNI	T1,2		;GET A -2 (NOT REALLY AN ERROR NUMBER)
	PUSHJ	P,ERRPRO	;GENERATE "ERROR" -2 (MOVE ACTDAE.SYS ENTRY)
	PJRST	ACTVXT		;FINISH RESPONSE TO QUEUE. AND RETURN
; CONVERT OLD-STYLE "GET PROFILE" CALL TO NEW-STYLE WILDCARD CALL
CVTWLD:	PUSHJ	P,.SAVE2	;SAVE P1 AND P2
	MOVEI	P1,ACOWLD	;POINT TO INTERNAL WILDCARD BLOCK
	MOVSI	S1,0(P1)	;START ADDRESS
	HRRI	S1,1(P1)	;MAKE A BLT POINTER
	SETZM	(P1)		;CLEAR FIRST WORD
	BLT	S1,UW$MIN-1(P1)	;CLEAR ENTIRE BLOCK
	MOVSI	S1,UW$MIN	;LENGTH
	MOVEM	S1,UW$TYP(P1)	;SAVE
	MOVE	S1,ACOTYP	;GET TYPE FIELD
	MOVE	S2,ACOUXP	;FIND OUT WHAT WE WERE GIVE
	CAIN	S2,.UGUSR	;USER NAME?
	JRST	CVTWL3		;YES

; PPN
CVTWL1:	SETZM	UW$WST(P1)	;SET WILDCARD SEARCH TYPE TO PPN
	CAIE	S1,UG.NXT	;WANT NEXT PROFILE?
	SKIPA	S1,ACOPPN	;PPN
	TDZA	S1,S1		;WILD PPN
	SKIPA	S2,[EXP -1]	;NON-WILD MASK
	TDZA	S2,S2		;WILD MASK
	TDZA	P2,P2		;NON-WILD PREVIOUS RESULT
	MOVE	P2,ACOPPN	;WILD PREVIOUS RESULT
	JUMPE	P2,CVTWL2	;ONWARD IF STARTING FROM FIRST PPN IN FILE
	TRNN	P2,-1		;ELSE HAVE A PROGRAMMER NUMBER?
	SOS	P2		;NO--BACK OFF ONE

CVTWL2:	MOVEM	S1,UW$PPN(P1)	;SAVE TARGET PPN
	MOVEM	S2,UW$PPM(P1)	;SAVE MASK
	MOVEM	P2,UW$BRE(P1)	;SAVE PREVIOUS RESULT
	JRST	CVTWL5		;FINISH UP

; NAME
CVTWL3:	MOVSI	S2,ACOUSR	;POINT TO NAME
	CAIN	S1,UG.NXT	;WANT NEXT?
	JRST	CVTWL4		;YES
	HRRI	S2,UW$NAM(P1)	;POINT TO STORAGE
	BLT	S2,UW$NAM+.AANLW-1(P1) ;COPY
	MOVEI	S2,2		;CODE
	MOVEM	S2,UW$WST(P1)	;SET WILDCARD SEARCH TYPE NON-WILD NAME
	JRST	CVTWL5		;FINISH UP

CVTWL4:	HRRI	S2,UW$BRE(P1)	;POINT TO STORAGE
	BLT	S2,UW$BRE+.AANLW-1(P1) ;COPY
	MOVSI	S2,(BYTE(8)"*",0) ;FAKING WILDCARD
	MOVEM	S2,UW$NAM(P1)	;SAVE IN MESSAGE
	MOVEI	S2,1		;CODE
	MOVEM	S2,UW$WST(P1)	;SET WILDCARD SEARCH TYPE TO WILD NAME

CVTWL5:	MOVE	S2,P1		;RETURN ADDRESS OF WILDCARD BLOCK
	POPJ	P,		;RETURN
WLDACK:	PUSHJ	P,.SAVE1	;SAVE P1
	MOVE	P1,[POINT 8,ACOWLD+UW$NAM] ;POINT TO NAME
	MOVE	P2,[POINT 8,ACOACK] ;POINT TO ACK BLOCK
	MOVEI	P3,.AANLC	;NUMBER OF CHARACTERS

WLDAC1:	ILDB	S1,P1		;GET A CHARACTER
	IDPB	S1,P2		;PUT A CHARACTER
	SKIPE	S1		;DONE?
	SOJG	P3,WLDAC1	;LOOP IF MORE CHARACTERS
	POPJ	P,		;RETURN
;ACTCUP - Change a user profile
;Call
;	ACOPRV/ Non-zero if invoking privs
;Return
;	RETF	Unprived or other problem
;	RETT	Change made


ACTCUP:	$CALL	M%GPAG		;GET A PAGE FOR THE RESPONSE MESSAGE
	MOVEM	S1,SABADR	;STORE THE ADDRESS OF THE PAGE/PACKET
	SKIPE	ACTLCK##	;IS THE FILE LOCKED?
	FATAL	(AFL,<Accounting file is locked>,ACAFL%,ACTVXT)
	MOVE	T1,MMSADR	;BASE MESSAGE ADDRESS
	PUSHJ	P,GETBLK	;MAKE SURE WE HAVE A FIRST BLOCK
	JUMPF	CUP.EF		;FORMAT ERROR IF NOT
	SOS	T3		;YES, POINT TO BASE OF BLOCK
	AOS	T1,QUECNT	;AND GET TOTAL COUNT OF BLOCKS
	MOVEM	T1,CUPCNT	;STORE FOR TRANSACTION PROCESSING
	MOVEM	T3,CUPLIS	;REMEMBER START OF LIST
	MOVEM	T3,QUEBLK	;AND RESTORE IT FOR PREPROCESSING
	MOVEI	T1,UGCUP$	;FUNCTION BEING PERFORMED
	PUSHJ	P,PREVAL	;PRESET THE VALIDATION BLOCK
	SETZM	CUPMEM		;ADDITIONAL DYNAMIC MEMORY
	MOVE	S1,[CUPMEM,,CUPMEM+1]
	BLT	S1,CUPZND	;CLEAR IT OUT
	MOVSI	S1,UW$DAT	;LENGTH OF WILD BLOCK WITH NO SELECTIONS
	MOVEM	S1,CUPWLD	;INIT TO NO SELECT BLOCKS
	$FALL	CUP.1		;OK, ENTER PARSE LOOP ON NEXT PAGE
CUP.1:	PUSHJ	P,GETBLF	;GET BLOCK, SEPARATING TYPE & FLAGS
	JUMPF	CUP.3		;TIME TO PROCESS AT END OF LIST
	CAIL	T1,.AEMIN	;IN RANGE?
	JRST	CUP.EF		;FORMAT ERROR IF NOT
	TXNN	S2,AF.SEL	;DOING ANY SELECTION WITH THIS BLOCK?
	JRST	CUP.2		;NO, CHECK MODIFIERS
	LOAD	S1,S2,AF.SEL	;YES, GET THE TYPE CODE
	TXNN	S2,AF.DEF	;MUST BE TESTING THE VALUE
	CAIE	S1,.AFAND	;VIA AN 'AND' SELECT
	JRST	CUP.10		;NO TO EITHER, JUST STORE THE BLOCK
	CAIE	T1,.AEPPN	;SELECTING ON THE PPN?
	JRST	CUP.11		;NO, KEEP LOOKING
	CAIL	T2,1		;YES, IS THE BLOCK
	CAILE	T2,2		; OF A VALID LENGTH?
	JRST	CUP.EF		;NO, COMPLAIN OF FORMAT ERROR
	SKIPE	CUPPPN		;HAVE WE ALREADY SEEN A PPN BLOCK?
	JRST	CUP.EF		;YES, COMPLAIN OF FORMAT ERROR
	MOVEI	S1,-1(T3)	;GET BASE ADDRESS OF BLOCK
	MOVEM	S1,CUPPPN	;STORE FOR PROFILE FETCHING
	JRST	CUP.1		;PARSE ENTIRE MESSAGE

CUP.11:	CAIE	T1,.AENAM	;SELECTING ON USERNAME?
	CAIN	T1,.AENAM+1	;OR FORCED NON-WILD?
	CAIA			;YES, CHECK IT
	JRST	CUP.12		;NO, KEEP LOOKING
	SKIPE	CUPNAM		;HAVE WE SEEN A NAME ALREADY?
	JRST	CUP.EF		;YES, COMPLAIN OF FORMAT ERROR
	MOVEI	S1,-1(T3)	;POINT TO BASE ADDRESS
	MOVEM	S1,CUPNAM	;SAVE FOR LATER
	JRST	CUP.1		;PARSE ENTIRE MESSAGE

CUP.12:	CAIE	T1,.AEPSW	;SELECTING ON PASSWORD?
	JRST	CUP.10		;NO, JUST ADD TO SELECT BLOCK
	SKIPE	CUPPWD		;YES, IS THIS A DUPLICATE?
	JRST	CUP.EF		;FORMAT ERROR IF SO
	MOVEI	S1,-1(T3)	;POINT TO BASE ADDRESS
	MOVEM	S1,CUPPWD	;SAVE FOR ACCESS CHECKING
	JRST	CUP.1		;PARSE ENTIRE MESSAGE

CUP.10:	MOVEI	T1,-1(T3)	;POINT TO BASE ADDRESS OF BLOCK
	AOS	T2		;AND MAKE LENGTH REFLECT REALITY
	PUSHJ	P,CUP.S1	;INSERT INTO SELECTION LIST OF WILDCARD BLOCK
	JRST	CUP.1		;PARSE ENTIRE MESSAGE

CUP.SL:	LDB	T2,[POINT 9,(T1),17] ;GET BLOCK LENGTH
	HRRZ	S2,(T1)		;AND FLAGS
CUP.S1:	TXNE	S2,AF.DEF	;IF SELECTING BASED ON .AEMAP,
	MOVEI	T2,1		;FORGET THE VALUE
	SKIPN	S1,CUPSEL	;IS THIS FIRST INSERTION TO THE BLOCK?
	MOVEI	S1,CUPWLD+UW$DAT ;YES, POINT TO START OF SELECTION DATA
	HRLI	S1,(T1)		;MAKE TRANSFER WORD
	HRRZ	S2,S1		;COPY DESTINATION POINTER
	ADD	S2,T2		;POINT ONE BEYOND DESTINATION BLOCK
	MOVEM	S2,CUPSEL	;SAVE FOR NEXT TIME
	BLT	S1,-1(S2)	;MOVE THE DATA
	AOS	CUPWLD+UW$SEL	;UPDATE THE COUNT OF SELECTION BLOCKS
	CAIN	T2,1		;IF SELECTING ON .AEMAP,
	DPB	T2,[POINT 9,-1(S2),17] ;UPDATE BLOCK LENGTH IN LIST
	SUBI	S2,CUPWLD	;GET CURRENT LENGTH OF WILDCARD BLOCK
	HRLM	S2,CUPWLD	;SET LENGTH IN HEADER FOR ACTRMS
	POPJ	P,		;RETURN
CUP.2:	CAIE	T1,.AEDEF	;WANT TO CHANGE THE DEFAULT PPN?
	JRST	CUP.21		;NO, KEEP LOOKING
	CAIL	T2,1		;DEMAND ONE WORD
	CAIL	T2,3		;AND NOT MORE THAN TWO,
	JRST	CUP.EF		;ELSE IS FORMAT ERROR
	TXNE	S2,AF.DEF	;AND NOT BEING DEFAULTED
	JRST	CUP.EF		;ELSE IS FORMAT ERROR
	MOVE	S1,(T3)		;GET VALUE
	MOVEM	S1,CUPDEF	;SAVE FOR LATER
	JRST	CUP.1		;EXAMINE ALL MESSAGE BLOCKS

CUP.21:	CAIE	T1,.AEPPN	;WANT TO CHANGE THE PPN?
	JRST	CUP.22		;NO, KEEP LOOKING
	TXNE	S2,AF.DEF	;CAN'T SET PPN TO DEFAULT
	JRST	CUP.EF		;COMPLAIN OF FORMAT ERROR
	CAIN	T2,1		;MUST BE JUST ONE DATA WORD
	SKIPE	CUPPPM		;SECOND PPN BLOCK FOUND?
	JRST	CUP.EF		;ALSO A FORMAT ERROR
	MOVEI	S1,-1(T3)	;POINT TO BASE OF BLOCK
	MOVEM	S1,CUPPPM	;REMEMBER PPN BEING MODIFIED
	MOVE	S1,(T3)		;GET PPN BEING SET
	SKIPGE	S1,(T3)		;GET PPN BEING SET
	JRST	CUP.EI		;INVALID IF NOT POSITIVE
	JRST	CUP.1		;ELSE LET IT THROUGH

CUP.22:	CAIE	T1,.AENAM	;WANT TO CHANGE THE NAME?
	JRST	CUP.23		;NO, KEEP LOOKING
	SKIPN	CUPNMM		;MUST HAVE ONLY ONE
	TXNE	S2,AF.DEF	;CAN'T SET NAME TO DEFAULT
	JRST	CUP.EF		;FORMAT ERROR IF TRIED
	MOVEI	S1,-1(T3)	;POINT TO BASE OF BLOCK
	MOVEM	S1,CUPNMM	;SAVE NAME MODIFIER BLOCK
	JRST	CUP.1		;PARSE ENTIRE MESSAGE

CUP.23:	CAIE	T1,.AEPSW	;CHANGING THE PASSWORD?
	JRST	CUP.24		;NO, KEEP LOOKING
	SKIPN	CUPPWM		;MUST HAVE ONLY ONE
	TXNE	S2,AF.DEF	;CAN'T SET TO DEFAULT
	JRST	CUP.EF		;FORMAT ERROR EITHER WAY
	MOVEI	S1,-1(T3)	;POINT TO BASE OF BLOCK
	MOVEM	S1,CUPPWM	;SAVE PASSWORD MODIFIER ADDRESS
	HRLI	T3,(POINT 8)	;MAKE BYTE POINTER TO SUPPLIED PASSWORD
	IMULI	T2,.APWCW	;CHARACTER COUNT
	CAILE	T2,.APWLC	;TOO MANY?
	MOVEI	T2,.APWLC	;TRUNCATE
	MOVE	T1,[POINT 8,CUPPWB] ;PLACE TO STORE THE PASSWORD
	PUSHJ	P,CUPSTR	;MOVE THE STRING
	JRST	CUP.1		;PARSE ENTIRE MESSAGE

CUP.24:	CAIE	T1,.AEVRS	;CHANGING THE VERSION NUMBER?
	JRST	CUP.20		;NO, JUST VALIDATE
	SKIPN	CUPVRS		;MUST HAVE ONLY ONE
	TXNE	S2,AF.DEF	;AND NOT DEFAULTED
	JRST	CUP.EF		;OR IS FORMAT ERROR
	CAIE	T2,2		;MUST HAVE EXACTLY TWO DATA WORDS
	JRST	CUP.EF		;FORMAT ERROR OTHERWISE
	MOVX	S1,AE.VRS	;VERSION MASK
	CAME	S1,1(T3)	;MAKE SURE RIGHT FIELD IS MODIFIED
	JRST	CUP.EF		;FORMAT ERROR IF NOT
	LOAD	S1,(T3),AE.VRS	;GET VALUE TO SET
	CAIE	S1,%AECVN	;IS IT IN PHASE WITH OURS?
	FATAL	(WFV,<Wrong format version specified>,ACWFV%,ACTVXT)
	SETOM	CUPVRS		;WE GOT THE VERSION WORD
	JRST	CUP.1		;PARSE ENTIRE MESSAGE

CUP.20:	MOVE	S1,CHGTAB##(T1)	;GET CONTROL BITS
	SKIPN	ACOPRV		;PRIVED?
	TXNE	S1,PD.UNP	;NO, DO WE NEED TO BE?
	CAIA			;NO, SKIP ON
	JRST	CUP.E3		;YES, GIVE PRIVILEGE ERROR
	TXNE	S1,PD.NMD	;IS IT LEGAL TO MODIFY THIS FIELD AT ALL?
	FATAL	(FNM,<Value at offset ^O/T1/ is not modifiable>,ACFNM%,ACTVXT)
	TXNE	S1,PD.CND	;CAN IT BE DEFAULTED?
	TXNN	S2,AF.DEF	;NO, ARE WE ATTEMPTING IT?
	AOSA	CUPMFC		;NO, COUNT ANOTHER FIELD TO MODIFY
	FATAL	(FND,<Value at offset ^O/T1/ is not defaultable>,ACFND%,ACTVXT)
	LOAD	T4,S1,PD.WRD	;GET MAXIMUM BLOCK LENGTH
	TXNE	S1,PD.MSK	;IF MASKABLE,
	AOS	T4		;ALLOW ANOTHER
	CAILE	T2,(T4)		;IS THE SUPPLIED BLOCK TOO LONG?
	JRST	CUP.EF		;YES, GIVE A FORMAT ERROR
	TXNN	S2,AF.DEF	;UNLESS DEFAULTING,
	JUMPE	T2,CUP.EF	;REQUIRE SOME DATA
	JRST	CUP.1		;OK, PARSE REST OF MESSAGE
CUP.3:	SKIPN	CUPVRS		;DID WE GET A VALID VERSION CHANGE WORD?
	JRST	CUP.EF		;FORMAT ERROR IF NOT
	SKIPN	T1,CUPPPM	;ARE WE TRYING TO CHANGE THE PPN?
	JRST	CUP.31		;NO, DON'T WORRY ABOUT IT
	SKIPN	ACOPRV		;MUST BE PRIVED FOR THESE OPERATIONS
	JRST	CUP.E3		;ERROR IF NOT
	SKIPN	1(T1)		;MODIFYING IT TO ZERO?
	JRST	[SETOM	CUPDEL	;YES, THAT'S HOW WE DELETE THINGS
		 JRST	CUP.31]	;CHECK OTHER FIELDS
	SKIPN	CUPWLD+UW$SEL	;NO, ADDING, MUST HAVE NO SELECT BLOCKS
	SKIPE	CUPPPN		;OF ANY KIND
	JRST	CUP.EF		;FORMAT ERROR IF SO
	SKIPN	CUPNAM		;CHECK OTHER SELECT BLOCKS
	SKIPE	CUPPWD		;FOR ABSENCE
	JRST	CUP.EF		;FORMAT ERROR IF PRESENT
	JRST	CUP.AE		;ALL IS COPASETIC, GO ADD AN ENTRY

CUP.31:	SKIPN	CUPNAM		;WANT TO SELECT BY NAME?
	JRST	CUP.33		;NO, SKIP NAME STUFF, CHECK FOR PPN
	SKIPE	T1,CUPPPN	;YES, WAS A PPN BLOCK ALSO GIVEN?
	PUSHJ	P,CUP.SL	;YES, ADD IT TO THE SELECTION DATA
	SETZM	CUPPPN		;MAKE SURE IT DOESN'T CONFUSE US IN THE FUTURE
	MOVE	T1,CUPNAM	;GET NAME SELECTION BLOCK
	MOVE	S1,(T1)		;GET OVERHEAD WORD
	ANDI	S1,AF.OFS	;KEEP .AENAM OR .AENAM+1
	SUBI	S1,.AENAM-1	;MAKE 1 OR 2
	MOVEM	S1,CUPWLD+UW$WST ;SETUP WILDCARD SEARCH TYPE
	MOVE	T2,[POINT 8,1(T1)] ;POINT TO SUPPLIED NAME
	MOVE	T3,[POINT 8,CUPWLD+UW$NAM] ;POINT TO NAME FIELD OF WILD BLOCK
	LDB	S1,[POINT 9,(T1),17] ;GET WORD LENGTH OF BLOCK
	SUBI	S1,1		;OFFSET FOR OVERHEAD WORD
	IMULI	S1,.APWCW	;TIMES CHARACTER PER WORD
	CAILE	S1,.APWLC	;WITHIN CHARACTER LENGTH LIMIT?
	MOVEI	S1,.APWLC	;NO, TRUNCATE
	MOVE	T4,S1		;KEEP INITIAL SPACE COUNT
	JUMPLE	S1,CUP.EF	;FORMAT ERROR IF NULL NAME

CUP.32:	LDB	S2,T2		;GET A SOURCE BYTE
	DPB	S2,T3		;COPY TO WILDCARD BLOCK
	SKIPE	S2		;DONE IF NULL
	SOJG	S1,CUP.32	;LOOP OVER ALL CHARACTERS IN MESSAGE BLOCK
	SUB	T4,S1		;FIND NUMBER OF CHARACTERS TRANSFERRED
	JUMPE	T4,CUP.EF	;FORMAT ERROR IF NULL NAME
	JRST	CUP.34		;GOT A NAME, SKIP THE PPN CHECKING

CUP.33:	SKIPN	T1,CUPPPN	;MUST HAVE A PPN FOR SELECTION IF NO NAME
	JRST	CUP.EF		;FORMAT ERROR IF NOT
	LDB	T2,[POINT 9,(T1),17] ;GET TOTAL BLOCK LENGTH
	MOVE	S1,1(T1)	;GET PPN FROM MESSAGE
	CAIL	T2,3		;IF MASK WAS SUPPLIED,
	SKIPA	S2,2(T1)	;THEN USE IT,
	SETO	S2,		;ELSE ASSUME NOT WILD
	DMOVEM	S1,CUPWLD+UW$PPN ;SETUP TO SEARCH BASED ON PPN
	$FALL	CUP.34		;CONTINUE ON NEXT PAGE
CUP.34:	SKIPE	ACOPRV		;DO WE HAVE PRIVS?
	JRST	CUP.35		;YES, DON'T NEED TO CHECK HERE
	SKIPE	CUPPWM		;IF MODIFYING PASSWORD,
	SKIPE	CUPPWD		;MUST HAVE OLD ONE
	CAIA			;OK
	JRST	CUP.EP		;PASSWORD ERROR
	MOVEI	S1,2		;GET NON-WILD NAME FLAG
	SKIPE	CUPWLD+UW$WST	;DOING A NAME PARSE?
	MOVEM	S1,CUPWLD+UW$WST ;YES, FORCE NON-WILD
	SKIPN	CUPWLD+UW$WST	;SEARCHING BY NAME?
	JRST	CUP.35		;YES, DONE CHECKING
	SETO	S1,		;NO, GET A -1
	CAME	S1,CUPWLD+UW$PPM ;CHECK FOR NON-WILD PPN MASK
	JRST	CUP.E3		;NOT PRIVILEGED

CUP.35:	SKIPN	T3,CUPPWD	;DO WE HAVE A PASSWORD?
	JRST	CUP.4		;NO, START MODIFYING
	LDB	T2,[POINT 9,(T3),17] ;YES, GET BLOCK LENGTH
	SOS	T2		;WANT ONLY DATA LENGTH
	IMULI	T2,.APWCW	;TIMES CHARACTERS PER WORD
	CAILE	T2,.APWLC	;TOO MANY CHARACTERS?
	MOVEI	T2,.APWLC	;TRUNCATE
	ADD	T3,[POINT 8,1]	;SOURCE BYTE POINTER
	MOVE	T1,[POINT 8,ACOPSW] ;DESTINATION B.P.
	PUSHJ	P,CUPSTR	;COPY THE STRING
	$FALL	CUP.4		;START GETTING/MODIFYING, NEXT PAGE
CUP.4:	SETZM	CUPCHG		;NOTHING CHANGED THIS PASS
	MOVEI	S1,ACOPRO	;PROFILE BLOCK
	MOVEI	S2,CUPWLD	;WILDCARD BLOCK
	PUSHJ	P,PROFIL	;FETCH THE PROFILE IN QUESTION
	JUMPF	CUP.6		;MIGHT BE DONE
	AOS	CUPWLD+UW$FND	;UPDATE COUNT OF ENTRIES MATCHED
	MOVE	S1,ACOPRO+.AEPPN ;GET THIS ENTRY'S PPN
	MOVEM	S1,ACOPPN	;SAVE FOR VARIOUS ROUTINES
	MOVEM	S1,PPN		;HERE TOO (JUST IN CASE)
	SKIPE	CUPDEL		;SUPPOSED TO DELETE THIS ENTRY?
	JRST	CUP.DE		;YES, DO SO
	PUSHJ	P,A$CKPP##	;NO, CHECK FOR RESERVED PPN
	SETCAM	TF,CUPRES	;REMEMBER IF DOING A RESERVED PROFILE
	SKIPN	CUPPWD		;NO, DO WE WANT TO VALIDATE THE PASSWORD?
	JRST	CUP.41		;NO, SKIP IT
	MOVEI	S1,ACOPSW	;POINT TO (OLD) PASSWORD
	MOVEI	S2,ACOPRO	;AND PROFILE BUFFER
	PUSHJ	P,CHKPSW##	;MAKE SURE ITS VALID
	MOVEI	S1,ACOPRO	;POINT TO PROFILE AGAIN
	PUSHJ	P,FIXVLD	;UPDATE VALIDATION TIME IN PROFILE
	JUMPF	CUP.EL		;INVALID LOGIN INFORMATION
	AOS	CUPCHG		;WE CHANGED SOMETHING
	JRST	CUP.42		;DON'T CHECK OWNERSHIP, WE HAVE THE PASSWORD

CUP.41:	MOVE	S1,PPN		;WHO WE'RE TRYING TO HACK
	PUSHJ	P,CHKOWN	;TEST OWNERSHIP IF NOT
	JUMPF	CUP.EI		;ILLEGAL PPN ERROR

CUP.42:	MOVE	S1,CUPLIS	;GET START OF MESSAGE BLOCKS
	MOVEM	S1,QUEBLK	;SAVE FOR GETBLK
	MOVE	S1,CUPCNT	;GET BLOCK COUNT
	MOVEM	S1,QUECNT	;ALSO FOR GETBLK
	$FALL	CUP.5		;ENTER MODIFICATION LOOP, NEXT PAGE
CUP.5:	MOVE	T1,MMSADR	;POINT TO MESSAGE
	PUSHJ	P,GETBLF	;GET BLOCK TYPE AND FLAGS
	JUMPF	CUP.50		;CHECK NEED TO UPDATE AT EOM
	TXNE	S2,AF.SEL	;IF A SELECTION BLOCK,
	JRST	CUP.5		;IGNORE IT (ALREADY CHECKED BY GETA)
	CAIN	T1,.AEVRS	;DOING THE VERSION?
	JRST	CUP.5		;GIMME A BREAK
	MOVSI	T4,-CUPTLN	;SET TO EXAMINE THE CHANGE TABLE
CUP.51:	HLRZ	S1,CUPTAB(T4)	;SEE IF WE CARE
	CAIE	S1,(T1)		;IS THERE A ROUTINE TO PROCESS THIS ENTRY?
	AOBJN	T4,CUP.51	;NOT YET, KEEP LOOKING
	JUMPGE	T4,CUP.52	;NOT AT ALL, HANDLE NORMALLY
	HRRZ	T4,CUPTAB(T4)	;YES, GET ITS ADDRESS
	JRST	(T4)		;LET IT PROCESS THE ENTRY

CUP.52:	MOVE	S1,CHGTAB##(T1)	;GET CONTROL BITS
	TXNE	S1,PD.EXT	;EXTENSIBLE BLOCK?
	JRST	CUP.53		;YES, UPDATE IT
	TXNE	S1,PD.MSK	;NO, MASKABLE WORD?
	TXNE	S2,AF.DEF	;WITH A REAL VALUE?
	JRST	CUP.55		;NO, UPDATE A STATIC BLOCK
	CAIG	T2,1		;IF NO MASK,
	JRST	CUP.55		;TREAT LIKE STATIC BLOCK (FULLWORD CHANGE)
	MOVE	S1,(T3)		;GET VALUE TO SET
	MOVE	T4,1(T3)	;AND CHANGE MASK
	AND	S1,T4		;CHANGE ONLY REQUESTED BITS
	ANDCA	T4,ACOPRO(T1)	;GET BITS TO PRESERVE FROM OLD VALUE
	IOR	S1,T4		;MAKE NEW VALUE WORD
	MOVEM	S1,(T3)		;RESET THE WORD
	SOS	T4,T2		;MEET EXPECTATIONS OF LATER TESTS
	PJRST	CUP.55		;THEN GO HANDLE AS A STATIC BLOCK

CUP.53:	LOAD	T4,S2,AF.DEF	;GET DEFAULTING BIT
	JUMPN	T4,CUP.54	;GO DEFAULT IT IF REQUESTED
	SKIPN	(T3)		;IF THE FIRST WORD IS ZERO,
	CAIE	T2,1		;AND THAT'S ALL THERE IS,
	CAIA			;(NO)
	JRST	CUP.54		;THEN GO DELETE THE BLOCK
	MOVE	S1,ACOPRO(T1)	;NO, GET ITS AOBJN POINTER
	ADDI	S1,ACOPRO	;DE-RELATIVIZE IT
	MOVN	S2,T2		;GET MINUS LENGTH OF MESSAGE BLOCK
	MOVSS	S2		;IN CORRECT HALFWORD
	HRRI	S2,(T3)		;AOBJN POINTER TO MESSAGE DATA
	PUSHJ	P,CUP.CW	;COMPARE THE DATA BLOCKS
	 PUSHJ	P,CUP.CD	;NO CHANGE, CHECK IF DEFAULTED
	  CAIA			;CHANGED OR DEFAULTED
	  JRST	CUP.5		;NO CHANGE AT ALL
	MOVNS	T2		;GET MINUS LENGTH
	MOVSS	T2		;IN CORRECT HALFWORD
	HRRI	T2,(T1)		;ALSO GET ENTRY OFFSET
	MOVEI	T1,ACOPRO	;POINT TO PROFILE
	SETZ	T4,		;CLEARING DEFAULTED BIT
	PUSHJ	P,A$EBLK##	;MODIFY THE PROFILE
	JUMPF	CUP.EE		;NO ROOM
	AOS	CUPCHG		;WE CHANGED SOMETHING
	JRST	CUP.5		;LOOP OVER ALL MESSAGE BLOCKS

CUP.54:	PUSH	P,T4		;SAVE THE DESIRED VALUE
	PUSHJ	P,CUP.CD	;CHECK IF DEFAULTED ALREADY
	  TDZA	S1,S1		;ZERO IF ALREADY DEFAULTED
	MOVEI	S1,1		;ONE IF NOT DEFAULTED (BACKWARDS VALUE OF T4)
	POP	P,T4		;RESTORE REQUESTED SETTING
	CAIE	S1,(T4)		;IF THE OLD STATUS MATCHES THE NEW,
	JUMPN	T4,CUP.5	;NOTHING TO DO IF DEFAULTING
	SKIPN	ACOPRO(T1)	;IF VALUE IS ALREADY ZERO,
	SKIPN	S1		;AND NOT DEFAULTED,
	CAIA			;(NO)
	JUMPE	T4,CUP.5	;THEN NOTHING TO DO IF THAT'S THE DESIRED STATE
	HRROI	T2,(T1)		;NO, MAKE ENTRY DESCRIPTOR
	MOVEI	T1,ACOPRO	;CHANGING THIS PROFILE
	SETZ	T3,		;GIVING NO VALUE
	PUSHJ	P,A$EBLK##	;DELETE OLD BLOCK (SETTING BITMAP AS DESIRED)
	JUMPF	CUP.EE		;NO ROOM (SHOULD NEVER HAPPEN)
	AOS	CUPCHG		;CHANGE HAPPENED
	JRST	CUP.5		;LOOP OVER ALL MESSAGE BLOCKS

CUP.55:	TXNE	S2,AF.DEF	;DEFAULTING?
	JRST	CUP.57		;YES, GO DEFAULT IT
	PUSHJ	P,CUP.CD	;SEE IF DEFAULTED
	  SETZ	T4,		;YES, MUST CHANGE
	LOAD	S1,CHGTAB##(T1),PD.WRD ;NO, GET BLOCK SIZE
	MOVNS	S1		;MAKE MINUS SIZE
	MOVSS	S1		;IN CORRECT HALFWORD
	HRRI	S1,ACOPRO(T1)	;AOBJN POINTER TO CURRENT VALUE
	MOVN	S2,T2		;GET MINUS MESSAGE BLOCK LENGTH
	MOVSS	S2		;IN LH
	HRRI	S2,(T3)		;AOBJN POINTER TO MESSAGE DATA
	JUMPE	T4,CUP.56	;ALWAYS CHANGE IF MUST CLEAR BIT
	PUSHJ	P,CUP.CW	;COMPARE WORD VALUES IN THE BLOCKS
	  JRST	CUP.5		;NO CHANGE HERE
CUP.56:	SKIPGE	S2		;ANYTHING LEFT IN MESSAGE DATA?
	SKIPA	T4,(S2)		;YES, USE IT
	SETZ	T4,		;NO, GET A ZERO
	MOVEM	T4,(S1)		;UPDATE PROFILE
	AOBJP	S2,.+1		;ADVANCE USER POINTER
	AOBJN	S1,CUP.56	;TRANSFER WORDS TO FILL THE BLOCK
	PUSHJ	P,CUP.CD	;CHECK PREVIOUS STATE OF DEFAULT BIT
	  ANDCAM T4,ACOPRO+.AEMAP(S1) ;CLEAR IT IF IT WAS SET
	AOS	CUPCHG		;SOMETHING CHANGED
	JRST	CUP.5		;LOOP OVER ALL MESSAGE BLOCKS

CUP.57:	PUSHJ	P,CUP.CD	;CHECK WHETHER DEFAULTED
	  JRST	CUP.5		;YES, THIS IS NO CHANGE
	IORM	T4,ACOPRO+.AEMAP(S1) ;NO, LIGHT THE BIT
;	LOAD	S1,CHGTAB##(T1),PD.WRD ;GET BLOCK LENGTH
;	MOVEI	S2,ACOPRO(T1)	;AND BLOCK ADDRESS
;	$CALL	.ZCHNK		;CLEAR THE BLOCK
	AOS	CUPCHG		;WE CHANGED IT
	JRST	CUP.5		;LOOP OVER ALL MESSAGE BLOCKS

CUP.NM:	MOVX	S2,AE.NCH	;NAME-CHANGE REQUIRED BIT
	TDNN	S2,ACOPRO+.AEFLG ;IS IT ON?
	SKIPE	ACOPRV		;OR ARE WE PRIVED?
	CAIA			;YES, WE'RE GOLDEN
	JRST	CUP.E3		;NOPE
	MOVN	S2,T2		;GET MINUS LENGTH OF USER ARG
	MOVSS	S2		;IN LH
	HRRI	S2,(T3)		;AOBJN POINTER TO USER DATA
	MOVE	S1,[-.AANLW,,ACOPRO+.AENAM] ;AOBJN POINTER TO PROFILE DATA
	PUSHJ	P,CUP.CW	;WORD-MODE BLOCK COMPARE
	  JRST	CUP.5		;NO CHANGE, DON'T BOTHER ME
	MOVX	S1,AE.NCH	;NAME-CHANGE REQUIRED BIT
	ANDCAM	S1,ACOPRO+.AEFLG ;CLEAR IT NOW
	AOS	CUPCHG		;WE'RE CHANGING THINGS
	MOVE	S1,[ACOPRO+.AENAM,,ACOPRO+.AENAM+1] ;BLT XFER WORD
	SETZM	ACOPRO+.AENAM	;CLEAR A WORD
	BLT	S1,ACOPRO+.AENAM+.AANLW-1 ;START OFF WITH CLEAN BLOCK
	IMULI	T2,.AANCW	;GET NUMBER OF CHARACTERS (MAX.) IN BLOCK
	CAILE	T2,.AANLC	;BEYOND MAXIMUM CHARACTER COUNT?
	MOVEI	T2,.AANLC	;TRUNCATE
	HRLI	T3,(POINT 8)	;AND A BYTE POINTER TO THE DATA
	MOVE	T1,[POINT 8,ACOPRO+.AENAM] ;AND A DESTINATION POINTER
	PUSHJ	P,CUPSTR	;COPY THE STRING
	JRST	CUP.5		;LOOP OVER ALL CHANGE BLOCKS

CUP.PW:	SKIPE	CUPADD		;IF ALREADY DID THIS,
	JRST	CUP.5		;DON'T DO IT AGAIN
	SKIPE	ACOPRV		;IF PRIVED,
	JRST	CUP.P1		;THEN JUST DO IT
	MOVX	S1,AE.PCP	;PASSWORD CHANGE PROHIBITED BIT
	TDNN	S1,ACOPRO+.AEREQ ;TEST IT
	JRST	CUP.P1		;CLEAR, GO CHANGE IT
	SKIPLE	S1,ACOPRO+.AEPCT ;SKIP IF MUST CHANGE PSW NOW
	$CALL	I%NOW		;ELSE, GET CURRENT UDT
	CAML	S1,ACOPRO+.AEPCT ;HAS THE PASSWORD EXPIRED?
	JUMPN	S1,CUP.P1	;CHANGE IT IF REQUIRED (DESPITE AE.PCP)
	FATAL	(PCP,<Password changes are prohibited for ^U/ACOPPN/>,ACPCP%,ACTVXT)

CUP.P1:	SKIPE	ACOPRV		;IGNORE LENGTH IF PRIVED
	JRST	CUP.P2		;YES
	MOVEI	S1,ACOPRO	;THE PROFILE WE'RE MODIFYING
	MOVEI	S2,CUPPWB	;THE PROPOSED NEW PASSWORD
	PUSHJ	P,LENPSW##	;CHECK IT OUT
	JUMPF	CUP.EW		;PASSWORD LENGTH ERROR

CUP.P2:	MOVX	S1,AE.PCP	;CAN THE USER INITIATE PSW CHANGES?
	TDNN	S1,ACOPRO+.AEREQ ;TEST
	JRST	CUP.P3		;YES, DON'T BOTHER TO CHECK IF SAME
REPEAT 0,<
	LOAD	S1,ACOPRO+.AEFLG,AE.PWE ;GET PREVIOUS ENCRYPTION ALGORITHM
	CAME	S1,CURALG##	;IS IT THE SAME AS WHAT WE WILL USE?
	JRST	CUP.P3		;NO, DON'T CHECK FOR MATCH WITH OLD PSW
>
	MOVEI	S1,CUPPWB	;THE PROPOSED NEW PASSWORD
	MOVEI	S2,ACOPRO	;THE PROFILE WE'RE MODIFYING
	PUSHJ	P,CHKPSW##	;SEE IF THIS IS A REAL CHANGE
	JUMPF	CUP.P3		;YES, JUST GO DO IT
	FATAL	(PMC,<Password must change>,ACPMC%,ACTVXT)	;NO

CUP.P3:	MOVEI	S1,CUPPWB	;THE PROPOSED NEW PASSWORD
	MOVEI	S2,ACOPRO	;THE PROFILE WE'RE MODIFYING
	PUSHJ	P,SETPSW##	;CHANGE THE PASSWORD PLEASE
	JUMPF	CUP.EA		;ENCRYPTION FAILURE?
	MOVEI	S1,ACOPRO	;PROFILE BUFFER
	PUSHJ	P,FIXPCR	;FIXUP REQUIRED PROFILE CHANGE
	AOS	CUPCHG		;SOMETHING CHANGED
	JRST	CUP.5		;LOOP OVER ALL CHANGE ENTRIES

CUP.RQ:	PUSH	P,S2		;SAVE FLAGS WORD
	PUSHJ	P,CUP.CD	;CHECK IF DEFAULTED
	  TDZA	T4,T4		;YES
	MOVEI	T4,1		;OR NO
	POP	P,S2		;RESTORE FLAGS
	TXNN	S2,AF.DEF	;SETTING TO DEFAULT?
	TRCA	T4,1		;NO, CHANGE MATCH FLAG
	SKIPA	S1,ACODEF+.AEREQ ;YES, GET DEFAULT
	SKIPA	S1,(T3)		;NO, GET VALUE
	MOVEI	T2,1		;YES, DEMAND A FULL-WORD CHANGE
	CAIL	T2,2		;WAS A MASK GIVEN?
	SKIPA	T1,1(T3)	;YES, FETCH IT
	SETO	T1,		;NO, ASSUME FULLWORD
	AND	S1,T1		;KEEP ONLY BITS TO CHANGE
	ANDCA	T1,ACOPRO+.AEREQ ;FETCH BITS TO LEAVE ALONE
	IOR	S1,T1		;CONSTRUCT NEW WORD
	CAMN	S1,ACOPRO+.AEREQ ;IS THIS A CHANGE?
	JUMPE	T4,CUP.5	;NO CHANGE AT ALL IF VALUES & BITS AGREE
	EXCH	S1,ACOPRO+.AEREQ ;YES, UPDATE, REMEMBERING OLD
	XOR	S1,ACOPRO+.AEREQ ;GET DIFFERENCE MASK
	ANDI	S1,AE.PCI	;ISOLATE CHANGES TO INTERVAL
	JUMPE	S1,CUP.R1	;DON'T UPDATE PCT IF NO CHANGE HERE
	MOVX	S1,AE.PCI	;YES, GET FIELD MASK
	AND	S1,ACOPRO+.AEREQ ;GET NEW CHANGE INTERVAL
	MOVSS	S1		;CORRECT FOR UDT FORMAT
	ADD	S1,ACOPRO+.AELPC ;GET NEW REQUIRED CHANGE TIME
	CAMLE	S1,ACOPRO+.AELPC ;IF USEFUL,
	MOVEM	S1,ACOPRO+.AEPCT ;SET IT IN THE PROFILE

CUP.R1:	AOS	CUPCHG		;WE MADE A CHANGE
	LOAD	T3,S2,AF.DEF	;GET DEFAULTING BIT
	MOVEI	T2,.AEREQ	;THIS IS WHAT WE CHANGED
	MOVEI	T1,ACOPRO	;POINT TO PROFILE
	PUSHJ	P,A$BMAP##	;SET THE BIT AS REQUESTED
	JRST	CUP.5		;LOOP OVER ALL CHANGE BLOCKS

CUP.50:	SKIPN	CUPCHG		;HAS ANYTHING CHANGED?
	JRST	CUP.4		;NO, JUST LOOP OVER ALL MATCHES
	MOVEI	S1,ACOPRO+.AENAM ;YES, POINT TO NAME (MAYBE MODIFIED)
	PUSHJ	P,A$CKNM##	;SEE IF IT'S RESERVED
	XOR	TF,CUPRES	;MAKE TRUE IFF NAME AND PPN AGREE ON RESERVATION
	JUMPF	CUP.EI		;WRONG STATE--SAY ILLEGAL PPN
	SKIPE	CUPRES		;IS PPN RESERVED?
	CAMN	S1,ACOPRO+.AEPPN ;YES, NAME BETTER BE FOR THIS PPN
	CAIA			;IT IS
	JRST	CUP.EI		;WRONG.  SAY ILLEGAL PPN
	MOVE	S1,MDBADR	;YES, GET MDB ADDRESS
	SKIPE	S1,MDB.SD(S1)	;GET SENDER'S PPN
	MOVEM	S1,ACOPRO+.AEPAP ;UPDATE PROFILE CHANGER'S PPN
	$CALL	I%NOW		;GET CURRENT UDT
	MOVEM	S1,ACOPRO+.AETIM ;SET LAST PROFILE CHANGE TIME
	SKIPE	CUPADD		;IS THIS AN UPDATE?
	JRST	CUP.A0		;NO, FINISH UP AN INSERT INSTEAD
	PUSHJ	P,UPDDSK	;UPDATE THE CURRENT RECORD
	JUMPF	CKOPR		;RETURN FAILURES APPROPRIATELY
	JRST	CUP.4		;LOOP OVER ALL MATCHING PROFILES

CUP.DE:	DMOVE	S1,ACOPPD	;POINT TO VANISHING USER BY PPN
	SKIPE	CUPWLD+UW$WST	;ARE WE SEARCHING BY PPN?
	DMOVE	S1,ACONMD	;NO, USE NAME DESCRIPTOR
	PUSHJ	P,DELA##	;DELETE FROM FILE "A"
	JUMPF	CKOPR		;RETURN FAILURES APPROPRIATELY
	JRST	CUP.4		;LOOP OVER ALL MATCHING PROFILES

CUP.6:	SKIPE	S1,RMGCOD	;CHECK FOR FATAL RMS ERRORS
	PJRST	RMGERX		;FATAL--SEND RESPONSE
	MOVEI	T1,CUPWLD	;WILD BLOCK
	MOVE	T2,[POINT 8,ACOACK] ;WILDCARD ACK BLOCK
	PUSHJ	P,A$WACK##	;GENERATE THE ACK TEXT
	MOVEI	T1,CUPWLD	;WILD BLOCK AGAIN
	MOVE	T2,[POINT 8,ACOACK] ;B.P. TO ACK TEXT
	MOVS	T4,UW$FND(T1)	;GET XWD SUCCESS,FAILURE
	MOVEI	T3,[ASCIZ /modified/] ;ASSUME MODIFY DONE
	SKIPE	CUPDEL		;RIGHT ASSUMPTION?
	MOVEI	T3,[ASCIZ /deleted/] ;NO, GET RIGHT TEXT
	MOVE	S1,T3		;SUMMARY ROUTINE USES WRONG AC
	PUSHJ	P,A$SWLD##	;GENERATE THE SUMMARY TEXT
	SKIPF			;SEE IF OK
	INFO	(SUM,<^T/(S1)/>,ACSUM%,ACTVXT)	;RESPOND TO USER AND RETURN
	FATAL	(NSU,<^T/(S1)/>,ACNSU%,ACTVXT)	;GIVE ERROR TO USER

CUPTAB:	.AEPSW,,CUP.PW		;NEED A ROUTINE TO MODIFY THE PASSWORD
	.AENAM,,CUP.NM		;AND THE NAME
	.AEREQ,,CUP.RQ		;AND THE PSW CHANGE INTERVAL
CUPTLN==.-CUPTAB		;LENGTH OF TABLE OF SPECIALS
CUP.AE:	PUSHJ	P,.SAVE2##	;GET SOME BREATHING ROOM
	SETOM	ACOPRO+.AEMAP	;START BY DEFAULTING THINGS
	MOVE	S1,[ACOPRO+.AEMAP,,ACOPRO+.AEMAP+1]
	BLT	S1,ACOPRO+.AEMAP+.AMPLW-1	;TURN ON ALL DEFAULTING BITS
	MOVEI	T1,ACOPRO	;POINT TO PROFILE
	SETZ	T3,		;SETUP TO CLEAR SOME BITS
	MOVSI	P1,-.AEMIN	;HOW FAR INTO THE TABLE TO GO
	MOVX	P2,PD.CND	;CAN-NOT-DEFAULT BIT
CUP.A1:	HRRZ	T2,P1		;COPY ENTRY OFFSET
	TDNE	P2,CHGTAB##(P1)	;CAN THIS ENTRY BE DEFAULTED?
	PUSHJ	P,A$BMAP##	;NO, FLAG IT IN THE BITMAP
	AOBJN	P1,CUP.A1	;LOOP OVER PROFILE ENTRIES
	MOVE	S1,CUPPPM	;ADDRESS OF NEW PPN BLOCK
	MOVE	S1,1(S1)	;GET NEW PPN
	MOVEM	S1,ACOPPN	;SETUP FOR ERROR ROUTINES
	MOVEM	S1,PPN		;LIKEWISE
	MOVEM	S1,ACOPRO+.AEPPN ;AND SET IN PROFILE
	MOVE	S1,[%AECVN,,.AEMIN] ;CURRENT VERSION AND PROFILE SIZE
	MOVEM	S1,ACOPRO+.AEVRS ;INITIALIZE IN PROFILE BUFFER
	MOVE	S1,CUPDEF	;GET DEFAULT PPN
	MOVEM	S1,ACOPRO+.AEDEF ;SET FOR PRODEF
	MOVEI	S1,ACOPRO	;POINT TO PROFILE
	PUSHJ	P,PRODEF	;FETCH DEFAULT PROFILE ENTRIES
	MOVE	S1,PPN		;ENTRY TO BE ADDED
	PUSHJ	P,A$CKPP##	;GET A DEFAULT NAME
	SETCAM	TF,CUPRES	;REMEMBER IF IT'S A RESERVED PPN
	SKIPN	CUPNMM		;IF NONE WAS RECEIVED,
	$TEXT	(<POINT 8,ACOPRO+.AENAM,-1>,<^T/(S1)/^0>) ;DEFAULT IT
	SETOM	CUPADD		;REMEMBER THAT WE WANT TO ADD, NOT MODIFY
	AOS	CUPCHG		;WE DO WANT TO MAKE THE CALL TO ACTRMS
	JRST	CUP.42		;MODIFY THE BLOCK, THEN COME BACK

CUP.A0:	MOVEI	S1,CUPPWB	;THE PROPOSED NEW PASSWORD
	MOVEI	S2,ACOPRO	;THE PROFILE WE'RE MODIFYING
	PUSHJ	P,SETPSW##	;SET THE ENCRYPTED PASSWORD IN THE BLOCK
	JUMPF	CUP.EA		;ENCRYPT FAILED
	SKIPN	CUPPWM		;WERE WE GIVEN A PASSWORD?
	SETOM	ACOPRO+.AEPCT	;NO, REQUIRE ONE UPON FIRST LOGIN
	SKIPN	CUPNMM		;WERE WE GIVEN A NAME?
	SKIPE	CUPRES		;OR DEFAULT NAME NOT CHANGEABLE?
	JRST	CUP.A2		;YES, DON'T NEED TO CREATE ONE
	MOVX	S2,AE.NCH	;NO, GET NAME-CHANGE BIT
	IORM	S2,ACOPRO+.AEFLG ;REQUIRE A NEW NAME UPON FIRST LOGIN

CUP.A2:	SKIPN	CUPPWM		;WERE WE GIVEN A PASSWORD?
	JRST	CUP.A6		;NO, CAN'T DO ANY OF THE FOLLOWING
	MOVE	S1,ACOPRO+.AETIM ;YES, GET LAST PROFILE CHANGE TIME
	MOVEM	S1,ACOPRO+.AELPC ;SET LAST PASSWORD CHANGE TIME
	SKIPE	ACOPRO+.AEPCT	;ALREADY HAVE A REQUIRED CHANGE TIME?
	JRST	CUP.A6		;YES, DON'T INVENT ONE
	LOAD	S1,ACOPRO+.AEREQ,AE.PCI ;NO, GET CHANGE INTERVAL
	JUMPE	S1,CUP.A6	;GIVE UP IF NO REQULAR CHANGES REQUIRED
	MOVSS	S1		;INTERVAL IS SET, CORRECT FOR UDT FORMAT
	ADD	S1,ACOPRO+.AETIM ;OFFSET FROM CURRENT TIME
	MOVEM	S1,ACOPRO+.AEPCT ;THEN SETUP BASED ON CHANGE INTERVAL

CUP.A6:
REPEAT 0,< ;FOLLOWING CODE MIGHT BE CUTE, BUT IT'S NOT USEFUL
	SKIPN	ACOPRO+.AEPNM	;DO WE HAVE A PERSONAL NAME YET?
	SKIPN	CUPNMM		;CAN'T DO THIS IF DEFAULTED NAME
	JRST	CUP.A7		;YES, DON'T DEFAULT ONE
	MOVEI	T1,ACOPRO	;POINT TO PROFILE
	MOVEI	T2,.AEPNM	;OFFSET TO TEST
	SETO	T3,		;CHECKING
	PUSHJ	P,A$BMAP##	;TEST IF DEFAULTED EMPTY
	JUMPF	CUP.A7		;NO, DON'T CHANGE IT
	MOVE	T1,[POINT 8,ACOPRO+.AENAM] ;YES, POINT TO NAME
	SETZ	T2,		;INIT COUNT
	ILDB	T3,T1		;GET CHAR FROM NAME
	SKIPE	T3		;IF NOT AT END,
	AOJA	T2,.-2		;KEEP COUNTING ITS LENGTH
	IDIVI	T2,.AANCW	;MAKE WORD COUNT
	SETCA	T2,		;NEGATE AND ROUND 'UP'
	LDB	T3,[POINT 9,@CUPNMM,17] ;GET LENGTH OF NAME BLOCK
	MOVNI	T3,-1(T3)	;GET MINUS DATA LENGTH
	CAMGE	T2,T3		;IN RANGE?
	MOVE	T2,T3		;NO, LIMIT OURSELVES
	MOVSS	T2		;GET IN RIGHT HALFWORD
	HRRI	T2,.AEPNM	;FIELD WE WANT TO SET
	MOVE	T3,CUPNMM	;POINT TO NAME BLOCK
	AOS	T3		;DATA FOR FETCHING
	MOVEI	T1,ACOPRO	;PROFILE TO MODIFY
	SETZ	T4,		;NOT FROM DEFAULT PROFILE
	PUSHJ	P,A$EBLK##	;TRY TO GIVE THE USER A MIXED-CASE NAME
> ;END OF REPEAT 0

CUP.A7:	MOVEI	S1,ACOPRO	;POINT TO NEW PROFILE
	PUSHJ	P,PUTA##	;FINISH THE CREATE
	JUMPF	CKOPR		;ANALYZE FAILURE OR ACK SUCCESS
	INFO	(INS,<User [^O/PPN,LHMASK/,^O/PPN,RHMASK/] inserted>,ACINS%,ACTVXT)
CUP.EF:	FATAL	(IFM,<Illegally formatted message>,ACIFM%,ACTVXT)

CUP.E3:	PUSHJ	P,ERROR3	;SET THE PRIVILEGE ERROR ACK
	PJRST	ACTVXT		;THEN DELIVER IT

CUP.EA:	MOVE	S1,CURALG##	;GET ALGORITHM INDEX
	FATAL	(EAF,<Encryption algorithm ^O/S1/ failed>,ACEAF%,ACTVXT)

CUP.EP:	FATAL	(MPR,<Missing password required>,ACMPR%,ACTVXT)

CUP.EL:	SKIPE	ACOPRV		;PRIVED USER?
	PJRST	ILLPSW		;YES, GO ADMIT TO BAD PASSWORD
	MOVEI	T1,ACPSW%	;NO, BUT TELL THE TRUTH IN THE ERROR FILE
	PUSHJ	P,LOGUSR	;LOG INITIAL USER STUFF
	$TEXT	(LOGFAI,<^O6R0/ACOPPN,LHMASK/^O6R0/ACOPPN,RHMASK/>)
	PUSHJ	P,FAIOUT	;FLUSH THE BUFFERS
	PUSHJ	P,UPDDSK	;RECORD THE VALIDATION FAILURE ON DISK
	PJRST	CUP.EI		;PRETEND IT WAS AN ILLEGAL PPN

CUP.EE:	FATAL	(PTF,<Profile is too full; user ^U/ACOPPN/>,ACPTF%,ACTVXT)

CUP.EW:	FATAL	(PLL,<Password length is less than ^D/S1/ characters>,ACPLL%,ACTVXT)
CUP.CW:	PUSHJ	P,.SAVE4##	;PRESERVE ALL ACS USED
	DMOVE	P3,S1		;COPY POINTERS
CUP.C1:	SKIPGE	P3		;IF STILL A BASE WORD,
	SKIPA	P1,(P3)		;THEN USE IT,
	SETZ	P1,		;ELSE FETCH ZERO
	SKIPGE	P4		;SIMILARLY WITH
	SKIPA	P2,(P4)		;THE MESSAGE DATA
	SETZ	P2,		;FETCH WORD OR ZERO
	CAME	P1,P2		;ARE THEY THE SAME?
	JRST	.POPJ1		;NOPE, FLAG DIFFERENT
	AOBJP	P3,.+1		;ADVANCE POINTER
	AOBJN	P4,CUP.C1	;LOOP OVER ALL WORDS
	JUMPL	P3,CUP.C1	;UNTIL BOTH POINTERS GIVE OUT
	POPJ	P,		;THE BLOCKS ARE THE SAME IF WE GET HERE

CUP.CD:	MOVEI	S1,(T1)		;COPY ENTRY OFFSET
	IDIVI	S1,^D36		;GET WORD & BIT NUMBERS
	MOVNS	S2		;GET SHIFT OFFSET
	MOVSI	T4,(1B0)	;GET BIT TO SHIFT
	LSH	T4,(S2)		;MAKE BIT TO TEST
	TDNN	T4,ACOPRO+.AEMAP(S1) ;IS THIS FIELD ALREADY DEFAULTED?
	AOS	(P)		;SKIP IF NOT
	POPJ	P,		;RETURN RESULT

CUPSTR:	ILDB	S1,T3		;GET A SOURCE BYTE
	JUMPE	S1,.POPJ	;DONE AT END OF ASCIZ
	IDPB	S1,T1		;STUFF A DESTINATION BYTE
	SOJG	T2,CUPSTR	;UNTIL EOS OR FULL BLOCK
	POPJ	P,		;THEN RETURN
CUPMEM:!			;BEGINNING OF UGCUP$-SPECIFIC STORAGE

CUPPWB:	BLOCK	.APWLW		;SPACE FOR A PASSWORD BLOCK
CUPWLD:	BLOCK	PAGSIZ		;SPACE FOR A WILDCARDING BLOCK
CUPSEL:	BLOCK	1		;CURRENT ALLOCATION OF SELECT BLOCKS
CUPADD:	BLOCK	1		;FLAG FOR CALLING PUTA RATHER THAN UPDA
CUPDEF:	BLOCK	1		;MODIFIED VALUE OF .AEDEF
CUPRES:	BLOCK	1		;FLAG FOR INSERTING RESERVED PROFILE
CUPDEL:	BLOCK	1		;FLAG FOR WHETHER ENTRY SHOULD BE DELETED
CUPPPN:	BLOCK	1		;POINTER TO SELECT BLOCK FOR PPN
CUPPPM:	BLOCK	1		;POINTER TO MODIFY BLOCK FOR PPN
CUPNAM:	BLOCK	1		;POINTER TO SELECT BLOCK FOR NAME
CUPNMM:	BLOCK	1		;POINTER TO MODIFY BLOCK FOR NAME
CUPPWD:	BLOCK	1		;POINTER TO SELECT BLOCK FOR PASSWORD
CUPPWM:	BLOCK	1		;POINTER TO MODIFY BLOCK FOR PASSWORD
CUPVRS:	BLOCK	1		;FLAG FOR WHETHER VERSION CHANGE WAS SEEN
CUPMFC:	BLOCK	1		;MODIFIABLE FIELD COUNT
CUPCHG:	BLOCK	1		;FLAG FOR WRITING OUT ACOPRO

CUPZND==.-1			;LAST WORD TO ZERO ON UGCUP$

CUPCNT:	BLOCK	1		;MEMORY OF QUECNT
CUPLIS:	BLOCK	1		;MEMORY OF QUEBLK
CKOPR:	JUMPT	ACTVXT		;JUMP IF OK
	MOVEI	S1,3		;IT DID NOT, SEE WHY
	PUSHJ	P,OPTA##	;GET STATUS OF FILE "A"
	CAIN	S1,ER$DUP##	;WAS THE NAME ALREADY IN USE?
	JRST	[MOVE	S1,[POINT 8,ACOPRO+.AENAM] ;YES, POINT TO NAME
		FATAL	(NAE,<Name already exists; ^Q/S1/>,ACNAE%,ACTVXT)]
	CAIN	S1,ER$RNF##	;RECORD NOT FOUND?
CUP.EI:	FATAL	(ILP,<Illegal PPN [^O/PPN,LHMASK/,^O/PPN,RHMASK/]>,ACILP%,ACTVXT)
	PUSHJ	P,RMGCH1	;CHECK FOR FATAL RMS ERRORS
RMGERX:	FATAL	(RMS,<Unexpected RMS error ^O6R0/S1/>,ACRMS%,ACTVXT)


;CHECK FOR FATAL RMS ERRORS.
RMGCHK:	$RETIT			;NO ERRORS--RETURN
	MOVEI	S1,3		;
	PUSHJ	P,OPTA##	;GET STATUS OF FILE "A"
RMGCH1:	CAIE	S1,ER$BUG##	;INTERNAL ERROR?
	CAIN	S1,ER$UDF##	;UNDEFINED STATE?
	TRNA			;YES--TELL THE OPERATOR (MAYBE)
	$RETT			;NO--IGNORE IT
	MOVEM	S1,RMGCOD	;SAVE RMS ERROR CODE
	PUSHJ	P,I%NOW		;GET CURRENT UNIVERSAL DATE/TIME
	SKIPN	RMGUDT		;FIRST TIME THROUGH THIS CODE?
	JRST	RMGCH2		;YES--SEND MESSAGE TO OPERATOR
	MOVE	S2,S1		;NO--CALCULATE TIME SINCE LAST MESSAGE
	SUB	S2,RMGUDT	;
	IMULI	S2,^D24*^D3600	;CONVERT TO SECONDS
	HLRZ	S2,S2		;
	CAIGE	S2,RMGINT	;HAS ENOUGH TIME PASSED?
	JRST	RMGCH3		;NO--DON'T BOTHER THE OPERATOR
RMGCH2:	MOVEM	S1,RMGUDT	;SAVE CURRENT UNIVERSAL DATE/TIME
	MOVEI	S1,4		;
	PUSHJ	P,OPTA##	;GET FILESPEC FOR FILE "A"
	$WTOXX	<Fatal RMS error on ^F/(S2)/; RMS error ^OR0/RMGCOD/>
RMGCH3:	MOVE	S1,RMGCOD	;RESTORE RMS ERROR CODE
	$RETF			;
;FIXUP REQUIRED PASSWORD CHANGE BITS
FIXPCR:	$SAVE	P1			;SAVE AN AC
	MOVE	P1,S1			;SAVE ADDRESS OF RECORD BUFFER
	$CALL	I%NOW			;GET CURRENT UDT
	CAML	S1,.AEPCT(P1)		;PERHAPS A REQUIRED CHANGE?
	SETZM	.AEPCT(P1)		;YES, CLEAR THE UT
	MOVEM	S1,.AELPC(P1)		;SAVE LAST PASSWORD CHANGE
	LOAD	S1,.AEREQ(P1),AE.PCI	;GET CHANGE INTERVAL
	MOVSS	S1			;CORRECT FOR UDT FORMAT
	ADD	S1,.AELPC(P1)		;ADJUST CHANGE TIME BY INTERVAL
	CAMLE	S1,.AELPC(P1)		;IF INTERVAL IS PRESENT,
	MOVEM	S1,.AEPCT(P1)		;SETUP NEW REQUIRED CHANGE TIME
	MOVX	S1,1B<.AEPCT-<<.AEPCT/36>*36>>	;GET DEFAULT BIT
	ANDCAM	S1,.AEMAP+.AEPCT/^D36(P1)	;CLEAR IT, SINCE CHANGED VALUE
	POPJ	P,

;ROUTINE TO HANDLE SELECTION ON .AEAUX

;CALL:	P1/ WILD BLOCK ADDRESS
;	P2/ SELECTION BLOCK ADDRESS
;	P3/ PROFILE BUFFER ADDRESS

UGAUX%::LDB	T1,[POINT 9,(P2),17] 	;NUMBER OF WORDS IN BLOCK
	SOJLE	T1,.RETF		;OFFSET FOR ONLY DATA WORDS
	MOVNS	T1			;MAKE MINUS DATA LENGTH
	MOVSS	T1			;IN LH FOR AOBJN
	HRRI	T1,1(P2)		;POINTER TO REQUESTOR'S DATA
	MOVE	T2,.AEAUX(P3)		;RELATIVE POINTER TO PROFILE DATA
	ADDI	T2,(P3)			;DE-RELATIVIZE IT
UGAUX1:	MOVE	T3,T2			;GET WORKING COPY OF PROFILE POINTER
	MOVE	T4,.AUSTR(T1)		;AND CURRENT NAME TO MATCH
UGAUX2:	CAME	T4,.AUSTR(T3)		;[152] FOUND START OF SUBSTRING?
	CAMN	T4,[-1]			;[152] OR A WILDCARD?
	JRST	UGAUX3			;YES, PROCESS THE LIST
	ADD	T3,[.AULEN-1,,.AULEN-1]	;UPDATE FOR MULTI-WORD ENTRIES
	AOBJN	T3,UGAUX2		;KEEP LOOKING THROUGH SEARCH LIST
	LOAD	T1,(P2),AF.SEL		;GET SELECTION TYPE
	CAIN	T1,.AFNOT		;ONLY SUCCEED
	AOS	(P)			;IF A 'NOT' BLOCK
	POPJ	P,			;RETURN THE DECISION

UGAUX3:	MOVE	T4,T1			;COPY SEARCH POINTER
UGAUX4:	MOVE	T2,(T4)			;GET NEXT SEARCH WORD
	CAMN	T2,[-1]			;IS IT A DON'T CARE?
	JRST	UGAUX5			;YES, IT SUCCEEDS
	SKIPL	T3			;[152] IF NO MORE IN PROFILE,
	TDZA	T1,T1			;[152] ZERO-FILL
	MOVE	T1,(T3)			;NO, FETCH WORD FROM PROFILE
	PUSHJ	P,CMPINS##		;CHECK FOR MATCH
	  POPJ	P,			;PROPAGATE FAILURE
UGAUX5:	AOBJP	T4,.POPJ1		;SUCCEED IF END OF MATCH LIST
	AOBJN	T3,UGAUX4		;KEEP SEARCHING
	JRST	UGAUX4			;[152] EVEN IF PROFILE IS SHORT
ACTPSW:	SKIPN	QUEFLG		;ONLY DEFINED FROM QUEUE. UUO
	JRST	IGNORE		;WASN'T. RELEASE MESSAGE AND IGNORE IT
	$CALL	M%GPAG		;GET A PAGE FOR THE RESPONSE MESSAGE
	MOVEM	S1,SABADR	;STORE THE ADDRESS OF THE PAGE/PACKET
	PUSHJ	P,CVTWLD	;CONVERT TO WILDCARD BLOCK (S2 GETS ADDR)
	MOVEI	S1,ACOPRO	;WHERE TO READ THE RECORD
	PUSHJ	P,PROFIL	;FETCH PROFILE
	JUMPF	[SKIPN	S1,RMGCOD ;CHECK FOR FATAL RMS ERRORS
		 JRST	ACTACA	;NOT FATAL--NO SUCH USERS
		 PJRST	RMGERX]	;FATAL--SEND RESPONSE
	MOVEI	S2,ACOPRO	;POINT TO THE RETURNED PROFILE
	MOVE	S1,.AEPPN(S2)	;GET THE PPN
	MOVEM	S1,ACOPPN	;SAVE IN PPN PLACE
ACTPS0:	MOVEI	S1,ACOPSW	;POINT AT THE PASSWORD
	MOVEI	S2,ACOPRO	;RECORD HE WANTS TO CHECK
	$CALL	CHKPSW##	;CHECK THE PASSWORD, PLEASE
	MOVEI	S1,ACOPRO	;POINT TO THIS PROFILE
	PUSHJ	P,FIXVLD	;UPDATE VALIDATION STATUS
	JUMPT	ACTPS1		;IT'S GOOD, CONTINUE
	PJRST	ILLPSW		;REPORT INVALID PASSWORD AND RETURN
ACTPS1:	JRST	ACTVXX		;AND RETURN
	SUBTTL	LOCK/UNLOCK USER ACCOUNT FILE

ACTLOK:	TDZA	S1,S1		;INDICATE ENTRY
ACTUNL:	SETOM	S1
	SKIPN	QUEFLG		;ONLY DEFINED FROM QUEUE. UUO
	PJRST	IGNORE		;WASN'T, RELEASE THE MESSAGE
	MOVE	T1,S1		;SAVE ENTRY INDICATOR
	$CALL	M%GPAG		;GET A PAGE FOR THE RESPONSE MESSAGE
	MOVEM	S1,SABADR	;STORE THE ADDRESS OF THE PAGE/PACKET
	JUMPL	T1,UNLOCK	;JUMP IF UNLOCK FUNCTION
	SKIPN	ACTLCK##	;ALREADY LOCKED?
	JRST	LOCK		;NO, GO DO IT
	FATAL	(FAL,<File already locked>,ACFAL%,ACTVXT)

LOCK:	PUSHJ 	P,CLSA##	;CLOSE FILE "A"
	JUMPF	LOCK.1		;CHECK FOR ERRORS
	MOVEI	S1,ACCTFN	;GET ADDRESS OF FILENAME FOR RMS
	MOVEI	S2,0		;READ-ONLY
	PUSHJ	P,OPNA##	;OPEN FILE "A" FOR INPUT
	JUMPF	LOCK.1		;CAN'T GO OPEN REGULAR (TRY ANYWAY)
	SETOM	ACTLCK##	;FLAG FILE IS LOCKED
	PJRST	ACTVXT		;RETURN

LOCK.1:	MOVEI	S1,3		;OPTION CODE
	PUSHJ	P,OPTA##	;GET STATUS FOR FILE "A"
	PUSH	P,S1		;SAVE ERROR CODE
	MOVEI	S1,ACCTFN	;GET FILENAME ADDRESS
	MOVEI	S2,1		;WANT TO WRITE THE FILE
	PUSHJ	P,OPNA##	;OPEN FILE "A" FOR I/O
	JUMPT	LOCK.2		;GO COMPLAIN ABOUT PREVIOUS ERROR
	MOVEI	S1,3		;UNLESS THIS FAILS TOO--GET OPTION CODE
	PUSHJ	P,OPTA##	;GET STATUS FOR FILE "A"
	MOVEM	S1,(P)		;SAVE
LOCK.2:	POP	P,S1		;GET RMS ERROR CODE BACK
	FATAL	(RMS,<Unexpected RMS error ^O6R0/S1/>,ACRMS%,ACTVXT)

UNLOCK:	SKIPN	ACTLCK##	;FILE LOCKED?
	FATAL	(FNL,<File not locked>,ACFNL%,ACTVXT)
	PUSHJ	P,CLSA##	;CLOSE FILE "A"
	JUMPF	UNLO.2		;CHECK FOR ERRORS
	MOVEI	S1,ACCTFN	;GET FILENAME ADDRESS
	MOVEI	S2,1		;WANT TO WRITE THE FILE
	PUSHJ	P,OPNA##	;OPEN FILE "A" FOR I/O
	JUMPF	UNLO.2
        SETZM	ACTLCK##	;FLAG FILE IS NOW UNLOCKED
	PJRST	ACTVXT		;RETURN
UNLO.2:	MOVEI	S1,3		;IT DID NOT, SEE WHY
	PUSHJ	P,OPTA##	;GET STATUS FOR FILE "A"
	FATAL	(RMS,<Unexpected RMS error ^O6R0/S1/>,ACRMS%,ACTVXT)

ACTSCD:	SKIPN	QUEFLG		;ONLY DEFINED FROM QUEUE. UUO
	 PJRST	IGNORE		;WASN'T, RELEASE THE MESSAGE
	$CALL	M%GPAG		;GET A PAGE FOR THE RESPONSE MESSAGE
	MOVEM	S1,SABADR	;STORE THE ADDRESS OF THE PAGE/PACKET
	PUSHJ	P,A$DSCD##	;DELETE SCDMAP.SYS DATA
	SKIPF
	PUSHJ	P,A$ISCD##	;INITIALIZE CLASS SCHEDULER MAPPING
	JUMPT	ACTVXT		;JUMP IF OK
	FATAL	(SMF,<Class scheduler mapping failed>,ACSMF%,ACTVXT)

	;ROUTINE TO UPDATE VALIDATION TIME AND FAILURE BIT

FIXVLD:	$SAVE	P1			;FOR THE BUFFER ADDRESS
	MOVE	P1,S1			;SAVE WHAT WE'RE UPDATING
	SKIPF				;SKIP IF FAILED
	SKIPA	S2,[ANDCAM S1,.AEFLG(P1)] ;GET INSTR TO CLEAR AE.FAI
	MOVE	S2,[IORM   S1,.AEFLG(P1)] ;GET INSTR TO SET AE.FAI
	PUSH	P,TF			;SAVE STATUS ON STACK
	MOVX	S1,AE.FAI		;GET FAILURE TIME VALID BIT
	XCT	S2			;DO THE DESIRED OPERATION
	$CALL	I%NOW			;GET CURRENT UDT
	MOVEM	S1,.AEFAI(P1)		;SAVE IT ALSO
	POP	P,TF			;RESTORE STATUS
	$RET
SUBTTL	ACTMAP - PERFORM USER PPN/NAME MAPPING


ACTMAP:	PUSHJ	P,.SAVE2	;SAVE P1 AND P2
	PUSHJ	P,M%GPAG	;GET A PAGE FOR THE RESPONSE MESSAGE
	MOVEM	S1,SABADR	;STORE THE ADDRESS OF THE PAGE/PACKET
	PUSHJ	P,MAPLGL	;CHECK FOR A LEGAL MESSAGE FORMAT
	JUMPF	ACTVXT		;ACK USER ON ERRORS

ACTMA1:	PUSHJ	P,MAPWLD	;BUILD WILDCARD BLOCK
	MOVEI	S1,ACOPRO	;WHERE TO READ THE RECORD
	MOVEI	S2,(P2)		;POINT TO WILDCARD BLOCK
	PUSHJ	P,GETA##	;FETCH PROFILE FROM FILE "A"
	JUMPF	[PUSHJ	P,RMGCHK ;CHECK FOR FATAL RMS ERRORS
		 JUMPT	ACTMA4	;NOT FATAL--NO SUCH USER
		 PJRST	RMGERX]	;FATAL-SEND RESPONSE
	MOVE	T1,ACOPRO+.AEPPN ;GET PPN
	MOVEM	T1,TMPMAP+UU$PPN ;SAVE
	MOVE	T1,[ACOPRO+.AENAM,,TMPMAP+UU$NAM] ;SET UP BLT
	BLT	T1,TMPMAP+UU$LEN-1 ;SAVE NAME IN TEMPORARY STORAGE
	SKIPE	T1,UW$WST(P2)	;A PPN?
	CAIN	T1,2		;OR NON-WILD NAME?
	JRST	ACTMA3		;YES--NO AMBIGUITY EXISTS
	MOVEI	T1,TMPMAP+UU$NAM ;POINTER TO RETURNED NAME
	HRLI	T1,(POINT 8)	;FOR LOADING
	SETZ	T2,		;TO COUNT CHARACTERS

ACTMA2:	ILDB	T3,T1		;GET NEXT CHARACTER
	SKIPE	T3		;STOP AT END OF NAME
	AOJA	T2,ACTMA2	;LOOP OVER ALL CHARACTERS
	CAMN	T2,TMPLEN	;NAMES OF THE SAME LENGTH?
	JRST	ACTMA3		;YES, THEY ARE IDENTICAL (EXCEPT FOR CASE?)
	MOVEI	S1,ACOPRO	;WHERE TO READ THE RECORD
	MOVEI	S2,(P2)		;POINT TO WILDCARD BLOCK
	PUSHJ	P,GETA##	;FETCH PROFILE FROM FILE "A"
	JUMPT	ACTMA4		;JUMP IF AMBIGUOUS NAME GIVEN
	PUSHJ	P,RMGCHK	;CHECK FOR FATAL RMS ERRORS
	JUMPF	RMGERX		;FATAL ERROR--SEND RESPONSE

ACTMA3:	MOVSI	T1,TMPMAP	;POINT TO TEMPORARY STORAGE
	HRRI	T1,(P1)		;AND TO DESTINATION
	BLT	T1,UU$LEN-1(P1)	;COPY

ACTMA4:	ADDI	P1,UU$LEN-1	;ACCOUNT FOR MULTI-WORD ENTRIES
	AOBJN	P1,ACTMA1	;ONTO THE NEXT MAPPING BLOCK
	MOVNI	T1,4		;GET A -4 (NOT REALLY AN ERROR NUMBER)
	PUSHJ	P,ERRPRO	;GENERATE "ERROR" -4 (MOVE MAPPING INFO)
	PJRST	ACTVXT		;FINISH RESPONSE TO QUEUE. AND RETURN
; CHECK FOR LEGAL MAPPING MESSAGE FORMAT
MAPLGL:	MOVE	P1,MMSADR	;POINT TO MESSAGE
	MOVEI	T3,(P1)		;INCASE IPCF MESSAGE
	MOVE	T1,UU$ACK(P1)	;GET THE ACK CODE
	MOVEM	T1,ACKCOD	;SAVE
	SKIPN	QUEFLG		;QUEUE UUO?
	JRST	MAPLG2		;NO
	SETZM	QUEBLK		;RESET POINTER TO SCAN FROM BEGINING
	MOVE	T1,MMSADR	;POINT TO MESSAGE

MAPLG1:	PUSHJ	P,GETBLK	;GET A BLOCK
	SKIPT			;CHECK FOR ERRORS
	FATAL	(PEM,<Premature end of mapping message block>,ACPEM%,.RETF)
	CAIE	T1,.QBAET	;START OF INTERESTING DATA?
	JRST	MAPLG1		;NOT YET

MAPLG2:	MOVEI	P1,UU$MAP(T3)	;POINT TO START OF ACTUAL DATA
	MOVEM	P1,ACOMAP	;SAVE ADDRESS
	SKIPN	T1,UU$CNT(T3)	;GET COUNT OF MAPPING BLOCKS
	FATAL	(MBZ,<Mapping block count is zero>,ACMBZ%,.RETF)
	MOVEM	T1,TMPCNT	;SAVE FOR ERRMAP
	MOVNS	T1		;NEGATE
	HRL	P1,T1		;MAKE AN AOBJN POINTER
	MOVE	P2,P1		;COPY MESSAGE POINTER

MAPLG3:	HRRZ	T1,P2		;GET CURRENT MAPPING BLOCK ADDRESS
	SUB	T1,ACOMAP	;COMPUTE OFFSET INTO MESSAGE
	IDIVI	T1,UU$LEN	;GET BLOCK NUMBER
	SKIPN	UU$PPN(P2)	;HAVE A PPN?
	SKIPE	UU$NAM(P2)	;OR A NAME?
	SKIPA			;YES TO EITHER
	FATAL	(MBF,<Mapping block format error; block = ^D/T1/>,ACMBF%,.RETF)
	ADDI	P2,UU$LEN-1	;ACCOUNT FOR MULTI-WORD ENTRIES
	AOBJN	P2,MAPLG3	;CHECK THE NEXT BLOCK
	$RETT			;RETURN
; BUILD WILDCARD MESSAGE
MAPWLD:	MOVEI	P2,ACOWLD	;POINT TO INTERNAL WILDCARD BLOCK
	MOVSI	T1,0(P2)	;START ADDRESS
	HRRI	T1,1(P2)	;MAKE A BLT POINTER
	SETZM	(P2)		;CLEAR FIRST WORD
	BLT	T1,UW$MIN-1(P2)	;CLEAR ENTIRE BLOCK
	MOVSI	T1,UW$MIN	;LENGTH
	HRRI	T1,UGWLD$	;FUNCTION CODE
	MOVEM	T1,UW$TYP(P2)	;SAVE
	SKIPN	T1,UU$PPN(P1)	;HAVE A PPN?
	JRST	MAPWL1		;NO--A NAME
	MOVEM	T1,UW$PPN(P2)	;SAVE IN WILDCARD BLOCK
	SETOM	UW$PPM(P2)	;NON-WILD MASK
	POPJ	P,		;RETURN

MAPWL1:	MOVEI	T1,UU$NAM(P1)	;POINT TO NAME
	HRLI	T1,(POINT 8,)	;8-BIT ASCIZ
	MOVEI	T2,UW$NAM(P2)	;POINT TO STORAGE
	HRLI	T2,(POINT 8,)	;8-BIT ASCIZ
	MOVSI	T3,-.AANLC	;LENGTH IN CHARACTERS

MAPWL2:	ILDB	T4,T1		;GET A CHARACTER
	JUMPE	T4,MAPWL4	;DONE?
	IDPB	T4,T2		;PUT A CHARACTER
	TRZE	T3,1B19		;WAS PREVIOUS CHARACTER A QUOTE?
	JRST	MAPWL3		;YES, DON'T DAMAGE THIS ONE
	CAIE	T4,"*"		;NO, CHECK FOR WILDCARD
	CAIN	T4,"?"		;OF EITHER TYPE
	TRO	T3,1B18		;YES, REMEMBER IT
	CAIE	T4,.CHCNV	;OR IS IT THE MAGIC QUOTE CHARACTER?
	JRST	MAPWL3		;NO, DON'T FUDGE FOR IT
	TRO	T3,1B19!1B20	;YES, FLAG FOR NEXT TIME AROUND
	SOS	T3		;DON'T COUNT CHARACTER AGAINST MATCH LENGTH
MAPWL3:	AOBJN	T3,MAPWL2	;LOOP
	MOVEI	T1,2		;CODE
	MOVEM	T1,UW$WST(P2)	;SET WILDCARD SEARCH TYPE TO NON-WILD NAME
	POPJ	P,		;RETURN

MAPWL4:	TRZ	T3,1B20		;JUST IN CASE IT'S STILL ON
	HRRZM	T3,TMPLEN	;SAVE LENGTH OF NAME
	MOVEI	T4,"*"		;MAKE A WILD NAME
	IDPB	T4,T2		;STORE CHARACTER
	MOVEI	T1,1		;CODE
	MOVEM	T1,UW$WST(P2)	;SET WILDCARD SEARCH TYPE TO WILD NAME
	POPJ	P,		;RETURN
SUBTTL	ACTWLD - GET PROFILE FOR POSSIBLY WILDCARDED PPN/NAME


ACTWLD:	PUSHJ	P,M%GPAG	;GET A PAGE FOR THE RESPONSE MESSAGE
	MOVEM	S1,SABADR	;STORE THE ADDRESS OF THE PAGE/PACKET
	MOVE	S2,MMSADR	;POINT TO MESSAGE
	SKIPN	QUEFLG		;FROM A QUEUE. UUO?
	PJRST	ACTOU0		;NO CONVERSION NECESSARY
	SETZM	QUEBLK		;RESET POINTER TO SCAN FROM BEGINING
	MOVE	T1,MMSADR	;POINT TO MESSAGE

ACTWL1:	PUSHJ	P,GETBLK	;GET A BLOCK
	JUMPF	ACTWL2		;CHECK FOR PREMATURE END OF MESSAGE
	CAIE	T1,.QBAET	;START OF INTERESTING DATA?
	JRST	ACTWL1		;NOT YET
	MOVE	S2,T3		;POINT TO DATA
	PJRST	ACTOU0		;GO ENTER COMMON CODE

ACTWL2:	FATAL	(ILP,<Illegal PPN [^O/S1,LHMASK/,^O/S1,RHMASK/]>,ACILP%,ACTVXT)
	SUBTTL	ACTVER - COMMON SUBROUTINES

;CHKPRJ - ROUTINE TO VERIFY ACCOUNT IN PROJCT.SYS.
;CALL:	PUSHJ	P,CHKPRJ
;	RETURN HERE - NO FATAL ERRORS DETECTED

CHKPRJ:	PUSHJ	P,CHKCOM	;DO COMMON STUFF TO GET POSITIONED
	JUMPF	.POPJ		;WHOOPS, SOME ERROR TO RETURN TO THE USER
	PJRST	VERIFY		;DO VALIDATION

;CHKDEF - ROUTINE TO SEE IF DEFAULT ACCOUNT STRING EXISTS IN PROJCT.SYS
;CALL:	PUSHJ	P,CHKDEF
;	RETURN HERE - NO FATAL ERROR DETECTED

CHKDEF:	PUSHJ	P,CHKCOM	;DO COMMON STUFF TO GET POSITIONED
	JUMPF	.POPJ		;WHOOPS, SAY NO DEFAULT EXISTS
	PJRST	FNDDEF		;GO FIND DEFAULT AND RETURN

;CHKCOM - PERFORM COMMON POSITIONING FOR CHKPRJ AND CHKDEF

CHKCOM:	PUSHJ	P,PRJRED	;LOOKUP PROJCT.SYS
	JUMPF	.POPJ		;IF NON-FATAL ERROR, MESSAGE IS ALREADY IN IPCF MESSAGE
	PUSHJ	P,BLDPRJ	;SEE IF WE NEED TO BUILD THE TABLE
	JUMPF	.POPJ		;IF NON-FATAL ERROR, MESSAGE IS ALREADY IN IPCF MESSAGE
	PJRST	SEARCH		;SEARCH PROJCT.SYS FOR BLOCK CONTAINING PPN

;CHKACT - ROUTINE TO LOOK IN ACTDAE.SYS.
;	AND SEE IF THE USER IS REQUIRED TO HAVE AN ACCOUNT WHEN A NULL ACCOUNT
;	HAS BEEN GIVEN IN THE IPCF MESSAGE.  IF HE IS, GIVE HIM AN INVALID
;	ACCOUNT ERROR MESSAGE.
;CALL:	PUSHJ	P,CHKACT
;	RETURN HERE

CHKACT:	MOVE	T1,PPN		;GET PPN
	PUSHJ	P,GETPRO	;DO COMMON STUFF TO FIND PPN IN ACTDAE.SYS
	JUMPF	.RETT		;ASSUME ACCOUNT STRING IS NOT REQUIRED
	MOVE	T1,.AEREQ(T1)	;GET USER'S REQUIREMENT WORD
	TXNE	T1,AE.ACT	;IS AN ACCOUNT REQUIRED FOR THIS PPN?
	$RETT			;YES--MUST VALIDATE
	$RETF			;NO VALIDATION NECESSARY

INVPPN:	SKIPA	S2,.AEPPN(T1)	;GET PPN FROM PROFILE
INVACC:	MOVE	S2,PPN		;GET PPN FROM LOW CORE
	PUSH	P,S2		;SAVE PPN
	PUSH	P,T1		;SAVE CALLER'S AC
	MOVEI	T1,ACIVA%	;INVALID ACCOUNT STRING ERROR
	PUSHJ	P,LOGUSR	;LOG THE ERROR
	POP	P,T1		;RESTORE AC
	$TEXT	(LOGFAI,<^O6R0/PPN,LHMASK/^O6R0/PPN,RHMASK/>) ;DEPENDENT INFO
	PUSHJ	P,FAIOUT	;WRITE OUT TO THE FAILURE LOG
	POP	P,S2		;GET PPN BACK
	MOVE	S1,DATADR	;GET ADDRESS OF DATA
	ADDI	S1,UV$ACT	;BEGINNING ADDRESS OF ACCOUNT TO BE VALIDATED
	FATAL	(IVA,<Invalid account "^T/(S1)/" for ^U/S2/>,ACIVA%,.RETF)
;BLDPRJ - ROUTINE TO SEE IF PROJCT.SYS HAS DATA AND TO DETERMINE IF
;	THE IN-CORE TABLE NEEDS TO BE REBUILT.  IF NOT, JUST RETURN.
;	OTHERWISE, REBUILD TABLE AND RETURN.
;CALL:	PUSHJ	P,BLDPRJ
;	RETURN HERE ALWAYS.  IF THERE'S AN ERROR, JRST TO AN ERROR ROUTINE

BLDPRJ:	SKIPN	T1,PROJCT+.RBSIZ ;IS THERE DATA IN PROJCT.SYS?
	$BOMB	<ACTNDA No data in PROJCT.SYS -- cannot validate>
	MOVE	T1,PROJCT+.RBTIM ;GET THE CREATION DATE
	EXCH	T1,Z.DATE	;STORE NEW, GET OLD
	CAMN	T1,Z.DATE	;HAS IT CHANGED?
	$RETT			;NO, WE HAVE THE CURRENT FILE IN-CORE
	SETZM	BLKNUM		;FORCE READ OF DISK
	MOVNI	T1,200		;SET LENGTH = 200 WORDS
	MOVEM	T1,PRJIOW	;FOR INITIAL READ OF INFORMATION BLOCK
	MOVEI	T1,1		;READ THE FILE/DATA INFORMATION BLOCK
	MOVEM	T1,PRJMUL	;SET LOG TO PHYS MULTIPLIER TO 1 ALSO
	MOVEM	T1,PRJCON	;CONSTANT TOO SO THAT LOGICAL 1 = PHYSICAL 1
	PUSHJ	P,READ
	SKIPT
	$BOMB	<ACTUXE Unexpected EOF in PROJCT.SYS when reading first block>
	MOVE	T1,PRJBUF+A.VERS ;CHECK THE VERSION NUMBER
	CAIE	T1,1		;WE SPEAK VERSION 1
	CAIN	T1,2		;AND VERSION 2 FORMATS
	CAIA			;ONE OF THE ABOVE
	$BOMB	<ACTVSP Version skew of PROJCT.SYS>
	MOVEM	T1,PRJVRS	;SAVE FOR LATER CHECKS
	MOVEI	T2,2		;CONSTANT = 2 FOR BLOCK CONVERSION
	CAIN	T1,1		;UNLESS VERSION 1 FORMAT
	MOVEI	T2,1		;THEN CONSTANT = 1
	MOVEM	T2,PRJCON	;STORE FOR LATER COMPUTATION
	SKIPN	T1,PRJBUF+A.WPBL ;GET WHAT PROJCT.EXE THINKS IS PRJWPB
	MOVEI	T1,200		;OLD PROJCT.SYS WAS WRITTEN AT 200 WORDS PER
	CAILE	T1,PRJWPB	;ALWAYS OK IF WE KNOW ABOUT LARGER FORMATS
	$BOMB	<ACTPTS PRJWPB too small for whats in PROJCT.SYS>
	MOVNM	T1,PRJIOW	;SAVE - WORD COUNT FOR READING BLOCKS
	LSH	T1,-7		;CONVERT TO REAL DISK BLOCKS PER LOGICAL ONE
	MOVEM	T1,PRJMUL	;SAVE MULTIPLIER FOR LOG TO PHYS CONVERSION

;FALL INTO BUILD TABLE ROUTINE
BLDPR1:	SKIPN	T1,PRJBUF+A.TLEN ;GET LENGTH OF TABLE
	$RETT			;NULL TABLE, ASSUME [*,*]=*
	CAMG	T1,Z.TLEN	;IS THE TABLE LARGER THAN BEFORE?
	JRST	[MOVEM	T1,Z.TLEN
		JRST	BLDPR3]	;NO. DON'T NEED MORE CORE. STORE LENGTH
	SKIPN	S2,Z.TADR	;HAVE WE BUILT THE TABLE BEFORE?
	JRST	BLDPR2		;NO. JRST BUILD IT
	MOVE	S1,Z.TLEN	;THE SIZE OF CORE BLOCK TO GIVE UP
	$CALL	M%RMEM		;GET RID OF THE OLD TABLE
BLDPR2:	MOVEM	T1,Z.TLEN	;STORE LENGTH OF NEW TABLE
	MOVE	S1,T1		;LENGTH OF NEW TABLE
	$CALL	M%GMEM		;GET ENOUGH CORE TO FIT THE NEW TABLE
	MOVEM	S2,Z.TADR	;SAVE THE BEGINNING ADDRESS
BLDPR3:	MOVE	T1,PRJBUF+A.FBLK ;GET BLOCK NUMBER NUMBER OF THE TABLE
	SUBI	T1,1		;BACK OFF TO PREVIOUS BLOCK
	MOVEM	T1,LSTBLK	;AND STORE AS LAST BLOCK CONTAINING DATA
	MOVE	T1,Z.TADR	;READ TABLE INTO LOW SEGMENT
	SUBI	T1,1
	MOVN	T2,Z.TLEN
	HRL	T1,T2
	MOVEM	T1,IOLIST	;SET UP COMMAND LIST FOR READING TABLE
	SETZM	IOLIST+1
	MOVE	T1,PRJBUF+A.FBLK ;GET THE BLOCK # OF THE TABLE
	SUB	T1,PRJCON	;COMPUTE READ DISK BLOCK NUMBER
	IMULI	T1,@PRJMUL	;AS ((LOGICAL-CONSTANT)*MULTIPLIER)+CONSTANT
	ADD	T1,PRJCON	;...
	MOVE	T2,PRJCHN	;AND THE CHANNEL (STORED IN LH(PRJCHN))
	PUSHJ	P,AUSETI	;POSITION TO THE BLOCK #
	SKIPT
	$BOMB	<ACTCUJ Cannot USETI to PROJCT.SYS block containing index table>
	MOVEI	T1,.FOINP	;INPUT
	HLL	T1,PRJCHN	;CHANNEL #
	TXO	T1,FO.PRV	;ALLOW FULL FILE ACCESS TO SYS:
	MOVEM	T1,PRJBLK+.FOFNC
	MOVEI	T1,IOLIST	;I/O LIST
	MOVEM	T1,PRJBLK+.FOIOS
	MOVE	T1,[2,,PRJBLK]
	FILOP.	T1,
	  SKIPA			;ERROR
	$RETT			;RETURN OK
	TXNE	T1,IO.EOF	;IS AN END OF FILE?
	$RETF			;YES.
	$BOMB	<ACTRFI Cannot read (^O/T1/) file information block of PROJCT.SYS>
;SEARCH - SEARCH THE TABLE FOR THE GIVEN PPN, READ IT IN
;CALL:	PUSHJ	P,SEARCH
;	ONLY RETURN.  IF ERROR, JRST TO THE ERROR ROUTINE

SEARCH:	MOVE	T4,PPN		;GET THE PPN TO VALIDATE FOR
	MOVE	T2,Z.TLEN	;GET LENGTH OF TABLE
	MOVE	T3,Z.TADR	;INITIALIZE ADDRESS
	HLRZ	T1,1(T3)	;GET POSSIBLE BLOCK NUMBER FOR ENTRY
	CAMLE	T4,(T3)		;IS IT IN THE FIRST BLOCK
SEARC1:	CAMN	T4,(T3)		;SAME AS FIRST PPN IN BLOCK
	JRST	SEARC2		;GO SEARCH THIS BLOCK
	CAMG	T4,(T3)		;IS IT IN PREVIOUS BLOCK
	SOJA	T1,SEARC2	;YES, GO SEARCH THAT BLOCK
	ADDI	T3,2		;DOUBLE WORD ENTRIES
	HLRZ	T1,1(T3)	;GET BLOCK NUMBER FOR THIS ENTRY
	SOS	T2		;BACK OFF THE LENGTH OF THE TABLE
	SOJG	T2,SEARC1	;AND LOOK SOME MORE
	MOVE	T1,LSTBLK	;IF NOT FOUND, MUST BE IN THE LAST BLOCK
SEARC2:	PUSHJ	P,READ		;READ IT IN
	JUMPT	.POPJ
	$BOMB	<ACTUEP Unexpected EOF in PROJCT.SYS when searching for ^P/PPN/>
;VERIFY - ROUTINE TO VALIDATE IF THE GIVEN PPN CAN USE THE GIVEN
;	ACCOUNT STRING
;CALL:	PUSHJ	P,VERIFY
;	ONLY RETURN. IF AN ERROR, JRST TO ERROR ROUTINE

VERIFY:	PUSHJ	P,.SAVE4	;SAVE P1-P4
	MOVEI	P2,BLKOFS	;GET THE WORD # OF FIRST ENTRY
	MOVE	S1,DATADR	;GET ADDRES OF THE VALIDATION MESSAGE
	MOVEI	S1,UV$ACT(S1)	;OFFSET INTO MESSAGE FOR ACCOUNT STRING
	PUSHJ	P,SYNCHK	;CHECK FOR AN ACCOUNT STRING SYNONYM IF ANY
IFN FTCASECONVERT,<		;IF CONVERTING LOWER TO UPPER
	MOVE	T1,DATADR	;GET THE ADDRESS OF THE VALIDATION MESSAGE
	MOVEI	T1,UV$ACT(T1)	;OFFSET INTO MESSAGE FOR ACCOUNT STRNG
	HRLI	T1,(POINT 7,0)	;MAKE A BYTE POINTER TO THE GIVEN ACCOUNT
	MOVEI	T2,.AACLC	;NUMBER OF CHARACTERS IN ACCOUNT STRING
VERIF0:	ILDB	T3,T1		;GET A CHARACTER
	JUMPE	T3,VERIF1	;QUIT AT END OF STRING
	CAIG	T3,"Z"+" "	;CHECK IF LOWER CASE LETTER
	CAIGE	T3,"A"+" "	;...
	JRST	VERIF5		;NOT LOWER CASE LETTER
	SUBI	T3," "		;CONVERT TO UPPER CASE
	DPB	T3,T1		;STORE BACK IN ACCOUNT STRING
VERIF5:	SOJG	T2,VERIF0	;CONTINUE FOR ALL CHARACTERS
>
VERIF1:	MOVE	P1,PPN		;GET THE PPN WE'RE VEIFYING FOR AGAIN
	XOR	P1,PRJBUF+PPNOFS(P2) ;GET THE PPN IN THE ENTRY
	AND	P1,PRJBUF+PPNOFS+1(P2) ; AND THE WILD CARD MASK
	JUMPE	P1,VERIF2	;HAVE WE FOUND A MATCH?
	HLRZ	T1,PRJBUF(P2)	;SET UP TO LOOK AT NEXT PPN ENTRY BY
	ADD	P2,T1		; GETTING RIGHT OFFSET INTO THE BLOCK
	CAMGE	P2,PRJBUF	;ARE THERE ANY MORE ENTRIES?
	JRST	VERIF1		;YES. KEEP SEARCHING
	PUSHJ	P,READNX	;READ NEXT BLOCK LOOKING FOR WILD CARDING
	SKIPT			;CHECK FOR ERRORS
	FATAL	(NAP,<No defined account string for ^U/PPN/>,ACNAP%,.RETF)
	MOVEI	P2,BLKOFS	;GET THE WORD # OF FIRST ENTRY
	JRST	VERIF1		;GO TRY TO FIND ANOTHER MATCH
VERIF2:	MOVE	P3,DATADR	;GET THE ADDRESS OF THE VALIDATION MESSAGE
	MOVEI	P3,UV$ACT(P3)	;OFFSET INTO MESSAGE FOR ACCOUNT STRING
	HRLI	P3,(POINT 7,0)	;MAKE A BYTE POINTER TO THE GIVEN ACCOUNT
	HLRZ	T1,PRJBUF+PPNOFS+CNTOFS(P2) ;GET THE CHARACTER COUNT OF ACCOUNT
	MOVNS	T1
	MOVE	T2,[POINT 7,PRJBUF+PPNOFS+ACTOFS(P2)] ;BYTE POINTER TO ACCOUNT IN ENTRY
VERIF3:	ILDB	T3,T2		;GET A CHARACTER IN THE ENTRY ACCOUNT
	ILDB	T4,P3		;GET A CHARACTER IN THE ARGUMENT ACCOUNT
	CAIN	T3,"*"		;WILDCARD THE REST OF THE USERS ACCOUNT STRING
	$RETT			;YES, DECLARE IT VALID
	CAIN	T3,"?"		;WILDCARD THIS CHARACTER
	MOVE	T3,T4		;YES, INSURE A MATCH
	CAME	T3,T4		;DO THEY MATCH?
	JRST	VERIF4		;NO. PROCEED TO NEXT ENTRY
	AOJN	T1,VERIF3	;YES. CONTINUE COMPARING THE ACCOUNTS
	ILDB	T4,P3		;GET THE NEXT CHARACTER OF THE ARGUMENT. IT
	JUMPE	T4,.RETT	; MUST BE NULL TO BE VALID
VERIF4:	MOVE	P1,PRJBUF+PPNOFS(P2) ;GET PPN ENTRY THAT MATCHED LAST TIME
	HRRZ	T1,PRJBUF(P2)	;HERE IF ACCOUNT WASN'T VALID FOR THIS ENTRY
	ADD	P2,T1		;SO GO ON TO THE NEXT ENTRY IF IT EXISTS
	CAMGE	P2,PRJBUF	;DOES IT EXIST?
	JRST	VERIF6		;YES, SEE IF STILL WITHIN SAME PPN
	MOVE	T1,PRJVRS	;GET VERSION OF PROJCT.SYS
	CAIN	T1,1		;THIS VERSION 1
	JRST	INVACC		;YES, PPNS COULDNT CROSS BLOCKS IN VERSION 1
	PUSHJ	P,READNX	;READ NEXT BLOCK TO SEE IF PPN IS CONTINUED
	JUMPF	INVACC		;NO NEXT BLOCK, ACCOUNT STRING NOT FOUND
	MOVEI	P2,BLKOFS	;RESTART AT BEGINNING OF NEW DATA BLOCK
VERIF6:	CAMN	P1,PRJBUF+PPNOFS(P2) ;ARE THE PPN'S STILL THE SAME?
	PJRST	VERIF2		;YES. COMPARE THE NEXT ACCOUNT STRING
	JRST	INVACC		;NO. VALIDATION ERROR
;FNDDEF - ROUTINE TO SEE IF DEFAULT ACCOUNT STRING EXISTS IN PROJCT.SYS
;	FOR THE REQUESTED PPN.  ALREADY KNOW THAT SPECIFIED STRING WAS NULL.
;CALL:	PUSHJ	P,FNDDEF
;	RETURN TRUE WITH UV$ACT FILLED IN WITH THE DEFAULT
;	RETURN FALSE IF NO DEFAULT FOUND

FNDDEF:	PUSHJ	P,.SAVE4	;SAVE P1-P4
	MOVEI	P2,BLKOFS	;GET THE WORD # OF FIRST ENTRY
FNDEF1:	MOVE	P1,PPN		;GET THE PPN WE'RE VEIFYING FOR AGAIN
	XOR	P1,PRJBUF+PPNOFS(P2) ;GET THE PPN IN THE ENTRY
	AND	P1,PRJBUF+PPNOFS+1(P2) ; AND THE WILD CARD MASK
	JUMPE	P1,FNDEF2	;HAVE WE FOUND A MATCH?
	HLRZ	T1,PRJBUF(P2)	;SET UP TO LOOK AT NEXT PPN ENTRY BY
	ADD	P2,T1		; GETTING RIGHT OFFSET INTO THE BLOCK
	CAMGE	P2,PRJBUF	;ARE THERE ANY MORE ENTRIES?
	JRST	FNDEF1		;YES. KEEP SEARCHING
	PUSHJ	P,READNX	;READ NEXT BLOCK (IF ANY) LOOKING FOR WILD CARDING
	JUMPF	.POPJ		;EOF. PPN DOESN'T EXIST IN PROJCT.SYS
	MOVEI	P2,BLKOFS	;GET THE WORD # OF FIRST ENTRY
	JRST	FNDEF1		;GO TRY TO FIND ANOTHER MATCH
FNDEF2:	MOVE	T1,PRJBUF+PPNOFS+CNTOFS(P2) ;FETCH CHR COUNT AND FLAGS
	TRNN	T1,1B35		;THIS THE DEFAULT
	JRST	FNDEF4		;NO, TRY ANOTHER
	HLRZS	T1		;ISOLATE STRING LENGTH
	MOVE	P3,DATADR	;GET THE ADDRESS OF THE VALIDATION MESSAGE
	MOVEI	P3,UV$ACT(P3)	;OFFSET INTO MESSAGE FOR ACCOUNT STRING
	HRLI	P3,(POINT 7,0)	;MAKE A BYTE POINTER TO THE GIVEN ACCOUNT
	MOVE	T2,[POINT 7,PRJBUF+PPNOFS+ACTOFS(P2)] ;BYTE POINTER TO ACCOUNT IN ENTRY
	SETZM	(P3)		;I KNOW IT IS A NULL ACCOUNT BUT...
	HRLI	T3,(P3)		;ZERO RECEIVING AREA ANYWAY
	HRRI	T3,1(P3)	;...
	BLT	T3,7(P3)	;...
	ILDB	T3,T2		;MOVE DEFAULT ACCOUNT STRING TO RETURN BLOCK
	IDPB	T3,P3		;...
	SOJG	T1,.-2		;...
	$RETT			;GIVE GOOD RETURN
FNDEF4:	MOVE	P1,PRJBUF+PPNOFS(P2) ;GET PPN ENTRY THAT MATCHED LAST TIME
	HRRZ	T1,PRJBUF(P2)	;HERE IF ACCOUNT WASN'T VALID FOR THIS ENTRY
	ADD	P2,T1		;SO GO ON TO THE NEXT ENTRY IF IT EXISTS
	CAMGE	P2,PRJBUF	;DOES IT EXIST?
	JRST	FNDEF5		;YES, SEE IF STILL WITHIN SAME PPN
	MOVE	T1,PRJVRS	;GET VERSION OF PROJCT.SYS
	CAIN	T1,1		;THIS VERSION 1
	$RETF			;YES, PPNS COULDNT CROSS BLOCKS IN VERSION 1
	PUSHJ	P,READNX	;READ NEXT BLOCK TO SEE IF PPN IS CONTINUED
	JUMPF	.POPJ		;NO NEXT BLOCK, DEFAULT NOT FOUND
	MOVEI	P2,BLKOFS	;RESTART AT BEGINNING OF NEW DATA BLOCK
FNDEF5:	CAMN	P1,PRJBUF+PPNOFS(P2) ;ARE THE PPN'S STILL THE SAME?
	PJRST	FNDEF2		;YES. COMPARE THE NEXT ACCOUNT STRING
	$RETF			;NO DEFAULT EXISTS
;SUBROUTINE TO READ THE NEXT BLOCK OF PROJCT.SYS.
;CALL:	PUSHJ	P,READNX
;	RETURN FALSE IF NO NEXT BLOCK
;	RETURN TRUE IF WITH BLOCK IN PRJBUF

READNX:	MOVE	T1,BLKNUM	;GET BLOCK NUMBER WE ARE LOOKING AT NOW
	ADDI	T1,1		;DOES THE NEXT BLOCK OF THE FILE CONTAIN
	CAMLE	T1,LSTBLK	; DATA?
	$RETF			;NO. THE PPN DOESN'T EXIST IN PROJCT.SYS

		;FALL INTO READ ROUTINE

;READ - ROUTINE TO READ IN A SPECIFIED BLOCK OF PROJCT.SYS.
;CALL:	MOVE	T1,BLOCK #
;	PUSHJ	P,READ
;	  ERROR RETURN.  EOF REACHED.  IF OTHER ERROR JRST TO ERROR ROUTINE.
;	GOOD RETURN

READ:	CAMN	T1,BLKNUM	;ALREADY HAVE THIS BLOCK IN CORE
	$RETT			;YES, GOOD RETURN, AVOID DISK ACTIVITY
	MOVEM	T1,BLKNUM	;SAVE THE BLOCK NUMBER WE ARE GOING TO READ IN
	SUB	T1,PRJCON	;COMPUTE READ DISK BLOCK NUMBER
	IMULI	T1,@PRJMUL	;AS ((LOGICAL-CONSTANT)*MULTIPLIER)+CONSTANT
	ADD	T1,PRJCON	;...
	MOVE	T2,PRJCHN
	PUSHJ	P,AUSETI	;POSITION TO BLOCK IN T1
	SKIPT
	$BOMB	<ACTCUB Could not USETI to a block in PROJCT.SYS>
	MOVEI	T1,.FOINP	;INPUT
	HLL	T1,PRJCHN	;GET THE CHANNEL
	TXO	T1,FO.PRV	;ALLOW FULL FILE ACCESS TO SYS:
	MOVEM	T1,PRJBLK+.FOFNC
	MOVEI	T1,IOLIST	;I/O LIST
	MOVEM	T1,PRJBLK+.FOIOS
	HRL	T1,PRJIOW	;GET - NUMBER OF WORDS TO READ
	HRRI	T1,PRJBUF-1	;FORM REST OF IOWD
	MOVEM	T1,IOLIST	;SET UP THE LIST
	SETZM	IOLIST+1
	MOVE	T1,[2,,PRJBLK]
	FILOP.	T1,
	  SKIPA			;ERROR
	$RETT
	TXNN	T1,IO.EOF	;IS IT AND END OF FILE?
	$BOMB	<ACTRBP Cannot READ (^O/T1/) PROJCT.SYS>
	SETZM	BLKNUM		;DON'T WANT TO KEEP BAD DATA AROUND
	$RETF			;INDICATE EOF.
;GETPRO - ROUTINE TO FIND A PPN'S ENTRY IN ACTDAE.SYS
;CALL:	MOVE	T1,PPN
;	PUSHJ	P,GETPRO
;	RETURN HERE--IF TRUE, T1/ADDR OF ACTDAE.SYS ENTRY FOR USER
;		   --IF FALSE, ERROR MESSAGE ALREADY BUILT

GETPRO:	TDZA	TF,TF		;EXTERNAL ENTRY POINT
GETPRX:	MOVEI	TF,1		;INTERNAL ENTRY POINT
	PUSH	P,TF		;SAVE FLAG
GETP.1:	MOVEM	T1,ACOPPN	;PPN TO GET
	PUSH	P,ACOUXP	;SAVE LOC
	MOVEI	S1,.UGPPN	;CODE FOR A PPN
	MOVEM	S1,ACOUXP	;SAVE TEMPORARILY
	PUSHJ	P,CVTWLD	;CONVERT TO WILDCARD BLOCK (S2 GETS ADDR)
	MOVEI	S1,ACOPRO	;WHERE TO READ THE RECORD
	PUSHJ	P,GETA##	;FETCH PROFILE FROM FILE "A"
	POP	P,ACOUXP	;RESTORE LOC
	JUMPF	GETP.2		;IF FAIL, SAY NO SUCH PPN
	POP	P,(P)		;PHASE STACK
	MOVEI	T1,ACOPRO	;POINT AT BLOCK WHERE PROFILE IS
	$RETT			; AND RETURN
GETP.2:	PUSH	P,S1		;SAVE THE PPN
	PUSHJ	P,RMGCHK	;CHECK FOR FATAL RMS ERRORS
	POP	P,S1		;RESTORE THE PPN
	POP	P,TF		;GET ENTRY POINT FLAG
	JUMPN	TF,.RETF	;JUMP IF INTERNAL
	SKIPE	S2,RMGCOD	;FATAL RMS ERROR?
	FATAL	(RMS,<Unexpected RMS error ^O6R0/S2/>,ACRMS%,.RETF)
	FATAL	(ILP,<Illegal PPN [^O/S1,LHMASK/,^O/S1,RHMASK/]>,ACILP%,.RETF)
SUBTTL	PROFIL - READ PROFILE AND PERFORM DEFAULTING


; THIS ROUTINE WILL FETCH A PROFILE AND DO DEFAULTING BASED ON
; THE CONTENTS OF .AEDEF AND .AEMAP.
; CALL:	MOVE	S1, ADDRESS OF BUFFER TO RETURN PROFILE
;	MOVE	S2, ADDRESS OF WILDCARD MESSAGE BLOCK
;	PUSHJ	P,PROFIL
;
; TRUE RETURN:	PROFILE READ AND DEFAULTED AS NECESSARY
; FALSE RETURN:	REQUESTED PROFILE OR DEFAULT PROFILE NOT FOUND

PROFIL:	PUSHJ	P,.SAVE4	;SAVE SOME ACS
	MOVE	P1,S1		;SAVE PROFILE BUFFER ADDRESS
	PUSHJ	P,GETA##	;FETCH REQUESTED PROFILE
	JUMPF	[PUSHJ	P,RMGCHK ;CHECK FOR FATAL RMS ERRORS
		 $RETF]		;RETURN FALSE (NO SUCH USER OR FATAL ERROR)
	PUSHJ	P,PROFMT	;CHECK ACCOUNTING FILE/PROFILE FORMAT VERSION
	JRST	PROFI0		;OK, GO DEFAULT IT

PRODEF:	PUSHJ	P,.SAVE4	;SAVE SOME ACS
	MOVE	P1,S1		;SAVE PROFILE BUFFER ADDRESS

PROFI0:	MOVE	S1,[ACODEF,,ACODEF+1] ;BLT WORD
	SETZM	ACODEF		;CLEAR START OF BLOCK
	BLT	S1,ACODEF+.AEMAX-1 ;START OFF WITH A CLEAN SLATE
	MOVE	S1,.AEDEF(P1)	;GET DEFAULT PPN WORD
	CAIN	S1,-1		;WANT DEFAULTING?
	$RETT			;NO--THAT'S EASY
	MOVSI	S1,-.AMPLW	;-LENGTH OF DEFAULT MAP
	HRRI	S1,.AEMAP(P1)	;MAKE AN AOBJN POINTER
	SKIPN	(S1)		;ANY FIELDS TO DEFAULT?
	AOBJN	S1,.-1		;NO
	JUMPGE	S1,.RETT	;RETURN IF NO DEFAULTING NECESSARY
	PUSHJ	P,PROLOD	;GO LOAD DEFAULT PROFILE
	JUMPF	.RETT		;RETURN IF CAN'T FIND PROFILE FOR DEFAULTING
	$SAVE	<T1,T2>		;DON'T CLOBBER CALLER'S ACS
	MOVE	T1,P1		;COPY USER PROFILE ADDRESS
	MOVEI	T2,ACODEF	;POINT TO DEFAULTING BLOCK
	PJRST	A$PDEF##	;GO APPLY THE DEFAULTS
; READ AND VERIFY ACCOUNTING FILE/PROFILE FORMAT VERSION NUMBER

PROFMT:	HLRZ	S1,.AEVRS(P1)	;GET VERSION
	CAIN	S1,6		;KNOWN FORMAT?
	POPJ	P,		;YES
	STOPCD	(WVR,HALT,,<Wrong accounting file format>)
; DETERMINE DEFAULT PPN AND LOAD THAT PROFILE INTO ACODEF

PROLOD:	MOVE	S1,[ACODWL,,ACODWL+1] ;SET UP BLT
	SETZM	ACODWL		;CLEAR FIRST WORD
	BLT	S1,ACODWL+UW$MIN-1 ;ZERO OUT DEFAULTING WILDCARD BLOCK
	HLLO	S1,.AEPPN(P1)	;ASSUME [10,%] WANTED
	SKIPN	S2,.AEDEF(P1)	;CONVENTIONAL DEFAULTING?
	JRST	PROLO1		;YES
	MOVE	S1,S2		;COPY POSSIBLE PPN
	TLNE	S1,-1		;FULL PPN SPECIFIED?
	JRST	PROLO1		;YES
	HRLZS	S1		;PUT PROJECT NUMBER IN PROPER PLACE
	HRR	S1,.AEPPN(P1)	;LOAD PROGRAMMER NUMBER

PROLO1:	MOVEM	S1,ACODWL+UW$PPN ;SAVE DEFAULT PPN
	SETOM	ACODWL+UW$PPM	;SET MASK
	MOVEI	S1,ACODEF	;PROFILE BUFFER
	MOVEI	S2,ACODWL	;WILDCARD BLOCK ADDRESS
	PUSHJ	P,GETA##	;FETCH DEFAULT PROFILE
	JUMPT	PROLO2		;CONTINUE IF NO ERRORS
	PUSHJ	P,RMGCHK	;CHECK FOR FATAL RMS ERRORS
				; (TELL OPERATOR, BUT KEEP TRYING)
	SKIPE	.AEDEF(P1)	;DOING CONVENTIONAL DEFAULTING?
	$RETF			;NO--CAN'T DO DEFAULTING
	MOVE	S1,ACODWL+UW$PPN ;GET PPN
	AOJE	S1,.RETF	;RETURN IF ALREADY TRIED [%,%]
	MOVNI	S1,1		;ELSE GET PPN OF LAST RESORT
	JRST	PROLO1		;AND TRY ONCE MORE

PROLO2:	PUSHJ	P,PROFMT	;CHECK ACCOUNTING FILE/PROFILE FORMAT VERSION
	$RETT			;AND RETURN
	SUBTTL	ACTIPC - SECTION TO HANDLE ALL OTHER IPCF MESSAGES


;GENERAL DEFINITIONS FOR ACTIPC MODULE
JOBNUM:	BLOCK 1			;JOB NUMBER OF USER
JOBMAX:	BLOCK 1			;MAXIMUM NUMBER OF JOBS ALLOWED TO BE LOGGED IN
				; (USUALLY THE NUMBER THE RUNNING MONITOR WAS
				; BUILT FOR).  THIS NUMBER WILL ONLY BE LESS
				; IF THE CHECKPOINT FILES CANNOT BE ALLOCATED
				; DUE TO DISK FULL PROBLEMS.  SEE THE DSKLOW ROUTINE
DEVNUM:	BLOCK 1			;nTH DEVICE JUST READ IN A JOB'S DEVICE
				; CHECKPOINT FILE
DEVAVL:	BLOCK 1			;LAST DEVICE SLOT AVAILABLE

DCHRBL:	BLOCK	.DCALT+1	;BLOCK FOR DSKCHR TO FIND ALTERATE PORT
	SUBTTL	ACTIPC - ACTLIN--ROUTINE TO HANDLE LOGIN IPCF MESSAGE

;ACTLIN - ROUTINE TO TAKE ACTION WHEN A USER LOGS IN.  ACTIONS ARE TO
;	INITIALIZE A JOB SLOT IN THE PRIMARY JOB CHECKPOINT FILE AND TRANSFER
;	INFORMATION FROM THE MESSAGE TO THE JOB SLOT.  LOGIN IS BLOCKED UNTIL
;	DATA IS WRITTEN AND AN ACK MESSAGE IS SEND BACK
;CALL:	MMSADR CONTAINS THE ADDRESS OF LOGIN IPCF DATA RECEIVED
;	MDBADR CONTAINS THE ADDRESS OF LOGIN IPCF MESSAGE DESCRIPTOR BLOCK

ACTLIN:	PUSHJ	P,IPCGEN	;FIND THE JOB NUMBER
	MOVE	T1,JOBNUM	;GET IT
	CAMLE	T1,JOBMAX	;CAN WE CHECKPOINT THIS JOB?
	FATAL	(JCE,<Job capacity exceeded>,ACJCE%,ACTVXT)
	JUMPF	[MOVE	T2,MMSADR
		MOVEM	T2,IPS.BL+SAB.MS	;ADDRESS OF DATA
		JRST	ACTLI1]
	PUSHJ	P,CBJZER	;ZERO THE PRIMARY DATA AREA
	PUSHJ	P,GTTTYS	;GET INITIAL TTY STATISTICS NUMBERS
	PUSHJ	P,CPJCOP	;ZERO AND INCLUDE INIT TTY STATS IN AUX AREA
	PUSHJ	P,CPJLIN	;TRANSFER THE DATA RECEIVED
	HRL	T1,JOBNUM	;GET THE JOB NUMBER
	HRRI	T1,.GTJLT	;JOB LOGIN TIME
	GETTAB	T1,		;GET IT
	  SETZ	T1,		;WHAT!
	MOVEM	T1,CPJBUF+CJLGTM ;RECORD FOR RESTART CHECK
	PUSHJ	P,WRITJP	;WRITE THE PRIMARY JOB SLOT
	SKIPE	QUEFLG		;QUEUE. UUO?
	JRST	[PUSHJ	P,QUEACK ;YES--ACK DIFFERENTLY
		 JUMPF	ACTLI2	;CHECK FOR ERRORS
		 $RETT]		;ELSE RETURN
	MOVE	T2,MMSADR	;USE THE SAME PAGE WE RECEIVED
	MOVEM	T2,IPS.BL+SAB.MS	;ADDRESS OF DATA
	MOVEI	T1,UGTRU$	;INDICATE ALL OK
	MOVEM	T1,UC$RES(T2)
ACTLI1:	MOVE	T1,ACKCOD	;GET THE UNIQUE MESSAGE IDENTIFIER
	MOVEM	T1,UC$ACK(T2)	;PUT IN THE MESSAGE
	MOVEI	T1,UGVAC$	;INDICATE RESPONSE MESSAGE
	MOVEM	T1,UC$TYP(T2)
	MOVE	T1,MDBADR	;ADDRESS OF MESSAGE DESCRIPTOR BLOCK
	MOVE	T1,MDB.SP(T1)	;GET PID OF THE JOB LOGGING IN
	MOVEM	T1,IPS.BL+SAB.PD
	MOVEI	T1,1000		;INDICATE A PAGE IS BEING SENT
	MOVEM	T1,IPS.BL+SAB.LN
	MOVEI	S1,SAB.SZ	;LENGTH OF SEND ARGUMENT BLOCK
	MOVEI	S2,IPS.BL	;ADDRESS OF SEND ARGUMENT BLOCK
	$CALL	C%SEND		;SEND THE MESSAGE, GLXLIB
	JUMPT	.POPJ		;PROPOGATE TRUE RETURN
	CAIN	S1,ERNSP$
	JRST	ACTLI2		;IF NO PID, ASSUME USER CONTROL-C'D OUT OF LOGIN
	MOVE	T1,MDBADR	;MESSAGE DESCRIPTOR BLOCK
	MOVE	T2,MDB.SD(T1)	;SENDER'S PPN
	MOVE	T3,MDB.PV(T1)
	ANDX	T3,MD.PJB	;JOB NUMBER OF SENDER
	$WTOXX	<Error (^E/S1/) sending a LOGIN response to job ^D/T3/ user ^P/T2/>
ACTLI2:	PUSHJ	P,CBJZER	;ZERO THE JOB SLOT BUFFER OF PRIMARY FILE
	PUSHJ	P,WRITJP	;AND WRITE IT TO ENSURE DATA INTEGRITY
	$RETT
;CPJLIN - ROUTINE TO TAKE DATA FROM THE LOGIN IPCF MESSAGE AND PUT IT INTO CPJBUF.
;	NOTICE THAT THIS ROUTINE ASSUMES THAT PART OF THE LOGIN IPCF MESSAGE
;	HAS THE SAME FORMAT AS CPJBUF.

CPJLIN:	MOVE	T1,DATADR	;GET ADDRESS OF DATA
	MOVE	T2,UL$ACK(T1)	;GET THE SENDER'S UNIQUE MESSAGE IDENTIFIER
	MOVEM	T2,ACKCOD	;AND SAVE IT FOR THE RESPONSE MESSAGE
	HRLI	T2,UL$LIN(T1)
	HRRI	T2,CPJBUF+CLINNO
	BLT	T2,CPJBUF+CTERDE ;MOVE LARGE CHUNK OF DATA
	SETO	T1,		;GET A -1
	CAMN	T1,CPJBUF+CACCT	;WAS THERE ONE SPECIFIED TO LOGIN
	SETZM	CPJBUF+CACCT	;NO, AVOID JUNK IN USAGE.OUT
	CAMN	T1,CPJBUF+CRMRK	;CHECK /REMARK TOO
	SETZM	CPJBUF+CRMRK

;NOW DO ITEMIZED DATA TRANSFER

	MOVE	T1,.JBVER	;ACCOUNT DAEMON VERSION NUMBER
	MOVEM	T1,CPJBUF+CACVER
	MOVE	T1,JOBNUM	;SENDERS JOB NUMBER
	MOVEM	T1,CPJBUF+CJOB
	MOVE	T1,CPJBUF+CSESST
	MOVEM	T1,CPJBUF+CLSTCK ;IN CASE SYSTEM CRASHES BEFORE CHECKPOINTING THIS JOB
	POPJ	P,
	SUBTTL	ACTIPC - ACTSES--ROUTINE TO HANDLE SESSION IPCF MESSAGE

;ACTSES - ROUTINE TO TAKE ACTION WHEN A USER TYPES A SESSION COMMAND.  ACTIONS
;	ARE TO MAKE A SESSION ENTRY, COPY THE PRIMARY BUFFER CPJBUF TO CAJBUF,
;	THE AUXILLIARY BUFFER, UPDATE CPJBUF AND WRITE BOTH BUFFERS.
;	LOGIN IS BLOCKED BECAUSE A CHECKPOINT MUST BE MADE.
;CALL:	MDBADR CONTAINS THE MESSAGE DESCRIPTOR BLOCK ADDRESS
;	MMSADR CONTAINS THE ADDRESS OF THE IPCF MESSAGE RECEIVED.

ACTSES:	PUSHJ	P,IPCGEN	;DO GENERAL DATA SETUP
	MOVE	T1,JOBNUM	;GET JOB NUMBER
	CAMLE	T1,JOBMAX	;CAN WE CHECKPOINT THIS JOB?
	FATAL	(JCE,<Job capacity exceeded by job ^D/T1/>,ACJCE%,ACTVXT)
	PUSHJ	P,CHKJOB	;GATHER ALL THE DATA
	PUSHJ	P,MAKSES	;NOW MAKE THE SESSION ENTRY
	MOVEI	T1,SESDVS	;POINT TO ROUTINE TO MAKE ENTRIES
	SKIPE	CPJBUF+CDEVFL	;ANY DEVICES FOR THIS JOB
	PUSHJ	P,ALLDEV	;CALL IT FOR ALL USER DEVICES
	PUSHJ	P,CPJCOP	;COPY THE PRIMARY DATA TO AUXILLIARY BUFFER
	PUSHJ	P,CPJSES	;COPY NEW DATA TO CPJBUF FROM IPCF MESSAGE
	PUSHJ	P,WRITJP	;WRITE THE BUFFER.
	SKIPE	QUEFLG		;QUEUE. UUO?
	PJRST	QUEACK		;YES--ACK AND RETURN
;	JRST	SNDRSP		;FALL INTO STANDARD RESPONSE STUFF


SNDRSP:	MOVE	T2,MMSADR	;USE THE SAME PAGE WE RECEIVED
	MOVEM	T2,IPS.BL+SAB.MS	;ADDRESS OF DATA
	MOVE	T1,ACKCOD	;GET THE UNIQUE MESSAGE IDENTIFIER
	MOVEM	T1,UC$ACK(T2)
	MOVEI	T1,UGTRU$	;INDICATE ALL OK
	MOVEM	T1,UC$RES(T2)
	MOVEI	T1,UGVAC$	;INDICATE RESPONSE MESSAGE
	MOVEM	T1,UC$TYP(T2)
	MOVE	T1,MDBADR	;ADDRESS OF MESSAGE DESCRIPTOR BLOCK
	MOVE	T1,MDB.SP(T1)	;GET PID OF THE JOB
	MOVEM	T1,IPS.BL+SAB.PD
	MOVEI	T1,1000		;INDICATE A PAGE IS BEING SENT
	MOVEM	T1,IPS.BL+SAB.LN
	MOVEI	S1,SAB.SZ	;LENGTH OF SEND ARGUMENT BLOCK
	MOVEI	S2,IPS.BL	;ADDRESS OF SEND ARGUMENT BLOCK
	$CALL	C%SEND		;SEND THE MESSAGE, GLXLIB
	JUMPT	.POPJ		;PROPOGATE TRUE RETURN
	MOVE	T1,MDBADR	;MESSAGE DESCRIPTOR BLOCK
	MOVE	T2,MDB.SD(T1)	;SENDER'S PPN
	MOVE	T3,MDB.PV(T1)
	ANDX	T3,MD.PJB	;JOB NUMBER OF SENDER
	$WTOXX	<Error (^E/S1/) sending response to job ^D/T3/ user ^P/T2/>
	$RETT
;CPJSES - ROUTINE TO COPY DATA FROM THE IPCF MESSAGE (SENT BECAUSE OF A
;	SESSION COMMAND TYPED BY A USER) TO THE PRIMARY CHECKPOINT BUFFER (CPJBUF).

CPJSES:	MOVE	T1,.JBVER	;ACCOUNT DAEMON VERSION NUMBER
	MOVEM	T1,CPJBUF+CACVER
	MOVE	T1,DATADR	;ADDRESS OF DATA
	MOVE	T2,US$ACK(T1)	;GET THE UNIQUE MESSAGE IDENTIFIER
	MOVEM	T2,ACKCOD	;AND SAVE IT FOR THE RESPONSE MESSAGE
	MOVE	T2,US$PRG(T1)	;PROGRAM NAME
	MOVEM	T2,CPJBUF+CPGNAM
	MOVE	T2,US$VER(T1)	;PROGRAM VERSION NUMBER
	MOVEM	T2,CPJBUF+CPGVER
	MOVE	T2,US$ACT(T1)	;GET FIRST WORD OF ACCOUNT STRING
	AOJE	T2,CPJSE1	; (SENT AS -1 IS NO CHANGE IN ACCOUNT STRING)
	MOVEI	T2,CPJBUF+CACCT	;NEW ACCOUNT
	HRLI	T2,US$ACT(T1)
	BLT	T2,CPJBUF+CACCT+7
CPJSE1:	MOVE	T2,US$BEG(T1)	;SESSION START DATE/TIME
	MOVEM	T2,CPJBUF+CSESST
	MOVEM	T2,CPJBUF+CLSTCK ;IN CASE SYSTEM CRASHES BEFORE NEXT CHECKPOINT
	MOVE	T2,US$RMK(T1)	;GET REMARK
	AOJE	T2,.POPJ	; (SENT AS -1 IF NO CHANGE IN REMARK)
	MOVEI	T2,CPJBUF+CRMRK	;NEW SESSION REMARK
	HRLI	T2,US$RMK(T1)
	BLT	T2,CPJBUF+CRMRK+7
	POPJ	P,

;ROUTINE CALLED FROM ACTSES (VIA ALLDEV) TO MAKE DEVICE SESSION ENTRIES

SESDVS:	PUSHJ	P,@[EXP CHKFSR,CHKMTA,CHKDTA,CHKSPN]-1(P1) ;CHECKPOINT IT
	PUSHJ	P,@[EXP MAKFSR,MAKMAG,MAKDEC,MAKSPN]-1(P1) ;MAKE THE ENTRY
	PUSHJ	P,CPDCOP	;MOVE CURRENT DATA TO AUX AREA
	MOVE	T1,CURDTM	;GET CURRENT DATE/TIME
	MOVEM	T1,@[EXP CPDBUF+FSESST,CPDBUF+MSESST,CPDBUF+DSESST,CPDBUF+SCSHIF]-1(P1)
	CAIN	P1,SPNTYP	;NO SPINDLE RECORDS IN JOB FILES
	PUSHJ	P,ACTIDT	;CRASH CAUSE THERES NO ACCOUNT STRING FOR THEM
	MOVE	T2,DATADR	;NEW ACCOUNT STRING IN MESSAGE FROM LOGIN
	MOVE	T1,US$ACT(T2)	;GET FIRST WORD OF ACCOUNT STRING
	AOJE	T1,SESDV1	;SENT AS -1 IF NO CHANGE IN ACCOUNT STRING
	MOVSI	T1,US$ACT(T2)	;SOURCE = NEW ACCOUNT STRING
	HRR	T1,[EXP CPDBUF+FACCT,CPDBUF+MACCT,CPDBUF+DACCT]-1(P1)
	BLT	T1,@[EXP CPDBUF+FACCT+7,CPDBUF+MACCT+7,CPDBUF+DACCT+7]-1(P1)
SESDV1:	PUSHJ	P,WRITDP	;WRITE IT BACK OUT
	POPJ	P,		;RETURN FOR NEXT DEVICE
;ACTATT - ROUTINE TO TAKE ACTION WHEN A USER ATTACHES TO HIS JOB WITH AN
;	ATTACH COMMAND.  ONLY THE NEW LINE NUMBER AND NODE NAME (IF ANY)
;	ARE COPYED TO THE JOB SLOT OF THE PRIMARY JOB CHECKPOINT FILE (CPJBUF).
;	NO SESSION ENTRY IS MADE.  MMSADR AND MDBADR ARE ALREADY SET UP.

ACTATT:	PUSHJ	P,.SAVE1	;SAVE P1
	PUSHJ	P,IPCGEN	;DO GENERAL DATA SETUP
	MOVE	P1,DATADR	;ADDRESS OF THE DATA FROM LOGIN
	MOVE	P1,UA$TJN(P1)	;GET TARGET JOB NUMBER
	CAMLE	P1,JOBMAX	;CAN WE CHECKPOINT THIS JOB?
	FATAL	(JCE,<Job capacity exceeded by job ^D/P1/>,ACJCE%,ACTVXT)
	EXCH	P1,JOBNUM	;FETCH SENDERS JOB NUMBER, STORE TARGET
	CAMLE	P1,JOBMAX	;CAN WE CHECKPOINT THIS JOB?
	FATAL	(JCE,<Job capacity exceeded by job ^D/P1/>,ACJCE%,ACTVXT)
	PUSHJ	P,READJP	;READ IT IN
	PUSHJ	P,CPJATT	;COPY NEW DATA TO CPJBUF FROM IPCF MESSAGE
	PUSHJ	P,DOFDTT	;COMPUTE UNBILLED TTY STATS FOR OLD TERMINAL
	EXCH	P1,JOBNUM	;GET STATS FOR TTY WHERE LOGIN IS NOW
	PUSHJ	P,GTTTYS	; THAT IS WHERE THE TARGET JOB WILL BE
	MOVE	T1,CPJBUF+CTTCMD ;COMMANDS ON THIS TERMINAL
	SUB	T1,SESBLK+CTTCMD ;NUMBER LEFT OUT OF LAST BILLING SESSION
	MOVEM	T1,CAJBUF+CTTCMD ;OFFSET SO WILL BE INCLUDED ON NEXT BILL
	MOVE	T1,CPJBUF+CTTYI  ;INPUT CHARACTER COUNTS
	SUB	T1,SESBLK+CTTYI
	MOVEM	T1,CAJBUF+CTTYI
	MOVE	T1,CPJBUF+CTTYO  ;OUTPUT CHARACTER COUNTS
	SUB	T1,SESBLK+CTTYO
	MOVEM	T1,CAJBUF+CTTYO
	MOVE	T1,CPJBUF+CTTYBR ;BREAK CHARACTER COUNTS
	SUB	T1,SESBLK+CTTYBR
	MOVEM	T1,CAJBUF+CTTYBR
	MOVEM	P1,JOBNUM	;WRITE DATA FOR TARGET JOB
	PUSHJ	P,WRITJP	;WRITE THE BUFFER.
	SKIPN	QUEFLG		;QUEUE. UUO?
	PJRST	SNDRSP		;NO--SEND A RESPONSE AND RETURN
	PJRST	QUEACK		;ACK APPROPRIATELY AND RETURN


;CPJATT - ROUTINE TO COPY DATA FROM THE IPCF MESSAGE (SENT BECAUSE OF A
;	ATTACH COMMAND TYPED BY A USER) TO THE PRIMARY CHECKPOINT BUFFER (CPJBUF).

CPJATT:	MOVE	T1,.JBVER	;ACCOUNT DAEMON VERSION NUMBER
	MOVEM	T1,CPJBUF+CACVER
	MOVE	T1,DATADR	;ADDRESS OF DATA
	MOVE	T2,UA$ACK(T1)	;GET UNIQUE MESSAGE IDENTIFIER
	MOVEM	T2,ACKCOD	;SAVE IT FOR THE RESPONSE MESSAGE
	MOVE	T2,UA$PRG(T1)	;PROGRAM NAME
	MOVEM	T2,CPJBUF+CPGNAM
	MOVE	T2,UA$VER(T1)	;PROGRAM VERSION NUMBER
	MOVEM	T2,CPJBUF+CPGVER
	MOVE	T2,UA$LIN(T1)	;LINE NUMBER
	MOVEM	T2,CPJBUF+CLINNO
	MOVE	T2,UA$NOD(T1)	;NODE NAME
	MOVEM	T2,CPJBUF+CNODE
	MOVE	T2,UA$TDE(T1)	;TERMINAL DESIGNATOR
	MOVEM	T2,CPJBUF+CTERDE
	POPJ	P,
;ACTOUT - ROUTINE TO TAKE ACTION WHEN THE MONITOR SENDS THE ACCOUNT DAEMON A
;	LOGOUT MESSAGE.  NOTE THAT THIS CAN HAPPEN EVEN IF A USER DOES NOT LOG
;	IN (E.G., HE TYPES A ^C BEFORE LOGIN DOES THE LOGIN UUO).

ACTOUT:	SKIPE	GFRFLG		;MUST BE FROM [SYSTEM]GOPHER
	SKIPE	QUEFLG		;AND NOT A USER'S QUEUE. UUO
	JRST	NOPRV		;WRONG. GIVE 'NOT PRIVILEGED' ERROR
	MOVE	T1,DATADR	;MESSAGE ADDRESS
	MOVE	T1,LGO.JB(T1)
	ANDX	T1,LG.JOB	;JOB NUMBER DOING LOGOUT UUO
	HLRZS	T1		;PUT JOB NUMBER IN RIGHT HALF
	MOVEM	T1,JOBNUM	;STORE IT
	CAMLE	T1,JOBMAX	;CAN WE CHECKPOINT THIS JOB?
	JRST	ACTOT1		;NO, JUST ACK [SYSTEM]GOPHER
	PUSHJ	P,CHKJOB	;GATHER ALL THE DATA
	PUSHJ	P,MAKSES	;CHECKPOINT THE JOB AND MAKE A SESSION ENTRY
	HRROI	T1,LGODVS	;ROUTINE TO MAKE DEVICE ENTRIES
	PUSHJ	P,ALLDEV	;CALL IT FOR ALL DEVICES THAT WE KNOW ABOUT
	PUSHJ	P,CBJZER	;NOW ZERO THE PRIMARY SLOT
	PUSHJ	P,CPJCOP	;COPY TO AUXILLIARY SLOT
	PUSHJ	P,WRITJP	;WRITE ZEROES TO PRIMARY CHECKPOINT FILE
ACTOT1:	MOVE	T1,MDBADR	;MESSAGE DESCRIPTOR BLOCK
	MOVE	T1,MDB.SP(T1)	;PID OF [SYSTEM]GOPHER
	MOVEM	T1,IPS.BL+SAB.PD ;STORE IT IN THE SEND ARGUMENT BLOCK
	MOVEI	T1,1		;LENGTH OF MESSAGE
	MOVEM	T1,IPS.BL+SAB.LN
	MOVE	T1,MMSADR	;ADDRESS OF DATA
	MOVEM	T1,IPS.BL+SAB.MS
	MOVE	T2,JOBNUM	;JOB NUMBER OF USER LOGGING OUT
	MOVEM	T2,(T1)		;TELL [SYSTEM]GOPHER
	MOVEI	S1,SAB.SZ
	MOVEI	S2,IPS.BL
	$CALL	C%SEND
	JUMPT	.POPJ		;RETURN IF OK
	MOVE	T1,MDBADR	;MESSAGE DESCRIPTOR BLOCK
	MOVE	T2,MDB.SD(T1)	;SENDER'S PPN
	MOVE	T3,MDB.PV(T1)
	ANDX	T3,MD.PJB	;JOB NUMBER OF SENDER
	$WTOXX	<Error (^E/S1/) sending LOGOUT response to job ^D/T3/ user ^P/T2/>
	$RETT

;ROUTINE TO MAKE THE ENTRIES FOR THE DEVICES OWNED BY THE JOB LOGGING OUT
;  CAN'T CHECKPOINT HERE CAUSE DEVICES WERE RELEASED BEFORE WE SEE THIS JOB

LGODVS:	MOVE	T1,CURDTM	;GET CURRENT DATE/TIME (ONLY THING TO CHECKPOINT)
	MOVEM	T1,@[EXP CPDBUF+FLSTCK,CPDBUF+MLSTCK,CPDBUF+DLSTCK,CPDBUF+SLSTCK]-1(P1)
	PUSHJ	P,@[EXP MAKFSR,MAKMAG,MAKDEC,MAKSPN]-1(P1) ;MAKE THE ENTRY
	PUSHJ	P,CBDZER	;CLEAR THE BLOCK
	PUSHJ	P,CPDCOP	;BOTH HALVES
	PUSHJ	P,WRITDP	;ZAP THE DISK AREA
	POPJ	P,		;AND RETURN FOR THE NEXT
;ACTFDM - Routine called when a mount message for a user file structure
;	is received.
;Call:	MDBADR contains the message descriptor address
;	MMSADR contains the message address

ACTFDM:	MOVE	T2,MMSADR	;ADDRESS OF MESSAGE
	MOVE	T1,UF$JOB(T2)	;GET THE JOB NUMBER OF THE USER
	CAMLE	T1,JOBMAX	;CAN WE CHECKPOINT THIS USER?
	FATAL	(JCE,<Job capacity exceeded by job ^D/T1/>,ACJCE%,ACTVXT)
	MOVEM	T1,JOBNUM	;STORE IT FOR THE CHECKPOINT FILE NAME
	MOVE	T1,UF$DEV(T2)	;FILE STRUCTURE NAME
	PUSHJ	P,FNDDEV	;FIND A ZERO DEVICE SLOT IN THE CHECKPOINT FILE
	JUMPT	ACTFD1		;WHAT, ALREADY FOUND, SOMEONE IS CONFUSED
	PUSHJ	P,CBDZER	;CLEAR OUT THE DEVICE AREA
	PUSHJ	P,CPDCOP	;AND THE SESSION/CSHIFT AREA
	PUSHJ	P,CPDFDM	;COPY MESSAGE DATA TO CPDBUF
	MOVE	T1,DEVAVL
	MOVEM	T1,DEVNUM	;WRITE THE DEVICE AREA
	PUSHJ	P,WRITDP
	PUSHJ	P,READJP	;READ IN JOB PROPER DATA
	AOS	CPJBUF+CDEVFL	;INCREMENT DEVICE COUNT
	PUSHJ	P,WRITJP	;AND RE-WRITE THE DATA
ACTFD1:	PUSHJ	P,CPDCLS	;CLOSE THE FILE
;	JRST	SNDACK		;FALL INTO STANDARD SEND ACK CODE


SNDACK:	MOVE	T2,MMSADR	;MESSAGE ADDRESS
	MOVE	T1,.MSFLG(T2)	;FLAGS WORD
	TXNN	T1,MF.ACK	;DID HE WANT AN ACK?
	JRST	IGNORE		;NO. RELEASE MESSAGE AND RETURN
	MOVE	T1,[.MSCOD+1,,UGACK$]
	MOVEM	T1,.MSTYP(T2)
	MOVX	T1,MF.NOM	;JUST ACK THE MESSAGE
	MOVEM	T1,.MSFLG(T1)	;PRESERVE THE CODE IN .MSCOD
	MOVE	T3,MDBADR	;MESSAGE DESCRIPTOR ADDRESS
	MOVE	T1,MDB.SP(T3)	;PID TO SEND TO
	MOVEM	T1,IPS.BL+SAB.PD
	LOAD 	T1,.MSTYP(T2),MS.CNT	;LENGTH OF MESSAGE
	MOVEM	T1,IPS.BL+SAB.LN
	MOVEI	S1,SAB.SZ	;SEND ARGUMENT BLOCK LENGTH
	MOVEI	S2,IPS.BL	;SEND ARUGMENT BLOCK ADDRESS
	$CALL	C%SEND
	JUMPT	.POPJ		;PROPOGATE TRUE RETURN
	$WTOXX	<Error (^E/S1/) sending ACK message>
	$RETT
;CPDFDM - Routine to copy data from user file structure mount message to CPDBUF.

CPDFDM:	MOVE	T2,MMSADR	;MESSAGE ADDRESS
	MOVEI	T1,FSRTYP	;DEVICE TYPE
	MOVEM	T1,CPDBUF+DEVTYP
	MOVE	T1,UF$DEV(T2)	;DEVICE NAME
	MOVEM	T1,CPDBUF+DEVICE
	MOVE	T1,UF$JOB(T2)	;JOB NUMBER OF USER
	MOVEM	T1,CPDBUF+FJOB
	MOVE	T1,UF$TRD(T2)	;TERMINAL DESIGNATOR
	MOVEM	T1,CPDBUF+FTERDE
	MOVE	T1,.JBVER	;ACTDAE VERSION NUMBER
	MOVEM	T1,CPDBUF+FACVER
	$CALL	I%NOW		;GET CURRENT DATE/TIME
	MOVEM	S1,CPDBUF+FLSTCK ;INITIALIZE LAST CHECKPOINT DATE/TIME
	MOVEM	S1,CPDBUF+FSESST ;SESSION START DATE/TIME
	MOVE	T1,UF$TNO(T2)	;LINE NUMBER OF USER
	MOVEM	T1,CPDBUF+FLINNO
	MOVE	T1,UF$PNM(T2)	;PROGRAM NAME (PULSAR)
	MOVEM	T1,CPDBUF+FPGNAM
	MOVE	T1,UF$PVR(T2)	;PROGRAM'S VERSION NUMBER
	MOVEM	T1,CPDBUF+FPGVER
	MOVE	T1,UF$NOD(T2)	;NODE NAME OF USER
	MOVEM	T1,CPDBUF+FNODE
	HRLI	T1,UF$ACT(T2)	;ACCOUNT STRING
	HRRI	T1,CPDBUF+FACCT
	BLT	T1,CPDBUF+FACCT+7
	MOVE	T1,UF$PPN(T2)	;USER'S PROJECT PROGRAMMER NUMBER
	MOVEM	T1,CPDBUF+FPPN
	DMOVE	T3,UF$NM1(T2)	;USER'S NAME
	DMOVEM	T3,CPDBUF+FNAME1
	MOVE	T1,UF$STY(T2)	;TYPE OF FILE STRUCTURE
	MOVEM	T1,CPDBUF+FFSTYP
	MOVE	T1,UF$PNO(T2)	;NUMBER OF PACKS IN FILE STRUCTURE
	MOVEM	T1,CPDBUF+FPCKNO
	MOVE	T1,UF$CTY(T2)	;CONTROLLER TYPE
	MOVEM	T1,CPDBUF+FCONTY
	MOVE	T1,UF$DTY(T2)	;DEVICE TYPE
	MOVEM	T1,CPDBUF+FDEVTY
	MOVE	T1,UF$DSP(T2)	;DISPOSITION
	MOVEM	T1,CPDBUF+FDISPO
	HRLI	T1,UF$TXT(T2)	;TEXT TO EXPLAIN DISPOSITION
	HRRI	T1,CPDBUF+FOTEXT
	BLT	T1,CPDBUF+FOTEXT+7
	MOVE	T1,UF$CDT(T2)	;CREATION DATE/TIME OF MOUNT REQUEST
	MOVEM	T1,CPDBUF+FCREDT
	MOVE	T1,UF$SDT(T2)	;SCHEDULED DATE/TIME OF MOUNT REQUEST
	MOVEM	T1,CPDBUF+FSCHDT
	MOVE	T1,UF$VDT(T2)	;SERVICED DATE/TIME OF MOUNT REQUEST
	MOVEM	T1,CPDBUF+FSERDT
	MOVE	T1,UF$CBR(T2)	;MOUNT COUNT BEFORE REQUEST
	MOVEM	T1,CPDBUF+FMNTCT
	MOVE	T1,UF$ACC(T2)	;ACCESS TYPE
	MOVEM	T1,CPDBUF+FACCES
	POPJ	P,
;ACTFDD - Routine called when a dismount message for a user file structure
;	is received.
;Call:	MDBADR contains the message descriptor address
;	MMSADR contains the message address

ACTFDD:	MOVE	T2,MMSADR	;ADDRESS OF MESSAGE
	MOVE	T1,UF$JOB(T2)	;JOB NUMBER OF USER
	CAMLE	T1,JOBMAX	;CAN WE CHECKPOINT THIS USER?
	FATAL	(JCE,<Job capacity exceeded by job ^D/T1/>,ACJCE%,ACTVXT)
	MOVEM	T1,JOBNUM	;STORE FOR THE CHECKPOINT FILE NAME
	MOVE	T1,UF$DEV(T2)	;FILE STRUCTURE NAME
	PUSHJ	P,FNDDEV	;FIND THE DEVICE ENTRY
	JUMPF	ACTFD2		;DIDN'T FOUND THE FILE STRUCTURE ENTRY
	PUSHJ	P,CPDFDD	;COPY DATA FROM THE MESSAGE
	$CALL	DATIM		;GET CURRENT DATE/TIME
	PUSHJ	P,CHKFSR	;CHECKPOINT THE DATA
	PUSHJ	P,MAKFSR	;GO MAKE AN ENTRY
	PUSHJ	P,CBDZER	;ZERO THE DEVICE AREA
	PUSHJ	P,CPDCOP	;COPY IN CASE OF AUXILLIARY DATA
	PUSHJ	P,WRITDP	;WRITE IT OUT
	PUSHJ	P,READJP	;READ IN JOB PROPER DATA
	SOSG	CPJBUF+CDEVFL	;DECREMENT DEVICE COUNT
	PUSHJ	P,CPDDEL	;FILE IS NOW EMPTY, DELETE IT
	PUSHJ	P,WRITJP	;AND RE-WRITE THE DATA
ACTFD2:	PUSHJ	P,CPDCLS	;CLOSE THE FILE
	PJRST	SNDACK		;SEND ACK AND RETURN


;CPDFDD - Routine to copy data from user file structure dismount message to
;	CPDBUF.

CPDFDD:	MOVE	T2,MMSADR	;MESSAGE ADDRESS
	MOVE	T1,UF$SCT(T2)	;MOUNT COUNT AFTER DISMOUNT
	MOVEM	T1,CPDBUF+FDISCT
	POPJ	P,
;ACTMGM - Routine called when a mount message for a user magtape is received.
;Call:	MDBADR contains the message descriptor address
;	MMSADR contains the message address

ACTMGM:	MOVE	T2,MMSADR	;ADDRESS OF MESSAGE
	MOVE	T1,UM$JOB(T2)	;GET THE JOB NUMBER OF THE USER
	CAMLE	T1,JOBMAX	;CAN WE CHECKPOINT THIS USER?
	FATAL	(JCE,<Job capacity exceeded by job ^D/T1/>,ACJCE%,ACTVXT)
	MOVEM	T1,JOBNUM	;STORE IT FOR THE CHECKPOINT FILE NAME
	MOVE	T1,UM$DEV(T2)	;DEVICE NAME
	PUSHJ	P,FNDDEV	;FIND A ZERO DEVICE SLOT IN THE CHECKPOINT FILE
	JUMPT	ACTMG1		;ALREADY FOUND, SOMEONE IS CONFUSED
	PUSHJ	P,CBDZER	;CLEAR OUT THE DEVICE AREA
	PUSHJ	P,CPDCOP	;AND THE SESSION/CSHIFT AREA
	PUSHJ	P,CPDMGM	;COPY MESSAGE DATA TO CPDBUF
	MOVE	T1,DEVAVL
	MOVEM	T1,DEVNUM	;WRITE THE DEVICE AREA
	PUSHJ	P,WRITDP
	PUSHJ	P,READJP	;READ IN JOB PROPER DATA
	AOS	CPJBUF+CDEVFL	;INCREMENT DEVICE COUNT
	PUSHJ	P,WRITJP	;AND RE-WRITE THE DATA
ACTMG1:	PUSHJ	P,CPDCLS	;CLOSE THE FILE
	PJRST	SNDACK		;SEND ACK AND RETURN
;CPDMGM - Routine to copy data from user magtape mount message to CPDBUF.

CPDMGM:	MOVE	T2,MMSADR	;MESSAGE ADDRESS
	MOVEI	T1,MAGTYP	;DEVICE TYPE
	MOVEM	T1,CPDBUF+DEVTYP
	MOVE	T1,UM$DEV(T2)	;DEVICE NAME
	MOVEM	T1,CPDBUF+DEVICE
	MOVE	T1,UM$JOB(T2)	;JOB NUMBER OF USER
	MOVEM	T1,CPDBUF+MJOB
	MOVE	T1,UM$TRD(T2)	;TERMINAL DESIGNATOR
	MOVEM	T1,CPDBUF+MTERDE
	MOVE	T1,.JBVER	;ACTDAE VERSION NUMBER
	MOVEM	T1,CPDBUF+MACVER
	$CALL	I%NOW		;GET CURRENT DATE/TIME
	MOVEM	S1,CPDBUF+MLSTCK ;INITIALIZE LAST CHECKPOINT DATE/TIME
	MOVEM	S1,CPDBUF+MSESST ;SESSION START DATE/TIME
	MOVE	T1,UM$TNO(T2)	;LINE NUMBER OF USER
	MOVEM	T1,CPDBUF+MLINNO
	MOVE	T1,UM$PNM(T2)	;PROGRAM NAME (PULSAR)
	MOVEM	T1,CPDBUF+MPGNAM
	MOVE	T1,UM$PVR(T2)	;PROGRAM'S VERSION NUMBER
	MOVEM	T1,CPDBUF+MPGVER
	MOVE	T1,UM$NOD(T2)	;NODE NAME OF USER
	MOVEM	T1,CPDBUF+MNODE
	HRLI	T1,UM$ACT(T2)	;ACCOUNT STRING
	HRRI	T1,CPDBUF+MACCT
	BLT	T1,CPDBUF+MACCT+7
	MOVE	T1,UM$PPN(T2)	;USER'S PROJECT PROGRAMMER NUMBER
	MOVEM	T1,CPDBUF+MPPN
	DMOVE	T3,UM$NM1(T2)	;USER'S NAME
	DMOVEM	T3,CPDBUF+MNAME1
	MOVE	T1,UM$CTY(T2)	;CONTROLLER TYPE
	MOVEM	T1,CPDBUF+MCONTY
	MOVE	T1,UM$DSP(T2)	;DISPOSITION
	MOVEM	T1,CPDBUF+MDISPO
	HRLI	T1,UM$TXT(T2)	;TEXT TO EXPLAIN DISPOSITION
	HRRI	T1,CPDBUF+MOTEXT
	BLT	T1,CPDBUF+MOTEXT+7
	MOVE	T1,UM$CDT(T2)	;CREATION DATE/TIME OF MOUNT REQUEST
	MOVEM	T1,CPDBUF+MCREDT
	MOVE	T1,UM$SDT(T2)	;SCHEDULED DATE/TIME OF MOUNT REQUEST
	MOVEM	T1,CPDBUF+MSCHDT
	MOVE	T1,UM$VDT(T2)	;SERVICED DATE/TIME OF MOUNT REQUEST
	MOVEM	T1,CPDBUF+MSERDT
	MOVE	T1,UM$VID(T2)	;VOLUME ID IN VOL1 LABEL
	MOVEM	T1,CPDBUF+MVOLID
	MOVE	T1,UM$RID(T2)	;REEL ID
	MOVEM	T1,CPDBUF+MRELID
	MOVE	T1,UM$LTY(T2)	;LABEL TYPE
	MOVEM	T1,CPDBUF+MLABEL
	MOVE	T1,UM$LST(T2)	;VOLUME LABEL STATE
	MOVEM	T1,CPDBUF+MSTATE
	MOVE	T1,UM$FSI(T2)	;FILE SET IDENTIFIER
	MOVEM	T1,CPDBUF+MFSTID
	POPJ	P,
;ACTMGD - Routine called when a dismount message for a user magtape is received.
;Call:	MDBADR contains the message descriptor address
;	MMSADR contains the message address

ACTMGD:	MOVE	T2,MMSADR	;ADDRESS OF MESSAGE
	MOVE	T1,UM$JOB(T2)	;JOB NUMBER OF USER
	CAMLE	T1,JOBMAX	;CAN WE CHECKPOINT THIS USER?
	FATAL	(JCE,<Job capacity exceeded by job ^D/T1/>,ACJCE%,ACTVXT)
	MOVEM	T1,JOBNUM	;STORE FOR THE CHECKPOINT FILE NAME
	MOVE	T1,UM$DEV(T2)	;DEVICE NAME
	PUSHJ	P,FNDDEV	;FIND THE DEVICE ENTRY
	JUMPF	ACTMG2		;DIDN'T FOUND THE MAGTAPE ENTRY
	PUSHJ	P,CPDMGD	;COPY DATA FROM THE MESSAGE
	$CALL	DATIM		;GET CURRENT DATE/TIME
	MOVEM	S1,CPDBUF+MLSTCK ;STORE AS TIME OF LAST CHECKPOINT
	PUSHJ	P,MAKMAG	;GO MAKE AN ENTRY
	PUSHJ	P,CBDZER	;ZERO THE DEVICE AREA
	PUSHJ	P,CPDCOP	;COPY IN CASE OF AUXILLIARY DATA
	PUSHJ	P,WRITDP	;WRITE IT OUT
	PUSHJ	P,READJP	;READ IN JOB PROPER DATA
	SOSG	CPJBUF+CDEVFL	;DECREMENT DEVICE COUNT
	PUSHJ	P,CPDDEL	;FILE IS NOW EMPTY, DELETE IT
	PUSHJ	P,WRITJP	;AND RE-WRITE THE DATA
ACTMG2:	PUSHJ	P,CPDCLS	;CLOSE THE FILE
	PJRST	SNDACK		;SEND ACK AND RETURN


;CPDMGD - Routine to copy data from user magtape dismount message to CPDBUF.

CPDMGD:	MOVE	T2,MMSADR	;ADDRESS OF MESSAGE
	MOVE	T1,UM$MRD(T2)	;CHARACTERS READ
	MOVEM	T1,CPDBUF+MMREAD
	MOVE	T1,UM$MWR(T2)	;CHARACTERS WRITTEN
	MOVEM	T1,CPDBUF+MMWRIT
	MOVE	T1,UM$RRD(T2)	;RECORDS READ
	MOVEM	T1,CPDBUF+MRECRD
	MOVE	T1,UM$SRE(T2)	;SOFT READ ERRORS
	MOVEM	T1,CPDBUF+MNOSRE
	MOVE	T1,UM$SWE(T2)	;SOFT WRITE ERRORS
	MOVEM	T1,CPDBUF+MNOSWE
	MOVE	T1,UM$HRE(T2)	;HARD READ ERRORS
	MOVEM	T1,CPDBUF+MNOHRE
	MOVE	T1,UM$HWE(T2)	;HARD WRITE ERRORS
	MOVEM	T1,CPDBUF+MNOHWE
	POPJ	P,
;ACTDTM - Routine called when a mount message for a user DECtape is received.
;Call:	MDBADR contains the message descriptor address
;	MMSADR contains the message address

ACTDTM:	MOVE	T2,MMSADR	;ADDRESS OF MESSAGE
	MOVE	T1,UD$JOB(T2)	;GET THE JOB NUMBER OF THE USER
	CAMLE	T1,JOBMAX	;CAN WE CHECKPOINT THIS USER?
	FATAL	(JCE,<Job capacity exceeded by job ^D/T1/>,ACJCE%,ACTVXT)
	MOVEM	T1,JOBNUM	;STORE IT FOR THE CHECKPOINT FILE NAME
	MOVE	T1,UD$DEV(T2)	;DEVICE NAME
	PUSHJ	P,FNDDEV	;FIND A ZERO DEVICE SLOT IN THE CHECKPOINT FILE
	JUMPT	ACTDT1		;ALREADY HAVE IT, SOMEBODY IS CONFUSED
	PUSHJ	P,CBDZER	;CLEAR OUT THE DEVICE AREA
	PUSHJ	P,CPDCOP	;AND THE SESSION/CSHIFT AREA
	PUSHJ	P,CPDDTM	;COPY MESSAGE DATA TO CPDBUF
	MOVE	T1,DEVAVL
	MOVEM	T1,DEVNUM	;WRITE THE DEVICE AREA
	PUSHJ	P,WRITDP
	PUSHJ	P,READJP	;READ IN JOB PROPER DATA
	AOS	CPJBUF+CDEVFL	;INCREMENT DEVICE COUNT
	PUSHJ	P,WRITJP	;AND RE-WRITE THE DATA
ACTDT1:	PUSHJ	P,CPDCLS	;CLOSE THE FILE
	PJRST	SNDACK		;SEND ACK AND RETURN
;CPDDTM - Routine to copy data from user DECtape mount message to CPDBUF.

CPDDTM:	MOVE	T2,MMSADR	;MESSAGE ADDRESS
	MOVEI	T1,DECTYP	;DEVICE TYPE
	MOVEM	T1,CPDBUF+DEVTYP
	MOVE	T1,UD$DEV(T2)	;DEVICE NAME
	MOVEM	T1,CPDBUF+DEVICE
	MOVE	T1,UD$JOB(T2)	;JOB NUMBER OF USER
	MOVEM	T1,CPDBUF+DJOB
	MOVE	T1,UD$TRD(T2)	;TERMINAL DESIGNATOR
	MOVEM	T1,CPDBUF+DTERDE
	MOVE	T1,.JBVER	;ACTDAE VERSION NUMBER
	MOVEM	T1,CPDBUF+DACVER
	$CALL	I%NOW		;GET CURRENT DATE/TIME
	MOVEM	S1,CPDBUF+DLSTCK ;INITIALIZE LAST CHECKPOINT DATE/TIME
	MOVEM	S1,CPDBUF+DSESST ;SESSION START DATE/TIME
	MOVE	T1,UD$TNO(T2)	;LINE NUMBER OF USER
	MOVEM	T1,CPDBUF+DLINNO
	MOVE	T1,UD$PNM(T2)	;PROGRAM NAME (PULSAR)
	MOVEM	T1,CPDBUF+DPGNAM
	MOVE	T1,UD$PVR(T2)	;PROGRAM'S VERSION NUMBER
	MOVEM	T1,CPDBUF+DPGVER
	MOVE	T1,UD$NOD(T2)	;NODE NAME OF USER
	MOVEM	T1,CPDBUF+DNODE
	HRLI	T1,UD$ACT(T2)	;ACCOUNT STRING
	HRRI	T1,CPDBUF+DACCT
	BLT	T1,CPDBUF+DACCT+7
	MOVE	T1,UD$PPN(T2)	;USER'S PROJECT PROGRAMMER NUMBER
	MOVEM	T1,CPDBUF+DPPN
	DMOVE	T3,UD$NM1(T2)	;USER'S NAME
	DMOVEM	T3,CPDBUF+DNAME1
	MOVE	T1,UD$DSP(T2)	;DISPOSITION
	MOVEM	T1,CPDBUF+DDISPO
	HRLI	T1,UD$TXT(T2)	;TEXT TO EXPLAIN DISPOSITION
	HRRI	T1,CPDBUF+DOTEXT
	BLT	T1,CPDBUF+DOTEXT+7
	MOVE	T1,UD$CDT(T2)	;CREATION DATE/TIME OF MOUNT REQUEST
	MOVEM	T1,CPDBUF+DCREDT
	MOVE	T1,UD$SDT(T2)	;SCHEDULED DATE/TIME OF MOUNT REQUEST
	MOVEM	T1,CPDBUF+DSCHDT
	MOVE	T1,UD$VDT(T2)	;SERVICED DATE/TIME OF MOUNT REQUEST
	MOVEM	T1,CPDBUF+DSERDT
	MOVE	T1,UD$VID(T2)	;VOLUME ID IN VOL1 LABEL
	MOVEM	T1,CPDBUF+DVOLID
	MOVE	T1,UD$RID(T2)	;REEL ID
	MOVEM	T1,CPDBUF+DRELID
	POPJ	P,
;ACTDTD - Routine called when a dismount message for a user DECtape is received.
;Call:	MDBADR contains the message descriptor address
;	MMSADR contains the message address

ACTDTD:	MOVE	T2,MMSADR	;ADDRESS OF MESSAGE
	MOVE	T1,UD$JOB(T2)	;JOB NUMBER OF USER
	CAMLE	T1,JOBMAX	;CAN WE CHECKPOINT THIS USER?
	FATAL	(JCE,<Job capacity exceeded by job ^D/T1/>,ACJCE%,ACTVXT)
	MOVEM	T1,JOBNUM	;STORE FOR THE CHECKPOINT FILE NAME
	MOVE	T1,UD$DEV(T2)	;DEVICE NAME
	PUSHJ	P,FNDDEV	;FIND THE DEVICE ENTRY
	JUMPF	ACTDT2		;DIDN'T FOUND THE DECTAPE ENTRY
	PUSHJ	P,CPDDTD	;COPY DATA FROM THE MESSAGE
	$CALL	DATIM		;GET CURRENT DATE/TIME
	PUSHJ	P,CHKDTA	;CHECKPOINT THE DATA
	PUSHJ	P,MAKDEC	;GO MAKE AN ENTRY
	PUSHJ	P,CBDZER	;ZERO THE DEVICE AREA
	PUSHJ	P,CPDCOP	;COPY IN CASE OF AUXILLIARY DATA
	PUSHJ	P,WRITDP	;WRITE IT OUT
	PUSHJ	P,READJP	;READ IN JOB PROPER DATA
	SOSG	CPJBUF+CDEVFL	;DECREMENT DEVICE COUNT
	PUSHJ	P,CPDDEL	;FILE IS NOW EMPTY, DELETE IT
	PUSHJ	P,WRITJP	;AND RE-WRITE THE DATA
ACTDT2:	PUSHJ	P,CPDCLS	;CLOSE THE FILE
	PJRST	SNDACK		;SEND ACK AND RETURN


;CPDDTD - Routine to copy data from user DECtape dismount message to CPDBUF.

CPDDTD:	POPJ	P,
;ACTSPM - Routine called when a mount message for a disk spindle is received.
;Call:	MDBADR contains the message descriptor address
;	MMSADR contains the message address

ACTSPM:	MOVE	T2,MMSADR	;ADDRESS OF MESSAGE
	SETZM	JOBNUM		;JOB NUMBER 0 IS USED FOR SPINDLES
	MOVE	T1,US$DEV(T2)	;DISK UNIT NAME
	PUSHJ	P,FNDDEV	;FIND A ZERO DEVICE SLOT IN THE CHECKPOINT FILE
	JUMPT	ACTSP1		;ALREADY HAVE IT, SOMEONE IS CONFUSED
	PUSHJ	P,CBDZER	;CLEAR OUT THE DEVICE AREA
	PUSHJ	P,CPDCOP	;AND THE SESSION/CSHIFT AREA
	PUSHJ	P,CPDSPM	;COPY MESSAGE DATA TO CPDBUF
	MOVE	T1,DEVAVL
	MOVEM	T1,DEVNUM	;WRITE THE DEVICE AREA
	PUSHJ	P,WRITDP
ACTSP1:	PUSHJ	P,CPDCLS	;CLOSE THE FILE
	PJRST	SNDACK		;SEND ACK AND RETURN
;CPDSPM - Routine to copy data from disk spindle mount message to CPDBUF.

CPDSPM:	MOVE	T2,MMSADR	;MESSAGE ADDRESS
	MOVEI	T1,SPNTYP	;DEVICE TYPE
	MOVEM	T1,CPDBUF+DEVTYP
	MOVE	T1,US$DEV(T2)	;DISK UNIT NAME
	MOVEM	T1,CPDBUF+DEVICE
	MOVEM	T1,DCHRBL+.DCNAM	;GET THE ALTERNATE PORT IF IT EXISTS
	MOVE	T1,[.DCALT+1,,DCHRBL]
	DSKCHR	T1,
	  JFCL
	MOVE	T1,DCHRBL+.DCALT	;GET ALTERNATE PORT
	MOVEM	T1,CPDBUF+ALTPRT
	MOVE	T1,US$JOB(T2)	;JOB NUMBER OF PULSAR
	MOVEM	T1,CPDBUF+SJOB
	MOVE	T1,US$TRD(T2)	;TERMINAL DESIGNATOR
	MOVEM	T1,CPDBUF+STERDE
	MOVE	T1,.JBVER	;ACTDAE VERSION NUMBER
	MOVEM	T1,CPDBUF+SACVER
	$CALL	I%NOW		;GET CURRENT DATE/TIME
	MOVEM	S1,CPDBUF+SLSTCK ;INITIALIZE LAST CHECKPOINT DATE/TIME
	MOVEM	S1,CPDBUF+SCSHIF ;USED FOR CONNECT TIME IN CASE OF CSHIFT
	MOVE	T1,US$TNO(T2)	;LINE NUMBER OF PULSAR
	MOVEM	T1,CPDBUF+SLINNO
	MOVE	T1,US$PNM(T2)	;PROGRAM NAME (PULSAR)
	MOVEM	T1,CPDBUF+SPGNAM
	MOVE	T1,US$PVR(T2)	;PROGRAM'S VERSION NUMBER
	MOVEM	T1,CPDBUF+SPGVER
	MOVE	T1,US$NOD(T2)	;NODE NAME OF PULSAR
	MOVEM	T1,CPDBUF+SNODE
	MOVE	T1,US$STY(T2)	;TYPE OF FILE STRUCTURE
	MOVEM	T1,CPDBUF+SFSTYP
	MOVE	T1,US$PNO(T2)	;NUMBER OF PACKS IN FILE STRUCTURE
	MOVEM	T1,CPDBUF+SPCKNO
	MOVE	T1,US$CTY(T2)	;CONTROLLER TYPE
	MOVEM	T1,CPDBUF+SCONTY
	MOVE	T1,US$DTY(T2)	;DEVICE TYPE
	MOVEM	T1,CPDBUF+SDEVTY
	MOVE	T1,US$DTM(T2)	;DATE/TIME PACK WAS SPUN UP
	MOVEM	T1,CPDBUF+SMNTDT
	MOVE	T1,US$DPI(T2)	;DISK PACK IDENTIFIER
	MOVEM	T1,CPDBUF+SPAKID
	MOVE	T1,US$FSN(T2)	;FILE STRUCTURE NAME
	MOVEM	T1,CPDBUF+SFSNAM
	MOVE	T1,US$MTH(T2)	;M OF N COUNT
	MOVEM	T1,CPDBUF+SPKMTH
	POPJ	P,
;ACTSPD - Routine called when a dismount message for a disk spindle is received.
;Call:	MDBADR contains the message descriptor address
;	MMSADR contains the message address

ACTSPD:	MOVE	T2,MMSADR	;ADDRESS OF MESSAGE
	SETZM	JOBNUM		;USE JOB 0 FOR SPINDLE USAGE
	MOVE	T1,US$DEV(T2)	;FILE STRUCTURE NAME
	PUSHJ	P,FNDDEV	;FIND THE DEVICE ENTRY
	JUMPF	ACTSP2		;DIDN'T FOUND THE FILE STRUCTURE ENTRY
	PUSHJ	P,CPDSPD	;COPY DATA FROM THE MESSAGE
	$CALL	DATIM		;GET CURRENT DATE/TIME
	PUSHJ	P,CHKSPN	;CHECKPOINT THE SPINDLE
	PUSHJ	P,MAKSPN	;GO MAKE AN ENTRY
	PUSHJ	P,CBDZER	;ZERO THE DEVICE AREA
	PUSHJ	P,CPDCOP	;COPY IN CASE OF AUXILLIARY DATA
	PUSHJ	P,WRITDP	;WRITE IT OUT
ACTSP2:	PUSHJ	P,CPDCLS	;CLOSE THE FILE
	PJRST	SNDACK		;SEND ACK AND RETURN


;CPDSPD - Routine to copy data from disk spindle spin-down message to CPDBUF.

CPDSPD:	POPJ	P,
;ACTCHD - Routine called when the operator has changed the Date/Time
;Call:	DTCPSI+.PSVIS contains the date/time offset

ACTCHD:	MOVE	T1,DTCPSI+.PSVIS;GET DATE/TIME OFFSET
	MOVEM	T1,DTMOFS	;STORE FOR LATER ADJUSTMENT
	PUSHJ	P,CHKAJB	;CHECKPOINT ALL STUFF, ADJUSTING FOR NEW TIME
	MOVM	T1,DTCPSI+.PSVIS;GET OFFSET BETWEEN TIMES
	HLRZM	T1,DAEOFD	;STORE NUMBER OF DAYS
	TLZ	T1,-1		;CLEAR DAYS
	MUL	T1,[^D24*^D60*^D60] ;CONVERT TO SECONDS
	DIV	T1,[1,,0]	;...
	TRNE	T2,1B18		;SHOULD IT BE ROUNDED
	AOS	T1		;YES
	MOVEM	T1,DAEOFS	;STORE OFFSET IN SECONDS
	MOVE	T1,MONJNO	;ACTDAE'S JOB NUMBER
	PUSHJ	P,SETTNL	;FIGURE OUT WHERE WE ARE
	PUSHJ	P,DATIM		;FETCH CURRENT DATE/TIME
	SUB	S1,DTMOFS	;ADJUST CURRENT TIME BY OFFSET
	MOVEM	S1,DAEODT	;STORE AS OLD DATE/TIME
	SETZM	DTMOFS		;BACK TO NORMAL TIME
	MOVEI	T1,.UTTAD	;ENTRY = DATE/TIME CHANGE
	MOVEI	DEFADR,DTMDFS	;POINT TO THE DEFUS LIST
	PUSHJ	P,MAKENT	;MAKE THE ENTRY
	$RETT			;SO DON'T BOTHER SENDING IT ONE
;THE DEFUS LIST FOR DATE/TIME ENTRY

DTMDFS:	USJNO.	(MONJNO)	;OUR JOB NUMBER
	USTAD.	(CURDTM)	;THE DATE/TIME
	USTRM.	(MONTDE)	;OUR TERMINAL DESIGNATOR
	USLNO.	(MONLNO)	;OUR LINE NUMBER
	USNOD.	(MONNOD)	;OUR NODE NAME
	USPNM.	([SIXBIT /ACTDAE/])	;ACTDAES PROGRAM NAME
	USPVR.	(.JBVER)	;AND VERSION NUMBER
	USAMV.	(.JBVER)	;ACTDAE VERSION NUMBER
	USODT.	(DAEODT)	;OLD DATE/TIME
	USOFD.	(DAEOFD)	;OFFSET IN DAYS
	USOFS.	(DAEOFS)	;OFFSET IN SECONDS
	0			;AND A ZERO TO TERMINATE THE LIST

DAEODT:	BLOCK	1		;SPACE TO HOLD INFORMATION
DAEOFD:	BLOCK	1		;...
DAEOFS:	BLOCK	1		;...
;ACTDUE - ROUTINE CALLED UPON RECEIPT OF A DISK USAGE MESSAGE FROM BACKUP
;CALL:	MMSADR CONTAINS THE MESSAGE ADDRESS

;THIS ROUTINE "KNOWS" THE FORMAT OF AN ENTRY TO BE MADE IN USAGE.OUT AND
;	SINCE DISK RECORDS HAVE AND EXTENSIBLE FORMAT (LOTS OF RECORD 3'S)
;	IT FAKES OUT MAKENT TO DO ITS BIDDING.  BEWARE OF THIS IF SOMEONE
;	CHANGES THE NUMBER OR POSITION OF RECORDS IN A DISK USAGE ENTRY.

ACTDUE:	SETZM	MAKDUE		;CLEAR FLAG
	MOVE	T1,MMSADR	;POINT TO RECEIVED MESSAGE
	HRLI	T2,UB$ACN(T1)	;MOVE DATA FROM MESSAGE
	HRRI	T2,DUEBLK	;TO SCRATCH STORAGE
	BLT	T2,DUEBLK+UB$ACT-UB$ACN-1 ;CAUSE DEFUS LIST CANT HANDLE INDEXING
	PUSHJ	P,DATIM		;SET UP CURRENT DATE/TIME
	SOS	@ENTRYS##-1+.UTFLU ;REDUCE ENTRY TO 2 RECORDS
	MOVEI	T1,.UTFLU	;MAKE DISK USAGE ENTRY
	MOVEI	DEFADR,DUELST	;POINT TO THE DEFUS LIST FOR THE FIRST 2 RECORDS
	PUSHJ	P,MAKENT	;AND MAKE THEM
	AOS	@ENTRYS##-1+.UTFLU ;BACK TO 3 RECORDS IN THE ENTRY
	SETOM	MAKDUE		;FLAG WE ONLY WANT THE LAST ON THIS TIME
	MOVE	P1,MMSADR	;POINT TO THE MESSAGE AGAIN
	MOVEI	P1,UB$ACT(P1)	;POINT TO THE FIRST ACCOUNT STRING SECTION
ACTDU1:	SOSGE	DUEBLK+UB$ACN-UB$ACN ;DONE YET?
	JRST	ACTDU2		;YES, CLEAN UP AND RETURN
	HRLI	T1,UB$ACT-UB$ACT(P1) ;PREPARE TO MOVE THE DATA
	HRRI	T1,DU1BLK	;FROM THE MESSAGE TO THE BLOCK
	BLT	T1,DU1BLK+UB$END-UB$ACT-1 ;POINTED TO BY THE DEFUS LIST
	MOVEI	T1,.UTFLU	;MAKE DISK USAGE ENTRY AGAIN (ONLY 3RD RECORD)
	MOVEI	DEFADR,DU1LST	;POINT TO THE DEFUS LIST
	PUSHJ	P,MAKENT	;AND MAKE THE ENTRY
	ADDI	P1,UB$END-UB$ACT ;STEP TO THE NEXT ACCOUNT STRING DATA
	JRST	ACTDU1		;AND MAKE ANY MORE
ACTDU2:	SETZM	MAKDUE		;DONE FAKING OUT MAKENT
	$RETT			;AND RETURN

;DEFUS LIST FOR DISK USAGE ENTRIES

DUELST:
	USJNO.	(DUEBLK+UB$JOB-UB$ACN) ;JOB NUMBER
	USTAD.	(CURDTM)		;CURRENT DATE/TIME
	USTRM.	(DUEBLK+UB$TRD-UB$ACN) ;TERMINAL DESIGNATOR
	USLNO.	(DUEBLK+UB$TNO-UB$ACN) ;TERMINAL NUMBER
	USPNM.	(DUEBLK+UB$PNM-UB$ACN) ;PROGRAM NAME
	USPVR.	(DUEBLK+UB$PVR-UB$ACN) ;PROGRAM VERSION NUMBER
	USAMV.	(.JBVER)		;ACCOUNT DAEMON VERSION NUMBER
	USNOD.	(DUEBLK+UB$NOD-UB$ACN) ;NODE NUMBER
	USNRF.	(DUEBLK+UB$ACN-UB$ACN) ;NUMBER OF RECORDS FOLLOWING
	USTAL.	(DUEBLK+UB$TAU-UB$ACN) ;TOTAL ALLOCATED DISK SPACE
	USTUS.	(DUEBLK+UB$TWU-UB$ACN) ;TOTAL WRITTEN DISK SPACE
	USTNF.	(DUEBLK+UB$TNF-UB$ACN) ;TOTAL NUMBER OF FILES
	USDFS.	(DUEBLK+UB$FSN-UB$ACN) ;FILE STRUCTURE NAME
	USPPN.	(DUEBLK+UB$PPN-UB$ACN) ;PPN
	USSTP.	(DUEBLK+UB$FST-UB$ACN) ;FILE STRUCTURE TYPE
	USKTP.	(DUEBLK+UB$CNT-UB$ACN) ;CONTROLLER TYPE
	USDTP.	(DUEBLK+UB$DVT-UB$ACN) ;DEVICE TYPE
	USLIQ.	(DUEBLK+UB$QIN-UB$ACN) ;LOGGED IN QUOTA
	USLOQ.	(DUEBLK+UB$QOU-UB$ACN) ;LOGGED OUT QUOTA
	USLLG.	(DUEBLK+UB$LLG-UB$ACN) ;DATE/TIME OF LAST LOGIN (OLD FORMAT)
	USLAT.	(DUEBLK+UB$LAT-UB$ACN) ;DATE/TIME OF LAST ACCOUNTING
	USUPF.	(DUEBLK+UB$UPF-UB$ACN) ;UFD WAS PROTECTED FLAG
	USFPF.	(DUEBLK+UB$FPF-UB$ACN) ;SOME FILES WERE PROTECTED FLAG
	USTMA.	(DUEBLK+UB$ABO-UB$ACN) ;ACCOUNT BUFFER OVERFLOW IN IPCF MESSAGE
	USEXP.	(DUEBLK+UB$EXP-UB$ACN) ;EXPIRED PPN FLAG
	USFON.	([ASCII/N/])		;NEVER FILES ONLY
	0				;AND A ZERO TO TERMINATE THE LIST

DU1LST:
	USDFS.	(DUEBLK+UB$FSN-UB$ACN) ;FILE STRUCTURE NAME
	USPPN.	(DUEBLK+UB$PPN-UB$ACN) ;PPN
	USDFT.	(DUEBLK+UB$FST-UB$ACN) ;FILE STRUCTURE TYPE
	USDKT.	(DUEBLK+UB$CNT-UB$ACN) ;CONTROLLER TYPE
	USDDT.	(DUEBLK+UB$DVT-UB$ACN) ;DEVICE TYPE
	USDAC.	(DU1BLK+UB$ACT-UB$ACT) ;ACCOUNT STRING
	USALC.	(DU1BLK+UB$BAL-UB$ACT) ;BLOCKS ALLOCATED
	USUSG.	(DU1BLK+UB$BWR-UB$ACT) ;BLOCKS WRITTEN
	USFIL.	(DU1BLK+UB$NFL-UB$ACT) ;NUMBER OF FILES
	0				;AND A ZERO TO TERMINATE THE LIST

MAKDUE:	BLOCK	1		;FLAG SAYING SECOND (THRU N'TH) CALL TO MAKENT
DUEBLK:	BLOCK	UB$ACT-UB$ACN	;AREA TO HOLD DATA FROM MESSAGE
DU1BLK:	BLOCK	UB$END-UB$ACT	;AREA TO HOLD INDIVIDUAL ACCOUNT RECORDS
	SUBTTL	ACTIPC - GENERAL ROUTINES


;IPCGEN - ROUTINE TO DO ALL GENERAL STORAGE AND COMPUTING NEEDED FOR JOB-SPECIFIC
;	IPCF MESSAGES. MDBADR AND MMSADR CONTAIN ALL THAT IS NECESSARY.

IPCGEN:	MOVE	T1,MDBADR	;ADDRESS OF MESSAGE DESCRIPTOR BLOCK
	MOVE	T2,MDB.PV(T1)	;SENDER'S JOB NUMBER
	ANDX	T2,MD.PJB
	MOVEM	T2,JOBNUM	;SAVE IT
	POPJ	P,
	SUBTTL	ACTCHK - SECTION TO HANDLE JOB AND DEVICE CHECKPOINT FILES

;GENERAL DEFINITIONS FOR CHECKPOINT MODULE

DTMOFS:	BLOCK 1			;DATE/TIME OFFSET. IF NON-ZERO, ADJUST DATA ITEMS
CURDTM:	BLOCK 1			;CURRENT DATE/TIME --FILLED IN BY ROUTINE DATIM
CHKNDX:	BLOCK 1			;INDEX INTO TABLE DESCRIBING CHECKPOINT TIMES

;THE FOLLOWING DEVICE TYPES ARE DEFINED FOR SYMBOLIC REFERENCES BUT SOME
;	TABLES AND DISPATCH VECTORS "KNOW" THE ORDER. DON'T CHANGE THESE.

FSRTYP==1		;INDICATES THE DEVICE AREA IS FOR A FILE STRUCTURE
MAGTYP==2		;INDICATES THE DEVICE AREA IS FOR A MAGTAPE
DECTYP==3		;INDICATES THE DEVICE AREA IS FOR A DECTAPE
SPNTYP==4		;INDICATES THE DEVICE AREA IS FOR A SPINDLE
	SUBTTL	ACTCHK - GENERAL DEFINITIONS FOR CHECKPOINT MODULE

;*********************************************************************
;	JOB CHECKPOINT FILE (PRIMARY AND AUXILLIARY) DEFINITIONS
;*********************************************************************

CPJFIL==1		;BLOCK WHERE GENERAL CHECKPOINT FILE INFORMATION IS STORED
JBOFFS==1		;OFFSET TO ADD TO JOB NUMBER TO FIND WHAT BLOCK JOB'S
			; INFORMATION IS STORED.

;STORAGE FOR PRIMARY JOB CHECKPOINT FILE CALLED USEJOB.BIN
CPJCHN:	BLOCK 1		;CHANNEL NUMBER OF PRIMARY JOB CHECKPOINT FILE
			; # IS POSITIONED WHERE THE FILOP EXPECTS IT (FO.CHN)
CPJBLK:	BLOCK 10	;FILOP. BLOCK USED FOR THE PRIMARY JOB CHECKPOINT FILE
USEJOB:	BLOCK .RBSIZ+1	;LOOKUP BLOCK FOR CPJBLK (USEJOB.BIN)
CPJGEN:	BLOCK 200	;BUFFER WHERE THE CHECKPOINT FILE INFORMATION BLOCK
			; WILL BE READ INTO.

RECLM1:			;WHERE READ DATA ENDS

;*****************************************************************
;* * * * * * * FORMAT OF CPJGEN * * * * * * * *
;*****************************************************************
	PHASE 0
	BLOCK 1			;RESERVE WORD 0 OF THE BLOCK
LASTCH:	BLOCK 1			;DATE/TIME OF THE LAST CHECKPOINT DONE.
FILMJB:	BLOCK 1			;NUMBER OF JOBS THIS FILE WAS BUILT FOR
FILBPJ:	BLOCK 1			;BLOCKS PER JOB IN CHECKPOINT FILE
FILBPD:	BLOCK 1			;BLOCKS PER DEVICE IN jjjDEV.BIN FILE(S)
	DEPHASE
;*****************************************************************
;* * * * * * * END OF CPJGEN FORMAT * * * * * * *
;*****************************************************************

;*****************************************************************
;* * * * * * FORMAT OF CPJBUF AND CAJBUF * * * * * * * *
;*****************************************************************
;	EACH JOB SLOT IN THE CHECKPOINT FILE CONTAINS THE PRIMARY AND AUXILLIARY
;	AREAS.  EACH JOB REQUIRES "N" DISK BLOCKS AS DETERMINED BY THE SIZE OF
;	THE PRIMARY AREA (CBUFLN) * 2.  THE AUXILLIARY AREA (FOR SESSION) BEGINS
;	AFTER THE PRIMARY AREA ROUNDED UP TO NEXT 100 WORD BOUNDRY FOR EXPANSION.
;	WHEN THE NUMBER OF BLOCKS REQUIRED FOR A JOB CHANGES, THE OLD USEJOB.BIN
;	FILE IS INVALID AND MUST BE DELETED BEFORE THE NEW VERSION OF THE ACTDAE
;	CAN BE USED.  TO AVOID LOSING DATA IN THE TRANSITION BETWEEN VERSIONS,
;	SCHEDULE A KSYS FOR THE SYSTEM, ATTACH THE OLD ACTDAE AFTER IT IS COMPLETE,
;	RUN THE OLD VERSION AGAIN (TO GET ACCOUNTING FOR [OPR] JOBS), DELETE
;	THE OLD USEJOB.BIN, PUT THE NEW VERSION ON SYS, AND RELOAD THE MONITOR.
;*****************************************************************

	PHASE 0

;THE FOLLOWING ITEMS ARE UPDATED WITH EACH CHECKPOINT IN THE PRIMARY CHECKPOINT
;	FILE.  THEY ARE ALSO THE MINUEND (PRIMARY CHECKPOINT FILE) AND THE
;	SUBTRAHEND (AUXILLIARY CHECKPOINT FILE) USED WHEN MAKING ANY SESSION
;	ENTRY.  NOTE THAT ANY CONVERSION/CALCULATIONS NEEDED WILL BE DONE
;	WHEN THE SESSION ENTRY IS BEING APPENDED TO THE USAGE FILE

CJOB:	BLOCK 1			;JOB NUMBER OF USER
CRUNTM:	BLOCK 1			;RUNTIME (TEN-MICROSECOND UNITS)
CDREAD:	BLOCK 1			;DISK READS (BLOCKS)
CDWRIT:	BLOCK 1			;DISK WRITES (BLOCKS)
CCTI:	BLOCK 1			;CORE-TIME INTEGRAL (KILO-CORE TICKS)
CVCTI:	BLOCK 1			;VIRTUAL CORE-TIME INTEGRAL (KILO-CORE TICKS)
CEBOX:	BLOCK 1			;EBOX (JIFFIES)
CMBOX:	BLOCK 1			;MBOX (JIFFIES)
CMCALL:	BLOCK 1			;MONITOR CALLS
CTTYI:	BLOCK 1			;TERMINAL INPUT CHARACTERS
CTTYO:	BLOCK 1			;TERMINAL OUTPUT CHARACTERS
CTTYBR:	BLOCK 1			;COUNT OF BREAK CHARACTERS USER TYPED
CTTCMD:	BLOCK 1			;MONITOR COMMAND COUNT
CQUTIM:	BLOCK 1			;TIME IN RUN QUEUE (USED TO CALCULATE RUN QUEUE QUOTIENT)

CEND:			;LENGTH OF VARIABLE DATA ( USED FOR SESBLK )

CACVER:	BLOCK 1			;VERSION OF ACTDAE (%%.ACV)
CLSTCK:	BLOCK 1			;DATE/TIME OF LAST CHECKPOINT
CJLGTM:	BLOCK 1			;LOGIN TIME OF THE JOB (FOR RESTART)
CDEVFL:	BLOCK 1			;FLAG INDICATING A DEVICE CHECKPOINT FILE EXISTS

;THE FOLLOWING ITEMS ARE NOT CHANGED DURING A CHECKPOINT BUT ARE NEEDED TO BE
;	RECORDED HERE IN CASE OF A SYSTEM CRASH SO A INCOMPLETE SESSION
;	ENTRY CAN BE MADE.  NOTE THAT THE FORMAT EXACTLY MATCHES (BEGINNING
;	WITH CLINNO) WITH THE LOGIN IPCF MESSAGE.  IF THEY DO NOT MATCH, THE
;	ROUTINE CALLED CPJLIN, MUST CHANGE FROM DOING THE BLT.

CLINNO:	BLOCK 1			;LINE NUMBER
CPGNAM:	BLOCK 1			;NAME OF PROGRAM (USUALLY LOGIN)
CPGVER:	BLOCK 1			;VERSION OF PROGRAM (USUALLY LOGIN)
CNODE:	BLOCK 1			;NODE NAME OF USER'S LOCATION
CACCT:	BLOCK 10		;ACCOUNT STRING
CSESST:	BLOCK 1			;SESSION START DATE/TIME
CJBTYP:	BLOCK 1			;JOB TYPE
CBTNAM:	BLOCK 1			;BATCH JOB NAME
CBTSEQ:	BLOCK 1			;BATCH SEQUENCE NUMBER
CRMRK:	BLOCK 10		;SESSION REMARK
CCLASS:	BLOCK 1			;SCHEDULING CLASS
CPPN:	BLOCK 1			;PROJECT-PROGRAMMER NUMBER OF USER
CNAME1:	BLOCK 1			;FIRST SIX LETTERS OF USER'S NAME
CNAME2:	BLOCK 1			;LAST SIX LETTERS OF USER'S NAME
CBTRID:	BLOCK 1			;BATCH REQUEST ID
CTERDE:	BLOCK 1			;TERMINAL DESIGNATOR

CBUFLN:			;LENGTH OF THE CHECKPOINT AREAS
	DEPHASE
;*****************************************************************
;* * * * * * END OF CPJBUF AND CAJBUF FORMAT * * * * * * * * *
;*****************************************************************
;*****************************************************************

	RELOC	RECLM1		;ORG OVER DATA DEFINITIONS TO SAVE SPACE

CPJIOB==<<2*CBUFLN>+177>/200	;NUMBER OF DISK BLOCKS PER JOB AREA
CPJIOL==200*CPJIOB		;NUMBER OF WORDS FOR I/O TO THE FILE

CPJBUF:	BLOCK	CPJIOL		;THE BUFFER AREA FOR WORKING ON THE FILE
CAJBUF==CPJBUF+<CPJIOL/2>	;AUXILLIARY AREA STARTS IN THE MIDDLE


;*********************************************************************
;	DEVICE CHECKPOINT FILE (PRIMARY AND AUXILLIARY) DEFINITIONS
;*********************************************************************

DVOFFS==0		;OFFSET TO ADD TO FIND WHAT BLOCK DEVICE INFORMATION BEGINS

;STORAGE FOR PRIMARY DEVICE CHECKPOINT FILE CALLED JJJDEV.BIN WHERE JJJ IS THE JOB NUMBER
CPDCHN:	BLOCK 1		;CHANNEL NUMBER OF PRIMARY DEVICE CHECKPOINT FILE
			; # IS POSITIONED WHERE THE FILOP EXPECTS IT (FO.CHN)
CPDBLK:	BLOCK 10	;FILOP. BLOCK USED FOR THE PRIMARY DEVICE CHECKPOINT FILE
JJJDEV:	BLOCK .RBSIZ+1	;LOOKUP BLOCK FOR CPDBLK (JJJDEV.BIN)
JJJDEL:	BLOCK .RBSIZ+1	;RENAME (DELETE) BLOCK FOR JJJDEV.BIN


RECLM2:			;WHERE READ DATA ENDS



;*****************************************************************
;* * * * * * FORMAT OF CPJBUF AND CADBUF * * * * * * * *
;*****************************************************************
;	EACH DEVICE SLOT IN THE CHECKPOINT FILE CONTAINS THE PRIMARY AND AUXILLIARY
;	AREAS.  EACH DEVICE REQUIRES "N" DISK BLOCKS AS DETERMINED BY THE SIZE OF
;	THE PRIMARY AREA (CDBFLN) * 2.  THE AUXILLIARY AREA (FOR SESSION) BEGINS
;	AFTER THE PRIMARY AREA ROUNDED UP TO NEXT 100 WORD BOUNDRY FOR EXPANSION.
;	WHEN THE NUMBER OF BLOCKS REQUIRED FOR A DEVICE CHANGES, THE OLD JJJDEV.BIN
;	FILE IS INVALID AND MUST BE DELETED BEFORE THE NEW VERSION OF THE ACTDAE
;	CAN BE USED.  TO AVOID LOSING DATA IN THE TRANSITION BETWEEN VERSIONS,
;	SCHEDULE A KSYS FOR THE SYSTEM, ATTACH THE OLD ACTDAE AFTER IT IS COMPLETE,
;	RUN THE OLD VERSION AGAIN (TO GET ACCOUNTING FOR [OPR] DEVICES), DELETE
;	ALL OLD JJJDEV.BIN'S, PUT THE NEW VERSION ON SYS, AND RELOAD THE MONITOR.
;
;	NOTE:  IN THE CASE OF DEVICE CHECKPOINT FILES ONLY -- IF A NEW
;	ACCOUNT DAEMON IS RUN WHICH HAS CHANGED THE NUMBER OF BLOCKS FOR EACH
;	DEVICE, ANY DEVICE CHECKPOINT FILE WHICH HAS AN UNKNOWN FORMAT
;	(FILBPD IN THE CPDFIL BLOCK IS NOT = TO CPDIOB) WILL BE DELETED WITH
;	A WARNING SENT TO THE OPERATOR AND NO USAGE ENTRY WILL BE MADE.
;
;*****************************************************************

;FILE STRUCTURE BLOCK FORMAT

	PHASE 0

DEVTYP:	BLOCK 1			;DEVICE TYPE (SEE FSRTYP DEFINITION AREA)
DEVICE:	BLOCK 1			;DEVICE NAME IN SIXBIT
FJOB:	BLOCK 1			;JOB NUMBER OF USER
FTERDE:	BLOCK 1			;TERMINAL DESIGNATOR
FACVER:	BLOCK 1			;VERSION OF ACTDAE (%%.ACV)
FLSTCK:	BLOCK 1			;DATE/TIME OF LAST CHECKPOINT
FLINNO:	BLOCK 1			;LINE NUMBER
FPGNAM:	BLOCK 1			;NAME OF PROGRAM (USUALLY PULSAR)
FPGVER:	BLOCK 1			;VERSION OF PROGRAM (USUALLY PULSAR)
FNODE:	BLOCK 1			;NODE NAME OF USER'S LOCATION
FACCT:	BLOCK 10		;ACCOUNT STRING
FSESST:	BLOCK 1			;SESSION START DATE/TIME
FPPN:	BLOCK 1			;PROJECT-PROGRAMMER NUMBER OF USER
FNAME1:	BLOCK 1			;FIRST SIX LETTERS OF USER'S NAME
FNAME2:	BLOCK 1			;LAST SIX LETTERS OF USER'S NAME
FFSTYP:	BLOCK 1			;TYPE OF FILE STRUCTURE
FPCKNO:	BLOCK 1			;NUMBER OF PACKS IN FILE STRUCTURE
FCONTY:	BLOCK 1			;CONTROLLER TYPE
FDEVTY:	BLOCK 1			;DEVICE TYPE
FDISPO:	BLOCK 1			;DISPOSITION
FOTEXT:	BLOCK 10		;TEXT TO EXPLAIN DISPOSITION
FCREDT:	BLOCK 1			;CREATION DATE/TIME OF MOUNT REQUEST
FSCHDT:	BLOCK 1			;SCHEDULED DATE/TIME OF MOUNT REQUEST
FSERDT:	BLOCK 1			;SERVICE DATE/TIME OF MOUNT REQUEST
FMNTCT:	BLOCK 1			;MOUNT COUNT BEFORE REQUEST
FDISCT:	BLOCK 1			;MOUNT COUNT AFTER DISMOUNT
FACCES:	BLOCK 1			;ACCESS TYPE
FCONNE:	BLOCK 1			;CONNECT TIME IN SECONDS

FBUFLN:			;LENGTH OF THE CHECKPOINT AREAS
	DEPHASE


;END OF FILE STRUCTURE BLOCK FORMAT
;MAGTAPE BLOCK FORMAT

	PHASE	DEVTYP


MEVTYP:	BLOCK 1			;SEE DEVTYP
MEVICE:	BLOCK 1			;DEVICE NAME IN SIXBIT (REFERENCED BY DEVICE
				; DEFINED IN FILE STRUCTURE BLOCK)
MJOB:	BLOCK 1			;JOB NUMBER OF USER
MTERDE:	BLOCK 1			;TERMINAL DESIGNATOR
MACVER:	BLOCK 1			;VERSION OF ACTDAE (%%.ACV)
MLSTCK:	BLOCK 1			;DATE/TIME OF LAST CHECKPOINT
MLINNO:	BLOCK 1			;LINE NUMBER
MPGNAM:	BLOCK 1			;NAME OF PROGRAM (USUALLY PULSAR)
MPGVER:	BLOCK 1			;VERSION OF PROGRAM (USUALLY PULSAR)
MNODE:	BLOCK 1			;NODE NAME OF USER'S LOCATION
MACCT:	BLOCK 10		;ACCOUNT STRING
MSESST:	BLOCK 1			;SESSION START DATE/TIME
MPPN:	BLOCK 1			;PROJECT-PROGRAMMER NUMBER OF USER
MNAME1:	BLOCK 1			;FIRST SIX LETTERS OF USER'S NAME
MNAME2:	BLOCK 1			;LAST SIX LETTERS OF USER'S NAME
MCONTY:	BLOCK 1			;CONTROLLER TYPE
MDISPO:	BLOCK 1			;DISPOSITION
MOTEXT:	BLOCK 10		;TEXT TO EXPLAIN DISPOSITION
MCREDT:	BLOCK 1			;CREATION DATE/TIME OF MOUNT REQUEST
MSCHDT:	BLOCK 1			;SCHEDULED DATE/TIME OF MOUNT REQUEST
MSERDT:	BLOCK 1			;SERVICE DATE/TIME OF MOUNT REQUEST
MVOLID:	BLOCK 1			;VOLUME ID RECORDED IN VOL1 LABEL
MRELID:	BLOCK 1			;REEL ID VISUAL LABEL OF TAPE
MMREAD:	BLOCK 1			;MAGTAPE READS - THOUSANDS OF CHARS
MMWRIT:	BLOCK 1			;MAGTAPE WRITES - THOUSANDS OF CHARS
MLABEL:	BLOCK 1			;LABEL TYPE
MSTATE:	BLOCK 1			;VOLUME LABEL STATE
MRECRD:	BLOCK 1			;PHYSICAL RECORDS READ
MRECWR:	BLOCK 1			;PHYSICAL RECORDS WRITTEN
MFSTID:	BLOCK 1			;FILE SET IDENTIFIER
MNOSRE:	BLOCK 1			;NUMBER OF SOFT READ ERRORS
MNOSWE:	BLOCK 1			;NUMBER OF SOFT WRITE ERRORS
MNOHRE:	BLOCK 1			;NUMBER OF HARD READ ERRORS
MNOHWE:	BLOCK 1			;NUMBER OF HARD WRITE ERRORS
MCONNE:	BLOCK 1			;CONNECT TIME IN SECONDS

MBUFLN:			;LENGTH OF THE CHECKPOINT AREAS
	DEPHASE


;END OF MAGTAPE BLOCK FORMAT
;DECTAPE BLOCK FORMAT

	PHASE	DEVTYP

DDVTYP:	BLOCK 1			;SEE DEVTYP
DDVICE:	BLOCK 1			;DEVICE NAME IN SIXBIT (REFERENCED BY DEVICE
				; DEFINED IN FILE STRUCTURE BLOCK)
DJOB:	BLOCK 1			;JOB NUMBER OF USER
DTERDE:	BLOCK 1			;TERMINAL DESIGNATOR
DACVER:	BLOCK 1			;VERSION OF ACTDAE (%%.ACV)
DLSTCK:	BLOCK 1			;DATE/TIME OF LAST CHECKPOINT
DLINNO:	BLOCK 1			;LINE NUMBER
DPGNAM:	BLOCK 1			;NAME OF PROGRAM (USUALLY MOUNT)
DPGVER:	BLOCK 1			;VERSION OF PROGRAM (USUALLY MOUNT)
DNODE:	BLOCK 1			;NODE NAME OF USER'S LOCATION
DACCT:	BLOCK 10		;ACCOUNT STRING
DSESST:	BLOCK 1			;SESSION START DATE/TIME
DPPN:	BLOCK 1			;PROJECT-PROGRAMMER NUMBER OF USER
DNAME1:	BLOCK 1			;FIRST SIX LETTERS OF USER'S NAME
DNAME2:	BLOCK 1			;LAST SIX LETTERS OF USER'S NAME
DDISPO:	BLOCK 1			;DISPOSITION
DOTEXT:	BLOCK 10		;TEXT TO EXPLAIN DISPOSITION
DCREDT:	BLOCK 1			;CREATION DATE/TIME OF MOUNT REQUEST
DSCHDT:	BLOCK 1			;SCHEDULED DATE/TIME OF MOUNT REQUEST
DSERDT:	BLOCK 1			;SERVICE DATE/TIME OF MOUNT REQUEST
DVOLID:	BLOCK 1			;VOLUME ID RECORDED IN VOL1 LABEL
DRELID:	BLOCK 1			;REEL ID VISUAL LABEL OF TAPE
DDREAD:	BLOCK 1			;DECTAPE READS - BLOCKS
DDWRIT:	BLOCK 1			;DECTAPE WRITES - BLOCKS
DCONNE:	BLOCK 1			;CONNECT TIME IN SECONDS

DBUFLN:			;LENGTH OF THE CHECKPOINT AREAS

	DEPHASE


;END OF DECTAPE BLOCK FORMAT
;DISK SPINDLE BLOCK FORMAT

	PHASE	DEVTYP

SEVTYP:	BLOCK 1			;SEE DEVTYP
SEVICE:	BLOCK 1			;DISK UNIT NAME IN SIXBIT
ALTPRT:	BLOCK 1			;IF DUAL PORTED, OTHER DISK UNIT NAME
SJOB:	BLOCK 1			;JOB NUMBER OF PULSAR
STERDE:	BLOCK 1			;TERMINAL DESIGNATOR
SACVER:	BLOCK 1			;VERSION OF ACTDAE (%%.ACV)
SLSTCK:	BLOCK 1			;DATE/TIME OF LAST CHECKPOINT
SMNTDT:	BLOCK 1			;DATE/TIME PACK WAS SPUN UP
SLINNO:	BLOCK 1			;LINE NUMBER
SPGNAM:	BLOCK 1			;NAME OF PROGRAM (USUALLY PULSAR)
SPGVER:	BLOCK 1			;VERSION OF PROGRAM (USUALLY PULSAR)
SNODE:	BLOCK 1			;NODE NAME
SCSHIF:	BLOCK 1			;CSHIFT DATE/TIME
SPAKID:	BLOCK 1			;DISK PACK IDENTIFIER
SFSNAM:	BLOCK 1			;FILE STRUCTURE NAME
SFSTYP:	BLOCK 1			;TYPE OF FILE STRUCTURE
SPCKNO:	BLOCK 1			;NUMBER OF PACKS IN FILE STRUCTURE
SPKMTH:	BLOCK 1			;M OF N COUNT
SCONTY:	BLOCK 1			;CONTROLLER TYPE
SDEVTY:	BLOCK 1			;DEVICE TYPE
SCONNE:	BLOCK 1			;CONNECT TIME IN SECONDS

SBUFLN:			;LENGTH OF THE CHECKPOINT AREAS

	DEPHASE


;END OF DISK SPINDLE BLOCK FORMAT


	RELOC	RECLM2		;ORG OVER DATA DEFINITIONS TO SAVE SPACE


;NOW DEFINE THE LENGTH OF A DEVICE CHECKPOINT AREA. FOR SIMPLICITY, THE
;LENGTH IS THE GREATEST LENGTH OF ANY DEVICE BLOCK DEFINED.

CDBFLN==FBUFLN			;INITIALIZE WITH FILE STRUCTURE LENGTH

IFG MBUFLN-CDBFLN, <CDBFLN==MBUFLN>	;IF MAGTAPE LENGTH IS GREATER
IFG DBUFLN-CDBFLN, <CDBFLN==DBUFLN>	;IF DECTAPE LENGTH IS GREATER
IFG SBUFLN-CDBFLN, <CDBFLN==SBUFLN>	;IF SPINDLE LENGTH IS GREATER


CPDIOB==<<2*CDBFLN>+177>/200	;NUMBER OF DISK BLOCKS PER DEVICE AREA
CPDIOL==200*CPDIOB		;NUMBER OF WORDS FOR I/O TO THE FILE

CPDBUF:	BLOCK	CPDIOL		;THE BUFFER AREA FOR WORKING ON THE FILE
CADBUF==CPDBUF+<CPDIOL/2>	;AUXILLIARY AREA STARTS IN THE MIDDLE
	SUBTTL	ACTCHK - MAIN ROUTINES

;CHKAJB - ROUTINE TO CHECKPOINT ALL JOBS CURRENTLY LOGGED IN. ON EXIT, NEXT
;	CHECKPOINT IS SET UP

CHKAJB:	$CALL	.SAVE1		;SAVE A WORKING AC
;	PUSHJ	P,READJG	;READ GENERAL CHECKPOINT BLOCK
				;DON'T REALLY NEED TO READ IT IN
	PUSHJ	P,DATIM		;GET CURRENT DATE/TIME
	MOVEM	S1,CPJGEN+LASTCH ;RECORD AS TIME OF LAST CHECKPOINT
	MOVE	S1,JOBMAX	;MAXIMUM NUMBER OF JOBS ALLOWED
	MOVEM	S1,CPJGEN+FILMJB ;RECORD FOR RESTART
	MOVEI	S1,CPJIOB	;NUMBER OF BLOCKS PER JOB IN CHECKPOINT FILE
	MOVEM	S1,CPJGEN+FILBPJ ;RECORD FOR VERSION CHECK
	MOVEI	S1,CPDIOB	;NUMBER OF BLOCKS PER DEVICE IN jjjDEV.BIN
	MOVEM	S1,CPJGEN+FILBPD ;RECORD FOR VERSION CHECK
	PUSHJ	P,WRITJG	;AND RE-WRITE THE BLOCK
	MOVX	P1,%NSHJB	;GET HIGHEST JOB IN THE SYSTEM
	GETTAB	P1,		;GET IT
ACTCGH:	$BOMB	<ACTCGH Cannot GETTAB Highest job in use>
	CAMLE	P1,JOBMAX	;CAN WE DO THIS MANY?
	MOVE	P1,JOBMAX	;STAY WITHIN THE FILE
	MOVNS	P1		;FORM AOBJN
	HRLZS	P1		;...
	HRRI	P1,1		;SKIP THE NULL JOB
CHKAJ1:	MOVNI	T2,(P1)		;NEGATE JOB NUMBER
	JOBSTS	T2,		;SEE IF A REAL JOB
	  JRST	CHKAJ2		;CAN'T DO IT
	TXNN	T2,JB.ULI	;IS THE JOB LOGGED IN
	JRST	CHKAJ3		;NO, TRY NEXT JOB
	HRRZM	P1,JOBNUM	;JOB NUMBER FOR CHKJOB
	PUSHJ	P,CHKJOB	;CHECKPOINT IT
	SKIPE	CPJBUF+CDEVFL	;ANY DEVICES FOR THIS JOB
	PUSHJ	P,CHJDVS	;CHECKPOINT THE JOBS DEVICES TOO
	JRST	CHKAJ3		;ONWARD
CHKAJ2:	$WTO	(<Accounting error>,<^I/CHKTXT/>,,<$WTFLG(WT.SJI)>)
CHKAJ3:	AOBJN	P1,CHKAJ1	;LOOP FOR ALL JOBS
	SETZM	JOBNUM		;JOB 0'S DEVICES ARE SYSTEM SPINDLES
	PUSHJ	P,CHJDVS	;CHECKPOINT THEM NOW
	PUSHJ	P,NXTCHK	;SET UP FOR NEXT CHECKPOINT
	POPJ	P,		;AND RETURN

CHJDVS:	MOVEI	T1,CHKDVS	;ROUTINE TO DO THE CHECKPOINT
	PUSHJ	P,ALLDEV	;DO THAT FOR ALL DEVICES
	POPJ	P,		;AND RETURN

CHKTXT:	ITEXT	(<Cannot read job status for job ^D/P1,RHMASK/
Job not checkpointed>)
;CHKJOB - ROUTINE TO CHECKPOINT THE JOB CHECKPOINT FILE, USEJOB.BIN ON ACT:.
;	NOTE THAT THIS IS THE PRIMARY CHECKPOINT FILE...THE AUXILIARY FILE
;	WILL NEVER BE CHECKPOINTED.
;CALL:	PUSHJ	P,CHKJOB

CHKJOB:	PUSHJ	P,READJP	;MUST READ IT IN CORE TO PRESERVE THE STATIC INFORMATION
	SKIPE	T1,DTMOFS	;WAS THERE A DATE/TIME CHANGE
	PUSHJ	P,[ADDM T1,CPJBUF+CJLGTM ;ADJUST LOGIN TIME OF JOB
		ADDM T1,CPJBUF+CSESST ;AND SESSION START TIME
		POPJ P,]	;DONE ADJUSTING
	PUSHJ	P,CHKPNT	;CHECKPOINT THE JOB'S DATA
	PUSHJ	P,WRITJP	;GO WRITE IT
	POPJ	P,		;AND RETURN

;ROUTINE TO CHECKPOINT THE DEVICE CHECKPOINT FILE FOR THIS JOB.

CHKDVS:	PUSHJ	P,@[EXP CHKFSR,CHKMTA,CHKDTA,CHKSPN]-1(P1) ;CHECKPOINT IT
	SKIPE	T1,DTMOFS	;WAS THERE A DATE/TIME CHANGE
	PUSHJ	P,@[EXP OFSFSR,OFSMTA,OFSDTA,OFSSPN]-1(P1) ;YES, ADJUST TIMES
	PUSHJ	P,WRITDP	;WRITE IT BACK OUT
	POPJ	P,		;RETURN FOR NEXT DEVICE

OFSFSR:	ADDM	T1,CPDBUF+FSESST ;ADJUST SESSION START TIME
	ADDM	T1,CPDBUF+FCREDT ;CREATION TIME OF REQUEST
	ADDM	T1,CPDBUF+FSCHDT ;SCHEDULED TIME OF REQUEST
	ADDM	T1,CPDBUF+FSERDT ;SERVICED TIME OF REQUEST
	POPJ	P,		;DONE ADJUSTING

OFSMTA:	ADDM	T1,CPDBUF+MSESST ;ADJUST SESSION START TIME
	ADDM	T1,CPDBUF+MCREDT ;CREATION TIME OF REQUEST
	ADDM	T1,CPDBUF+MSCHDT ;SCHEDULED TIME OF REQUEST
	ADDM	T1,CPDBUF+MSERDT ;SERVICED TIME OF REQUEST
	POPJ	P,		;DONE ADJUSTING

OFSDTA:	ADDM	T1,CPDBUF+DSESST ;ADJUST SESSION START TIME
	ADDM	T1,CPDBUF+DCREDT ;CREATION TIME OF REQUEST
	ADDM	T1,CPDBUF+DSCHDT ;SCHEDULED TIME OF REQUEST
	ADDM	T1,CPDBUF+DSERDT ;SERVICED TIME OF REQUEST
	POPJ	P,		;DONE ADJUSTING

OFSSPN:	ADDM	T1,CPDBUF+SCSHIF ;ADJUST SESSION START TIME
	ADDM	T1,CPDBUF+SMNTDT ;SPIN UP DATE/TIME
	POPJ	P,		;DONE ADJUSTING
;SESAJB - ROUTINE TO MAKE SESSION ENTRIES FOR ALL JOBS. CALLED WHEN IT IS
;	TIME TO CLOSE OUT THE USAGE.OUT FILE OR WHEN A CSHIFT OCCURS

SESAJB:	PUSHJ	P,CHKAJB	;FIRST, CHECKPOINT ALL JOB INFORMATION
	$CALL	.SAVE1		;SAVE A WORKING AC
	MOVX	P1,%NSHJB	;GET HIGHEST JOB IN USE RIGHT NOW
	GETTAB	P1,		;GET IT
	  JRST	ACTCGH		;GIVE AN ERROR
	CAMLE	P1,JOBMAX	;CAN WE DO THIS MANY?
	MOVE	P1,JOBMAX	;STAY WITHIN THE FILE
	MOVNS	P1		;FORM AOBJN
	HRLZS	P1		;...
	HRRI	P1,1		;SKIP THE NULL JOB
SESAJ1:	HRRZM	P1,JOBNUM	;STORE JOB NUMBER WE ARE DOING
	PUSHJ	P,READJP	;READ IN THE INFORMATION
	SKIPN	CPJBUF+CJOB	;IS THERE A JOB
	JRST	SESAJ2		;NO, TRY THE NEXT
	PUSHJ	P,MAKSES	;MAKE THE SESSION ENTRY
	PUSHJ	P,CPJCOP	;MOVE PRIMARY DATA TO AUXILLIARY REGION
	MOVE	T1,CPJBUF+CLSTCK ;GET TIME OF LAST CHECKPOINT (=NOW)
	MOVEM	T1,CPJBUF+CSESST ;STORE AS SESSION START TIME
	PUSHJ	P,WRITJP	;AND RE-WRITE THE FILE
	SKIPE	CPJBUF+CDEVFL	;ANY DEVICES FOR THIS JOB
	PUSHJ	P,SESADV	;MAKE SESSION ENTRIES FOR ALL THE JOBS DEVICES TOO
SESAJ2:	AOBJN	P1,SESAJ1	;TRY THE NEXT JOB
	SETZM	JOBNUM		;JOB 0 = SYSTEM SPINDLE INFO
	PUSHJ	P,SESADV	;DO THEM NOW
	PUSHJ	P,NXTCHK	;RESTART CHECKPOINT TIMER IF THIS TOOK A LONG TIME
	POPJ	P,		;AND RETURN

SESADV:	MOVEI	T1,SEADVS	;ROUTINE TO ACTUALLY MAKE THE ENTRIES
	PUSHJ	P,ALLDEV	;DO IT FOR ALL DEVICES
	POPJ	P,		;AND RETURN

;ROUTINE TO MAKE SESSION ENTRIES FOR ALL THE DEVICES OWNED BY A JOB

SEADVS:	PUSHJ	P,@[EXP MAKFSR,MAKMAG,MAKDEC,MAKSPN]-1(P1) ;MAKE THE ENTRY
	PUSHJ	P,CPDCOP	;MOVE DATA ALREADY BILLED
	MOVE	T1,@[EXP CPDBUF+FLSTCK,CPDBUF+MLSTCK,CPDBUF+DLSTCK,CPDBUF+SLSTCK]-1(P1)
	MOVEM	T1,@[EXP CPDBUF+FSESST,CPDBUF+MSESST,CPDBUF+DSESST,CPDBUF+SCSHIF]-1(P1)
	PUSHJ	P,WRITDP	;WRITE IT BACK TO THE FILE
	POPJ	P,		;ALL DONE HERE
;READJG - ROUTINE TO READ THE GENERAL CHECKPOINT BLOCK OF THE PRIMARY JOB
;	CHECKPOINT FILE. THE BLOCK NUMBER IS DEFINED BY CPJFIL AND SHOULD
;	ALWAYS BE THE FIRST BLOCK OF THE FILE.

READJG:	MOVEI	T1,CPJFIL	;GET THE BLOCK NUMBER
	MOVE	T2,CPJCHN	; AND THE CHANNEL NUMBER
	PUSHJ	P,AUSETI	;POSITION THE INPUT
	SKIPT
	$BOMB	<ACTCUG Cannot USETI (^O/T1/) to general checkpoint block>
	MOVE	T1,[IOWD 200,CPJGEN]
	MOVEM	T1,IOLIST	;SET UP THE I/O LIST
	SETZM	IOLIST+1
	MOVE	T1,CPJCHN	;CHANNEL NUMBER
	HRRI	T1,.FOINP	;READ
	TXO	T1,FO.PRV	;FULL FILE ACCESS TO ACT:
	MOVEM	T1,CPJBLK+.FOFNC
	MOVEI	T1,IOLIST
	MOVEM	T1,CPJBLK+.FOIOS
	MOVE	T1,[2,,CPJBLK]
	FILOP.	T1,
	$BOMB	<ACTCRG Cannot READ general checkpoint block>
	$RETT


;WRITJG - ROUTINE TO WRITE THE GENERAL CHECKPOINT BLOCK OF THE PRIMARY JOB
;	CHECKPOINT FILE. THE BLOCK NUMBER IS DEFINED BY CPJFIL AND SHOULD
;	ALWAYS BE THE FIRST BLOCK OF THE FILE.

WRITJG:	MOVEI	T1,CPJFIL	;GET THE BLOCK NUMBER
	MOVE	T2,CPJCHN	; AND THE CHANNEL NUMBER
	PUSHJ	P,AUSETO	;POSITION THE OUTPUT
	SKIPT
	$BOMB	<ACTCUG Cannot USETO to general checkpoint block>
	MOVE	T1,[IOWD 200,CPJGEN]
	MOVEM	T1,IOLIST	;SET UP THE I/O LIST
	SETZM	IOLIST+1
	MOVE	T1,CPJCHN	;CHANNEL NUMBER
	HRRI	T1,.FOOUT	;WRITE
	TXO	T1,FO.PRV	;FULL FILE ACCESS TO ACT:
	MOVEM	T1,CPJBLK+.FOFNC
	MOVEI	T1,IOLIST
	MOVEM	T1,CPJBLK+.FOIOS
	MOVE	T1,[2,,CPJBLK]
	FILOP.	T1,
	$BOMB	<ACTCWG Cannot WRITE general checkpoint block>
	$RETT
;READJP - ROUTINE TO READ A BLOCK OF THE PRIMARY JOB CHECKPOINT FILE FOR A JOB.
;CALL:	JOBNUM SET TO DESIRED JOB

READJP:	MOVE	T1,JOBNUM	;GET THE DESIRED JOB NUMBER
	SOS	T1		;COMPUTE POSITION IN CHECKPOINT FILE
	IMULI	T1,CPJIOB	;*NUMBER OF BLOCKS PER CHECKPOINT AREA
	ADDI	T1,1+JBOFFS	;ADJUST + FILE OFFSET
	MOVE	T2,CPJCHN	;SET UP THE CHANNEL NUMBER
	PUSHJ	P,AUSETI	;POSITION THE INPUT
	SKIPT
ACTCPC:	$BOMB	<ACTCPC Cannot position checkpoint file for job ^D/JOBNUM/, file status = ^O/T1/>
	MOVE	T1,[IOWD CPJIOL,CPJBUF]
	MOVEM	T1,IOLIST	;SET UP THE I/O LIST
	SETZM	IOLIST+1
	MOVE	T1,CPJCHN	;CHANNEL NUMBER
	HRRI	T1,.FOINP	;READ
	TXO	T1,FO.PRV	;FULL FILE ACCESS TO ACT:
	MOVEM	T1,CPJBLK+.FOFNC
	MOVEI	T1,IOLIST
	MOVEM	T1,CPJBLK+.FOIOS
	MOVE	T1,[2,,CPJBLK]
	FILOP.	T1,
	$BOMB	<ACTCRP Cannot READ checkpoint file for job ^D/JOBNUM/, file status = ^O/T1/>
	$RETT

;WRITJP - ROUTINE TO WRITE A BLOCK OF THE PRIMARY JOB CHECKPOINT FILE FOR A JOB.
;CALL:	JOBNUM SET TO DESIRED JOB

WRITJP:	MOVE	T1,JOBNUM	;GET THE DESIRED JOB NUMBER
	SOS	T1		;COMPUTE POSITION IN CHECKPOINT FILE
	IMULI	T1,CPJIOB	;*NUMBER OF BLOCKS PER CHECKPOINT AREA
	ADDI	T1,1+JBOFFS	;ADJUST + FILE OFFSET
	MOVE	T2,CPJCHN	;SET UP THE CHANNEL NUMBER
	PUSHJ	P,AUSETO	;POSITION THE OUTPUT
	JUMPF	ACTCPC		;REPORT POSITIONING ERROR
	MOVE	T1,[IOWD CPJIOL,CPJBUF]
	MOVEM	T1,IOLIST	;SET UP THE I/O LIST
	SETZM	IOLIST+1
	MOVE	T1,CPJCHN	;CHANNEL NUMBER
	HRRI	T1,.FOOUT	;WRITE
	TXO	T1,FO.PRV	;FULL FILE ACCESS TO ACT:
	MOVEM	T1,CPJBLK+.FOFNC
	MOVEI	T1,IOLIST
	MOVEM	T1,CPJBLK+.FOIOS
	MOVE	T1,[2,,CPJBLK]
	FILOP.	T1,
	$BOMB	<ACTCWP Cannot WRITE checkpoint file for job ^D/JOBNUM/, file status = ^O/T1/>
	$RETT
;CBJZER - ROUTINE TO ZERO THE PRIMARY JOB CHECKPOINT FILE JOB SLOT BUFFER
;	(CPJBUF).  THIS IS USED TO INITIALIZE JOB SLOTS AT LOGIN TIME AND
;	TO ENSURE DATA INTEGRITY IN CASE OF ERROR.

CBJZER:	MOVE	T1,[CPJBUF,,CPJBUF+1]
	SETZM	CPJBUF
	BLT	T1,CPJBUF+CBUFLN-1
	POPJ	P,

;CPJCOP - ROUTINE TO COPY THE PRIMARY CHECKPOINT FILE JOB SLOT BUFFER (CPJBUF)
;	TO ITS COUNTERPART OF THE AUXILLIARY JOB CHECKPOINT FILE BUFFER (CAJBUF).
;	THIS IS USED TO ZERO CAJBUF AND TO RETAIN INFORMATION IN CASE OF A SESSION
;	COMMAND EVENT OR CSHIFT COMMAND EVENT.

CPJCOP:	MOVE	T1,[CPJBUF,,CAJBUF]
	BLT	T1,CAJBUF+CBUFLN-1
	POPJ	P,
;READDP - ROUTINE TO READ THE NEXT DEVICE AREA OF THE DEVICE CHECKPOINT FILE
;	ALREADY OPENED.
;CALL:	DEVNUM CONTAINS THE NUMBER OF READS ALREADY DONE (IN OTHER WORDS,
;	NUMBER OF DEVICES ALREADY READ IN)

READDP:	AOS	T1,DEVNUM	;GET NUMBER OF READS DONE
	SOS	T1		;COMPUTE POSITION IN CHECKPOINT FILE
	IMULI	T1,CPDIOB	;*NUMBER OF BLOCKS PER CHECKPOINT AREA
	ADDI	T1,1+DVOFFS	;ADJUST + FILE OFFSET
	MOVE	T2,CPDCHN	;SET UP THE CHANNEL NUMBER
	PUSHJ	P,AUSETI	;POSITION THE INPUT
	JUMPF	ACTPCF
	MOVE	T1,[IOWD CPDIOL,CPDBUF]
	MOVEM	T1,IOLIST	;SET UP THE I/O LIST
	SETZM	IOLIST+1
	MOVE	T1,CPDCHN	;CHANNEL NUMBER
	HRRI	T1,.FOINP	;READ
	TXO	T1,FO.PRV	;FULL FILE ACCESS TO ACT:
	MOVEM	T1,CPDBLK+.FOFNC
	MOVEI	T1,IOLIST
	MOVEM	T1,CPDBLK+.FOIOS
	MOVE	T1,[2,,CPDBLK]
	FILOP.	T1,
	$BOMB	<ACTRCF Cannot READ device checkpoint file for job ^D/JOBNUM/, file status = ^O/T1/>
	$RETT


ACTPCF:!TXNN	T1,IO.EOF	;IS IT END OF FILE?
	$BOMB	<ACTPCF Cannot position	device checkpoint file for job ^D/JOBNUM/, file status = ^O/T1/>
	PUSHJ	P,CPDCLS	;CLOSE THE FILE
	PUSHJ	P,CPDSAU	;AND RE-OPEN IT (CLEAR EOF)
	$RETF			;YES.
;WRITDP - ROUTINE TO WRITE A DEVICE AREA OF THE DEVICE CHECKPOINT FILE ALREADY
;	OPENED.
;CALL:	DEVNUM SET TO DESIRED DEVICE COUNT

WRITDP:	MOVE	T1,DEVNUM	;GET THE DESIRED JOB NUMBER
	SOS	T1		;COMPUTE POSITION IN CHECKPOINT FILE
	IMULI	T1,CPDIOB	;*NUMBER OF BLOCKS PER CHECKPOINT AREA
	ADDI	T1,1+DVOFFS	;ADJUST + FILE OFFSET
	MOVE	T2,CPDCHN	;SET UP THE CHANNEL NUMBER
	PUSHJ	P,AUSETO	;POSITION THE OUTPUT
	JUMPF	ACTPCF		;REPORT POSITIONING ERROR
	MOVE	T1,[IOWD CPDIOL,CPDBUF]
	MOVEM	T1,IOLIST	;SET UP THE I/O LIST
	SETZM	IOLIST+1
	MOVE	T1,CPDCHN	;CHANNEL NUMBER
	HRRI	T1,.FOOUT	;WRITE
	TXO	T1,FO.PRV	;FULL FILE ACCESS TO ACT:
	MOVEM	T1,CPDBLK+.FOFNC
	MOVEI	T1,IOLIST
	MOVEM	T1,CPDBLK+.FOIOS
	MOVE	T1,[2,,CPDBLK]
	FILOP.	T1,
	$BOMB	<ACTWCF Cannot WRITE checkpoint file for job ^D/JOBNUM/, file status = ^O/T1/>
	$RETT
;CBDZER - ROUTINE TO ZERO THE PRIMARY JOB CHECKPOINT FILE JOB SLOT BUFFER
;	(CPJBUF).  THIS IS USED TO INITIALIZE JOB SLOTS AT LOGIN TIME AND
;	TO ENSURE DATA INTEGRITY IN CASE OF ERROR.

CBDZER:	MOVE	T1,[CPDBUF,,CPDBUF+1]
	SETZM	CPDBUF
	BLT	T1,CPDBUF+CDBFLN-1
	POPJ	P,

;CPDCOP - ROUTINE TO COPY THE PRIMARY CHECKPOINT FILE JOB SLOT BUFFER (CPDBUF)
;	TO ITS COUNTERPART OF THE AUXILLIARY JOB CHECKPOINT FILE BUFFER (CADBUF).
;	THIS IS USED TO ZERO CADBUF AND TO RETAIN INFORMATION IN CASE OF A SESSION
;	COMMAND EVENT OR CSHIFT COMMAND EVENT.

CPDCOP:	MOVE	T1,[CPDBUF,,CADBUF]
	BLT	T1,CADBUF+CDBFLN-1
	POPJ	P,
;CHKPNT - ROUTINE CALLED FROM CHKJOB TO CHECKPOINT THE JOB (JOBNUM) AND STORE
;	THE INFORMATION IN CPJBUF AND CPDBUF.
;		CHKPNT IS DRIVEN BY TWO TABLES GENERATED BY THE "TABS" MACRO.
;	THE FIRST TABLE CONTAINS THE ARGUMENT TO GETTAB; THE SECOND CONTAINS
;	AN INSTRUCTION WHICH IS EXECUTED TO STORE THE RESULTS.  NOTE THAT THIS
;	MACRO IS BASED ON GETTAB'S INDEXED BY JOB NUMBER ONLY.

CHKPNT:	MOVSI	T2,-.NMTAB	;MAKE AN AOBJN POINTER
CHKPN1:	MOVE	T1,GTAB1(T2)	;GET AN ARGUMENT
	HRL	T1,JOBNUM	;GET THE JOB NUMBER
	GETTAB	T1,		;DO THE GETTAB
	  SETZ	T1,		;CAN'T
	XCT	GTAB2(T2)	;STORE THE RESULT
	AOBJN	T2,CHKPN1	;AND LOOP

;NOW GET ALL THE NECESSARY INFORMATION THAT CANNOT BE GETTAB'ED.
	HRRZ	T1,JOBNUM	;GET THE JOB NUMBER
	TXO	T1,RN.PCN	;GET THE RUNTIME TO THE NEAREST TEN-MICROSECOND
	RUNTIM	T1,
	MOVEM	T1,CPJBUF+CRUNTM;STORE IT
	PUSHJ	P,GTTTYS	;GET TTY STATISTICS
	PUSHJ	P,DATIM		;GET THE CURRENT DATE/TIME
	MOVEM	S1,CPJBUF+CLSTCK ;STORE AS TIME OF LAST CHECKPOINT
	SKIPE	CPJBUF+CJOB	;IS THE JOB KNOWN TO US
	POPJ	P,		;YES, STATIC INFORMATION IS CORRECT
;HERE WHEN CHECKPOINTING A JOB (SESSION OR LOGOUT ALSO) AND THE JOB ISN'T
;	KNOWN TO THE ACTDAE.  THIS USUALLY HAPPENS FOR JOBS STARTED BY INITIA
;	OR VIA SYSJOB.INI (INCLUDES ACTDAE).

	MOVE	T1,JOBNUM	;THE JOB NUMBER
	MOVEM	T1,CPJBUF+CJOB	;STORE THE JOB NUMBER
	MOVE	T1,[SIXBIT/ACTDAE/] ;OUR NAME
	MOVEM	T1,CPJBUF+CPGNAM ;AS PROGRAM WHO PROVIDED THE DATA
	MOVE	T1,.JBVER	;OUR VERSION NUMBER
	MOVEM	T1,CPJBUF+CPGVER
	MOVEM	T1,CPJBUF+CACVER ;STORE IN BOTH PLACES
	MOVSI	T2,-.NUTAB	;MAKE AN AOBJN POINTER
CHKPN3:	MOVE	T1,GTAB3(T2)	;GET AN ARGUMENT
	HRL	T1,JOBNUM	;GET THE JOB NUMBER
	GETTAB	T1,		;DO THE GETTAB
	  SETZ	T1,		;CAN'T
	XCT	GTAB4(T2)	;STORE THE RESULT
	AOBJN	T2,CHKPN3	;AND LOOP
	MOVE	T1,[.ACTRD,,T2] ;READ ACCT STRING FUNCTION
	MOVEI	T2,2		;NUMBER OF ARGS
	HRRZ	T3,JOBNUM	;THE JOB NUMBER
	MOVEI	T4,CPJBUF+CACCT	;WHERE TO PUT THE ACCOUNT STRING
	SETZM	CPJBUF+CACCT	;INCASE UUO FAILS
	ACCT.	T1,		;ASK FOR IT
	  $WTO	(<Accounting error>,<^I/ACTTXT/>,,<$WTFLG(WT.SJI)>)
	HRRZ	T1,JOBNUM	;THE JOB NUMBER
	PUSHJ	P,SETTNL	;SET TERMINAL, NODE, LINE FOR JOB
	MOVE	T1,MONLNO	;SETTNL LEFT VALUES THERE
	MOVEM	T1,CPJBUF+CLINNO ;SO MOVE THEM INTO THE CHECKPOINT BLOCK
	MOVE	T1,MONNOD	;THE NODE NAME
	MOVEM	T1,CPJBUF+CNODE
	MOVE	T1,MONTDE	;THE TERMINAL DESIGNATOR
	MOVEM	T1,CPJBUF+CTERDE
	POPJ	P,		;AND RETURN

ACTTXT:	ITEXT	(<Cannot read account string while checkpointing
job ^D/JOBNUM/; assuming a null string>)
	SUBTTL	ACTCHK - GETTABS USED FOR CHECKPOINTING

;THE ARGUMENTS TO THE TABS MACRO ARE:
;	1) ARGUMENT TO GETTAB
;	2) INSTRUCTION TO STORE THE RESULT

DEFINE TABS,<
	T	<.GTRCT>,<PUSHJ P,DSKRED>
	T	<.GTWCT>,<PUSHJ P,DSKWRT>
	T	<.GTKCT>,<MOVEM T1,CPJBUF+CCTI>
	T	<.GTVKS>,<MOVEM T1,CPJBUF+CVCTI>
	T	<.GTEBT>,<MOVEM T1,CPJBUF+CEBOX>
	T	<.GTMBT>,<MOVEM T1,CPJBUF+CMBOX>
	T	<.GTUUC>,<MOVEM T1,CPJBUF+CMCALL>
	T	<.GTTRQ>,<MOVEM T1,CPJBUF+CQUTIM>
> ;END DEFINE TABS

DSKRED:	ANDX	T1,RC.TTL	;ISOLATE TOTAL DISK READS
	MOVEM	T1,CPJBUF+CDREAD	;STORE IT
	POPJ	P,

DSKWRT:	ANDX	T1,WC.TTL	;ISOLATE TOTAL DISK WRITES
	MOVEM	T1,CPJBUF+CDWRIT	;STORE IT
	POPJ	P,

;NOW GENERATE THE TABLES

DEFINE T(A,B),<
	EXP	<A>
>

GTAB1:	TABS
	.NMTAB==.-GTAB1

DEFINE T(A,B),<
	EXP	<B>
>

GTAB2:	TABS


;THESE TABLES FOR JOBS LOGGED IN BEFORE THE ACTDAE STARTED (SYSJOB, INITIA)

DEFINE TABS,<
	T	<.GTPPN>,<MOVEM T1,CPJBUF+CPPN>
	T	<.GTNM1>,<MOVEM T1,CPJBUF+CNAME1>
	T	<.GTNM2>,<MOVEM T1,CPJBUF+CNAME2>
	T	<.GTJLT>,<MOVEM T1,CPJBUF+CSESST>
	T	<.GTLIM>,<PUSHJ P,ISBTCH>
	T	<.GTJLT>,<MOVEM T1,CPJBUF+CJLGTM>
> ;END DEFINE TABS

ISBTCH:	TXNE	T1,JB.LBT	;IS THIS A BATCH JOB
	SKIPA	T1,[2]		;YES, GET CODE FOR BATCH
	MOVEI	T1,1		;NO, CODE FOR TIMESHARING JOB
	MOVEM	T1,CPJBUF+CJBTYP ;STORE TYPE
	POPJ	P,		;RETURN FOR MORE GETTABS

;NOW GENERATE THE TABLES

DEFINE T(A,B),<
	EXP	<A>
>

GTAB3:	TABS
	.NUTAB==.-GTAB3

DEFINE T(A,B),<
	EXP	<B>
>

GTAB4:	TABS
;SUBROUTINE TO GET THE CURRENT TTY STATISTICS FOR THE JOB IN JOBNUM
;	FILLS IN THE WORDS IN CPJBUF, DOESN'T DO ANYTHING IF THE JOB IS DETACHED

GTTTYS:	HRRZ	T3,JOBNUM	;GET THE JOB NUMBER
	TRMNO.	T3,		;LINE NUMBER OF JOB
	  POPJ	P,		;ASSUME DETACHED
	MOVEI	T2,.TOBCT	;GET MONITOR COMMANDS IN LEFT HALF AND COUNT OF
	MOVE	T1,[2,,T2]	; BREAK CHARACTERS TYPED BY USER IN RIGHT HALF
	TRMOP.	T1,
	  SETZ	T1,		;CAN'T GET IT
	HLRM	T1,CPJBUF+CTTCMD
	HRRM	T1,CPJBUF+CTTYBR
	MOVEI	T2,.TOICT	;COUNT OF BREAK CHARACTERS TYPED
	MOVE	T1,[2,,T2]
	TRMOP.	T1,
	  SETZ	T1,		;CAN'T GET IT
	MOVEM	T1,CPJBUF+CTTYI
	MOVEI	T2,.TOOCT	;OUTPUT CHARACTERS (INCLUDING FILL)
	MOVE	T1,[2,,T2]
	TRMOP.	T1,
	  SETZ	T1,		;CAN'T GET IT
	MOVEM	T1,CPJBUF+CTTYO
	POPJ	P,		;AND RETURN
;SETTNL - SUBROUTINE TO SET UP TERMINAL DESIGNATOR, NODE NAME AND LINE NUMBER
;	FOR THE JOB IN T1.  STORES VALUES IN MONTDE, MONLNO, AND MONNOD WHICH
;	IS WHERE THE SYSTEM RESTART RECORD WOULD LIKE TO FIND THEM.
;USES T1-T4

SETTNL:	SETZM	MONLNO		;GET RID OF OLD STUFF FIRST
	SETZM	MONNOD		;...
	MOVSI	T4,(ASCIZ/D/)	;ASSUME DETACHED
	TRMNO.	T1,		;GET TERMINAL DESIGNATOR
	  JRST	SETTN1		;DETACHED
	DPB	T1,[POINT 9,MONLNO,35] ;STORE IN CASE NO NETWORKS
	GETLCH	T1		;GET LINE CHARACTERISTICS
	MOVSI	T4,(ASCIZ/T/)	;ASSUME REGULAR TTY
	TXNE	T1,GL.CTY	;THE SYSTEM CTY
	MOVSI	T4,(ASCIZ/C/)	;YES
	TXNE	T1,GL.ITY	;INVISIBLE (PSEUDO) TTY
	MOVSI	T4,(ASCIZ/P/)	;YES
	HRRZS	T1		;GET RID OF GETLCH BITS
	GTNTN.	T1,		;CONVERT TO NODE AND LINE
	  JRST	SETTN1		;NO NETWORKS
	HRRZM	T1,MONLNO	;STORE REAL LINE NUMBER
	HLRZ	T3,T1		;ISOLATE NODE NUMBER
	MOVEI	T2,2		;NUMBER OF ARGUMENTS
	MOVE	T1,[.NDRNN,,T2]	;RETURN NODE NAME FOR NUMBER
	NODE.	T1,		;ASK TODD
	  SKIPA			;FAILED?
	MOVEM	T1,MONNOD	;STORE SIXBIT NODE NAME
SETTN1:	MOVEM	T4,MONTDE	;STORE TERMINAL DESIGNATOR
	POPJ	P,		;AND RETURN
;CHKSPN - Routine to checkpoint a disk spindle data area.  The
;	device data area has already been read into CPDBUF.
;	This routine will also be called whenever a dismount message
;	is received for the device in CPDBUF.

CHKSPN:	MOVE	T1,CURDTM	;GET CURRENT DATE/TIME
	MOVEM	T1,CPDBUF+SLSTCK ;STORE IT IN THE BLOCK
	$RETT


;CHKFSR - Routine to checkpoint a user file structure data area.  The
;	device data area has already been read into CPDBUF.
;	This routine will also be called whenever a dismount message
;	is received for the device in CPDBUF.

CHKFSR:	MOVE	T1,CURDTM	;GET CURRENT DATE/TIME
	MOVEM	T1,CPDBUF+FLSTCK ;STORE IT IN THE BLOCK
	$RETT


;CHKDTA - Routine to checkpoint a user DECtape device data area.  The
;	device data area has already been read into CPDBUF.
;	This routine will also be called whenever a dismount message
;	is received for the device in CPDBUF.

CHKDTA:	MOVE	T1,CURDTM	;GET CURRENT DATE/TIME
	MOVEM	T1,CPDBUF+DLSTCK ;STORE IT IN THE BLOCK
	$RETT
;CHKMTA - Routine to checkpoint a user magtape device data area.  The
;	device data area has already been read into CPDBUF.
;	This routine will also be called whenever a dismount message
;	is received for the device in CPDBUF.

CHKMTA:	MOVE	T1,CURDTM	;GET CURRENT DATE/TIME
	MOVEM	T1,CPDBUF+MLSTCK ;STORE IT IN THE BLOCK
	MOVEI	T2,MTABLK	;ADDRESS OF .TFSTA ARGUMENT BLOCK
	MOVEI	T1,.TFSTA
	MOVEM	T1,.TSFUN(T2)	;READ THE STATISTICS
	MOVE	T1,CPDBUF+DEVICE ;MAGTAPE DEVICE NAME
	MOVEM	T1,.TSDEV(T2)
	MOVE	T1,[.TSHWE+1,,MTABLK]
	TAPOP.	T1,
	  POPJ	P,		;WHAT!
	MOVE	T1,.TSCRD(T2)	;MAGTAPE READS
	MOVEM	T1,CPDBUF+MMREAD
	MOVE	T1,.TSCWR(T2)	;MAGTAPE WRITES
	MOVEM	T1,CPDBUF+MMWRIT
	MOVE	T1,.TSSRE(T2)	;SOFT READ ERRORS
	MOVEM	T1,CPDBUF+MNOSRE
	MOVE	T1,.TSHRE(T2)	;HARD READ ERRORS
	MOVEM	T1,CPDBUF+MNOHRE
	MOVE	T1,.TSSWE(T2)	;SOFT WRITE ERRORS
	MOVEM	T1,CPDBUF+MNOSWE
	MOVE	T1,.TSHWE(T2)	;HARD WRITE ERRORS
	MOVEM	T1,CPDBUF+MNOHWE
	MOVE	T1,.TSREC(T2)	;RECORDS
	MOVEM	T1,CPDBUF+MRECRD
	$RETT

MTABLK:	BLOCK	.TSHWE+1	;BLOCK FOR TAPOP. STATISTICS
;FNDDEV - Routine to fine the device area for the specified job and device.
;	If an empty device area is found, the device count is stored in
;	DEVAVL.
;Call:	JOBNUM contains the job number
;	T1 contains the device name
;Returns false if EOF and the device area has not been found
;Returns true if the device area has been found.  CPDBUF contains the data.

FNDDEV:	$CALL	.SAVE1
	MOVE	P1,T1		;SAVE THE DEVICE NAME
	PUSHJ	P,CPDSAU	;OPEN THE DEVICE CHECKPOINT FILE
	SETZM	DEVNUM		;START WITH THE FIRST DEVICE IN THE FILE
	SETZM	DEVAVL		;INITIALIZE FOR FIRST AVAILABLE SLOT IN THE FILE
FNDDE1:	$CALL	READDP		;READ THE NEXT BLOCK
	JUMPF	[MOVE	T1,DEVNUM	;EOF
		SKIPN	DEVAVL		;IS THERE AN EMPTY DEVICE AREA?
		MOVEM	T1,DEVAVL	;NO. INDICATE THE LAST OF THE FILE
		$RETF]
	CAMN	P1,CPDBUF+DEVICE ;IS THIS THE CORRECT DEVICE?
	$RETT			;YES.
	SKIPE	CPDBUF+DEVICE	;IS THIS AREA AVAILABLE?
	JRST	FNDDE1		;NO. READ IN THE NEXT DEVICE AREA
	MOVE	T1,DEVNUM	;YES.
	SKIPN	DEVAVL		;HAS AN AREA ALREADY BEEN FOUND?
	MOVEM	T1,DEVAVL	;NO. STORE THIS DEVICE AREA COUNT
	JRST	FNDDE1		;AND READ THE NEXT DEVICE AREA

;ALLDEV - ROUTINE TO FIND ALL THE DEVICES FOR A JOB AND CALL A SERVICE FOR
;	EACH ONE AFTER READING THE DATA INTO CPDBUF.
;CALL T1 =  0,,ROUTINE TO SCAN THE FILE
;     T1 = -1,,ROUTINE TO SCAN THEN DELETE THE FILE
;     JOBNUM = THE JOB WE ARE LOOKING AT
;"ROUTINE" GET CALLED FOR EACH DEVICE WITH P1 = THE DEVICE TYPE NUMBER

ALLDEV:	$CALL	.SAVE2		;SAVE SOME ACS
	MOVE	P2,T1		;COPY ROUTINE TO CALL
	PUSHJ	P,CPDSAU	;OPEN THE CORRECT FILE
	SETZM	DEVNUM		;START AT THE BEGINNING OF THE FILE
ALLDV1:	$CALL	READDP		;READ IN A DEVICE AREA
	JUMPF	ALLDV2		;END OF FILE
	SKIPN	CPDBUF+DEVICE	;IS THERE A DEVICE IN THIS AREA
	JRST	ALLDV1		;NO, TRY THE NEXT AREA
	SKIPLE	P1,CPDBUF+DEVTYP ;GET DEVICE TYPE
	CAILE	P1,4		;;;RANGE CHECK IT
ACTIDT:	JRST	[$WTO	(<Accounting error>,<Unknown device type ^O/P1/>,,<$WTFLG(WT.SJI)>)
		 JRST	ALLDV1]	;GET ANOTHER DEVICE
	PUSHJ	P,(P2)		;CALL ROUTINE WITH GOOD "P1"
	JRST	ALLDV1		;AND GET ANOTHER DEVICE
ALLDV2:	MOVE	T1,DEVNUM	;GET DEVICE AFTER EOF
	CAIE	T1,1		;IF FIRST, THEN FILE IS EMPTY, DELETE IT
	SKIPGE	P2		;CALLER WANT FILE TO DISAPPEAR
	PUSHJ	P,CPDDEL	;YES, DELETE IT
	PUSHJ	P,CPDCLS	;CLOSE THE FILE
	POPJ	P,		;AND RETURN
	SUBTTL	ACTCHK - TIME KEEPING ROUTINES

DATIM:	$CALL	I%NOW		;GET THE CURRENT DATE/TIME
	MOVEM	S1,CURDTM	;SAVE IT
	$RETT

;CHKTMR - RETURN NUMBER OF SECONDS INTO THE CURRENT HOUR

CHKTMR:	MSTIME	S1,		;GET MS. PAST MIDNIGHT
	IDIVI	S1,^D1000	;GET SECONDS PAST MIDNIGHT
	IDIVI	S1,^D3600	;S1 = THE HOUR, S2 = SECONDS PAST THE HOUR
	POPJ	P,		;RETURN VALUES

;NXTCHK - COMPUTE THE NEXT CHECKPOINT TIME

NXTCHK:	PUSHJ	P,CHKTMR	;FIND OUT CURRENT TIME IN CHECKPOINT UNITS
	MOVSI	S1,-INTBLN	;NUMBER OF ENTRIES IN THE TABLE
	CAML	S2,INTTBL(S1)	;STOP WHEN WE FIND ONE BEYOND NOW
	AOBJN	S1,.-1		;KEEP LOOKING
	HRRZM	S1,CHKNDX	;SAVE INDEX INTO TABLE
	POPJ	P,		;RETURN

;CHKIVL - COMPUTE THE TIME REMAINING UNTIL A CHECKPOINT IS REQUIRED
;		DOES THE CHECKPOINT IF IT IS TIME TO

CHKIVL:	PUSHJ	P,CHKTMR	;FIND OUT THE CURRENT TIME
	SKIPGE	S1,CHKNDX	;INDEX FOR NEXT COMPUTED TIME
	JRST	CHKIV1		;FORCED ONE AT STARTUP, DO IT NOW
	MOVE	S1,INTTBL(S1)	;AND GET "WHEN" FROM TABLE
	SUB	S1,S2		;S1 = TIME TO DESIRED CHECKPOINT
	CAILE	S1,CHKINT*^D60	;IF TIME .GT. THE CHECKPOINT INTERVAL
	SETO	S1,		;THEN THIS IS WRAP AROUND ON THE HOUR
	JUMPG	S1,.POPJ	;RETURN TIME IF NOT YET TIME TO CHECKPOINT
CHKIV1:	PUSHJ	P,CHKAJB	;CHECKPOINT ALL ACTIVE JOBS
	SKIPN	FAIIFN		;IF NO FAILURE LOG FILE IFN, TRY OF OPEN FILE
	PUSHJ	P,OPNFAI
	JRST	CHKIVL		;RECOMPUTE/RETURN TIME TIL NEXT ONE

;THE TABLE USED FOR DETERMINING WHEN THE NEXT CHECKPOINT WILL OCCUR

INTTBL:
..VAL==0
REPEAT ^D60,<
	..VAL==..VAL+<CHKINT*^D60>
	IFL ..VAL-^D3600,<EXP ..VAL>
>
	EXP	^D3600		;LAST ENTRY AT THE FULL HOUR
INTBLN==.-INTTBL		;NUMBER OF ENTRIES IN THE TABLE
	SUBTTL	ACTUSG - MODULE TO HANDLE USAGE FILES


;GENERAL DEFINITIONS

USGBSZ:	BLOCK	1		;USAGE FILE BYTE SIZE
USGBPT:	BLOCK	1		;USAGE FILE BYTE POINTER
USGOSZ:	BLOCK 1			;SIZE OF USAGE.OUT AT FIRST SIGHT
USGIFN:	BLOCK 1			;GALAXY HANDLE FOR USAGE.OUT
USGPTR:	BLOCK 1			;BYTE POINTER INTO USGBUF FOR THE CURRENT ENTRY
SAVPTR:	BLOCK 1			;TEMPORARY STORAGE FOR USGPTR
USGBUF:	BLOCK ^D200		;BUFFERS WHERE USAGE ENTRIES ARE BUILT BEFORE BEING
				; WRITTEN OUT TO THE USAGE.OUT FILE.
USGBND:				;FIRST WORD OUTSIDE THE BUFFER

NUMBER:	BLOCK 1			;TEMPORARY STORAGE FOR DATA BEING FORMATTED
USGENT:	BLOCK 1			;CURRENT ENTRY BEING WORKED ON
USGERD:	BLOCK 1			;CURRENT RECORD DEFINITION BLOCK FOR "USGENT"
JIFSEC:	BLOCK 1			;NUMBER OF JIFFIES/SECOND ON THIS MACHINE
ETICKS:	BLOCK 1			;NUMBER OF EBOX TICKS/JIFFY
MTICKS:	BLOCK 1			;NUMBER OF MBOX TICKS/JIFFY

FAIIFN:	EXP	0		;VALIDATION FAILURE FILE IFN
FAIPAG:	EXP	0		;ADDRESS OF VALIDATION FAILURE BUFFER
FAIMAX:	EXP	0		;MAXIMUM BYTES IN BUFFER
FAICNT:	EXP	0		;NUMBER OF BYTES THAT CAN FIT
FAIPTR:	EXP	0		;BYTE POINTER INTO BUFFER

USRZER:				;START ZEROING HERE
USRERR:	BLOCK	1		;ERROR CODE
USRJOB:	BLOCK	1		;JOB NUMBER OF OFFENDER
USRPPN:	BLOCK	1		;PPN OF OFFENDER
USRNAM:	BLOCK	2		;12 CHAR SIXBIT NAME OF OFFENDER
USRPRG:	BLOCK	1		;PROGRAM NAME OF OFFENDER

USRARG:	BLOCK	1		;START OF ARG BLOCK FOR NETOP.
USRFLG:	BLOCK	1		;FLAGS RETURNED BY NETOP.
USRUDX:	BLOCK	1		;UDX OF OFFENDER
USRDTY:	BLOCK	1		;DEVTYP OF HIS TTY
USRDCH:	BLOCK	1		;DEVCHR OF HIS TTY
USRNDA:	BLOCK	1		;ADDRESS OF STRING BLOCK CONTAINING HIS NODE
USRLNA:	BLOCK	1		;ADDRESS OF STRING BLOCK CONTAINING HIS LINE

USRTTY:	BLOCK	1		;TTY NAME OF OFFENDER
USRNOD:	BLOCK	1	;ANF NODE NUMBER OF OFFENDER
USRNDN:	BLOCK	<<^D16/4>+1>	;STRING BLOCK FOR NODE NAME OF OFFENDER
USRLIN:	BLOCK	<<^D16/4>+1>	;STRING BLOCK FOR LINE NAME OF OFFENDER

USRAPC:	BLOCK	1		;APC CODE OF OFFENDER
USRZEN==.-1
;USGMAK - ROUTINE TO DO PRELIMINARY SET UP FOR MAKING ANY KIND OF USAGE ENTRY.
;	SO FAR ONLY QUEUE. UUO'S ARE ALLOWED FOR PROGRAMS OTHER THAN THE
;	ACTDAE TO MAKE AN ENTRY.
;CALL:	QUEADR/ ADDRESS OF MAKE AN ENTRY DATA WHICH CONTAINS:
;		0/ USENT$
;		1/ ENTRY TYPE
;		2/ BEGINNING OF DEFUS LIST
;	MDBADR/ MESSAGE DESCRIPTOR BLOCK ADDRESS
;	MMSADR/ MESSAGE DATA ADDRESS

USGMAK:	SKIPN	QUEFLG		;ONLY DEFINED FROM QUEUE. UUO
	JRST	IGNORE		;WASN'T. RELEASE MESSAGE AND IGNORE IT
	MOVE	T2,QUEADR	;ADDRESS OF DATA IN THE FORMAT THIS ROUTINE EXPECTS
	MOVE	T1,1(T2)	;ENTRY TYPE
	MOVEI	DEFADR,2(T2)	;BEGINNING ADDRESS OF DEFUS LIST
	PUSHJ	P,MAKENT	;MAKE THE ENTRY IN THE USAGE FILE
	PUSH	P,TF		;SAVE SUCCESS/FAILURE OF MAKENT
	MOVE	S1,QUELEN	;GIVE BACK THE MEMORY USED FOR DEFUS LIST
	MOVE	S2,QUEADR
	$CALL	M%RMEM
	PUSHJ	P,M%GPAG	;GET A PAGE FOR THE RESPONSE
	MOVEM	S1,SABADR	;STORE WHERE COMMON ROUTINES WANT IT
	MOVX	T1,MF.NOM	;NO MESSAGE (YET)
	MOVEM	T1,.MSFLG(S1)	;STORE FLAG SETTINGS
	POP	P,TF		;RESTORE RETURN FROM MAKENT
	SKIPT			;DID MAKENT HONOR THE REQUEST
	FATAL	(IET,<Invalid entry type ^D/USGENT/>,ACIET%,.+1)
	PJRST	ACTVXT		;RELEASE OLD MSG AND SEND QUEUE UUO RESPONSE
;MAKENT - ROUTINE TO BE CALLED WHEN AN ENTRY IS TO BE APPENDED TO USAGE.OUT.
;CALL:	MOVE	T1,ENTRY NUMBER
;	MOVE 	DEFADR,BEGINNING ADDRESS OF DEFUS DATA LIST

MAKENT:	MOVEM	T1,USGENT	;SAVE ENTRY NUMBER DESIRED
	JUMPLE	T1,.RETF	;FIRST ENTRY IS 1
	CAIL	T1,.UTUSR	;SYSTEM OR USER DEFINED ENTRY
	JRST	[SUBI T1,.UTUSR-1 ;USER, CONVERT TO TABLE INDEX
		 CAILE T1,ENTRUL## ;SEE IF DEFINED IN ACTRCD
		 $RETF		;NOPE, GIVE ERROR RETURN
		 MOVE T1,ENTRYU##-1(T1) ;GET RECORD DEFINITION ADDRESS
		 JRST MAKEN1]	;JOIN MAIN CODE AGAIN
	CAILE	T1,ENTRYL##	;SEE IF DEFINED IN ACTRCD
	$RETF			;NOPE, GIVE ERROR RETURN
	MOVE	T1,ENTRYS##-1(T1) ;GET RECORD DEFINITION ADDRESS
MAKEN1:	MOVEM	T1,USGERD	;SAVE FOR DATFIL
	MOVE	S1,USGBPT	;GET BYTE POINTER
	HRRI	S1,USGBUF	;ADD IN ADDRESS
	MOVEM	S1,USGPTR	;STORE IT
	SETZM	USGBUF		;CLEAR THE WORKING BUFFER
	MOVE	S1,[USGBUF,,USGBUF+1]
	BLT	S1,USGBND-1	;CLEAR IT
	PUSHJ	P,PREFIL	;GO PRE-FILL THE ENTRY
	MOVE	T1,USGPTR	;BYTE POINTER AT END
	MOVEM	T1,SAVPTR	;SAVE TO COMPUTE SIZE OF ENTRY
	PUSHJ	P,DATFIL	;NOW FILL IN THE SUPPLIED DATA
	PUSHJ	P,USGAPP	;GO PUT ENTRY INTO THE FILE
	$RETT			;MAKENT SUCCEEDS
;PREFIL - ROUTINE TO PRE-FILL AN ENTRY IN CASE SOME DATA ISN'T PROVIDED BY
;	THE CALLER.
;CALL:	MOVE	T1,ADDRESS OF RECORD LIST
;	USGENT CONTAINS THE ENTRY NUMBER

PREFIL:	$CALL	.SAVE4
	HRRZ	P1,T1
	TXO	P1,<(P2)>	;PUT P2 IN INDEX REGISTER FOR "DOUBLE INDEXING"
	MOVEI	P2,1		;FIRST RECORD SEQUENCE NUMBER
	SKIPE	MAKDUE		;ARE WE MAKING DISK USAGE ENTRIES (2ND HALF)
	MOVEI	P2,3		;YES, GO STRAIGHT TO THE 3RD RECORD
PREFI1:	HRRZ	P3,@P1		;GET ADDRESS OF DATA ITEM LIST
	PUSHJ	P,MAKHDR	;FILL FIRST 20 CHARACTERS
	HLRZ	P4,(P3)		;GET DATA ITEM COUNT -- P4 IS THE LOOP COUNTER
				; OVER RECORD'S DATA LIST
	ADDI	P3,1		;STEP TO BEGINNING OF DATA ITEMS IN LIST
PREFI2:	MOVE	T1,USGPTR	;GET BYTE POINTER TO THE BEGINNING OF THIS
	MOVEM	T1,1(P3)	; DATUM IN USGBUF AND SAVE IT IN THE
				; SECOND WORD OF THIS DATUM'S DESCRIPTOR
	MOVSI	T1,(1B0)	;INDICATE NULL DATA IN T1
	LDB	T3,[POINT 12,(P3),11]	;GET CONVERSION TYPE
	LDB	LENGTH,[POINT 9,(P3),20]	;GET LENGTH OF DATA ITEM
	PUSHJ	P,CONVRT	;FILL
	ADDI	P3,2		;STEP TO NEXT DATA ITEM DESCRIPTOR
	SOJG	P4,PREFI2	;PRE-FILL THE RECORD
	PUSHJ	P,CRLF		;PUT IN CARRIAGE RETURN-LINE FEED
	CAMGE	P2,(P1)		;HAVE ALL RECORDS BEEN PRE-FILLED?
	AOJA	P2,PREFI1	;NO. DO NEXT RECORD
	POPJ	P,


;CRLF - ROUTINE TO OUTPUT A CARRIAGE RETURN-LINE FEED.

CRLF:	MOVEI	T1,.CHCRT	;CARRIAGE RETURN
	IDPB	T1,USGPTR
	MOVEI	T1,.CHLFD	;LINE-FEED
	IDPB	T1,USGPTR
	POPJ	P,
;MAKHED - ROUTINE TO ENTER THE FIRST 20 CHARACTERS OF A RECORD.
;	TO BE SAFE, THE ONLY ROUTINE CALLING THIS ONE IS PREFIL.
;
;CALL:	MOVE	P1,ADJUSTED INDEXED POINTER TO RECORD LIST
;	MOVE	P2,RECORD SEQUENCE #
;	MOVE	P3,ADDRESS OF RECORDS' DATA ITEM LIST

MAKHDR:	MOVE	T1,USGENT	;ENTRY TYPE ADDRESS
	TXO	T1,US%IMM	;INDICATE T1 CONTAINS DATA
	MOVEI	LENGTH,^D4	;LENGTH OF FOUR
	MOVEI	T3,.USDEC	;DECIMAL CONVERSION
	PUSHJ	P,CONVRT
	MOVEI	T1,^D1		;INDICATE TOPS10 OPERATING SYSTEM
	TXO	T1,US%IMM	;INDICATE THAT T1 CONTAINS THE DATA
	MOVEI	LENGTH,^D1	;LENGTH OF 1
	MOVEI	T3,.USDEC	;DECIMAL CONVERSION
	PUSHJ	P,CONVRT
	HRRZ	T1,P2		;RECORD SEQUENCE NUMBER
	TXO	T1,US%IMM	;INDICATE T1 CONTAINS DATA
	MOVEI	LENGTH,^D1	;LENGTH OF 1
	MOVEI	T3,.USDEC	;DECIMAL CONVERSION
	PUSHJ	P,CONVRT
	LDB	T1,[POINT 9,(P3),26]	;DEC REVISION NUMBER
	TXO	T1,US%IMM	;INDICATE T1 CONTAINS DATA
	MOVEI	LENGTH,^D2	;LENGTH OF 2
	MOVEI	T3,.USDEC	;DECIMAL CONVERSION
	PUSHJ	P,CONVRT
	LDB	T1,[POINT 9,(P3),35]	;CUSTOMER REVISION NUMBER
	TXO	T1,US%IMM	;INDICATE THAT T1 CONTAINS DATA
	MOVEI	LENGTH,^D2	;LENGTH OF 2
	MOVEI	T3,.USDEC	;DECIMAL CONVERSION
	PUSHJ	P,CONVRT
	MOVEI	LENGTH,^D10	;NOW DO THE FILLER
	MOVEI	T3,.USSPC	;SPACE CONVERSION
	PUSHJ	P,CONVRT
	POPJ	P,
;DATFIL - ROUTINE TO TAKE GIVEN DATA, CONVERT TO ASCII AND PUT IN ITS PROPER
;	PLACE OF THE USAGE ENTRY IN USGBUF.  NOTE THAT THE ENTRY SHOULD HAVE
;	ALREADY BEEN PRE-FILLED SINCE THE BYTE POINTER OF EACH DATUM IS STORED
;	IN ALL THE DATA ITEM LISTS. SEE PREFIL ROUTINE.  USGENT SHOULD ALREADY
;	CONTAIN THE ENTRY NUMBER.
;	P1 WILL BECOME THE ADJUSTED INDEXED POINTER TO THE ENTRY'S RECORD LIST.
;	P2 WILL CONTAIN THE RECORD SEQUENCE NUMBER CURRENTLY BEING SCANNED.
;	P3 WILL CONTAIN THE ADDRESS OF THE CURRENT RECORD'S DATA LIST.
;	P4 IS THE LOOP COUNTER OVER THE CURRENT RECORD'S DATA LIST.
;	DEFADR CONTAINS THE ADDRESS OF THE DEFUS ITEM IN THE MESSAGE CURRENTLY
;	BEING WORKED UPON.  ALL DATA ITEMS OF ALL RECORDS OF AN ENTRY ARE SCANNED
;	FOR EACH DATUM SUPPLIED IN THE MESSAGE.  THAT WAY A PARTICULAR DATUM
;	CAN APPEAR IN AN ENTRY TWICE BUT ONLY OCCUR ONCE IN THE MESSAGE.
;CALL:	MOVE	DEFADR, ADDRESS OF DATA CONSISTING OF DEFUS FORMATTED DATA (SEE ACTSYM)

DATFIL:	$CALL	.SAVE4
	MOVE	P1,USGERD	;GET ADDRESS OF THIS ENTRY'S RECORD LIST
DATFI1:	SKIPN	(DEFADR)	;IS THERE MORE DATA TO PUT IN THE ENTRY?
	POPJ	P,		;NO. ALL DONE PUTTING ENTRY IN USGBUF
	TXO	P1,<(P2)>	;PUT P2 IN INDEX REG. FOR "DOUBLE INDEXING"
	MOVEI	P2,1		;FIRST RECORD SEQUENCE NUMBER
	SKIPE	MAKDUE		;ARE WE MAKING DISK USAGE ENTRIES (2ND HALF)
	MOVEI	P2,3		;YES, GO STRAIGHT TO THE 3RD RECORD
DATFI2:	HRRZ	P3,@P1		;GET ADDRESS OF THIS RECORD'S DATA ITEM LIST
	HLRZ	P4,(P3)		;RECORD'S DATA ITEM COUNT, P4 IS LOOP COUNTER
				; OVER RECORD'S DATA LIST
	ADDI	P3,1		;STEP TO BEGINNING OF RECORD'S DATA LIST
DATFI3:	LDB	T1,[POINTR (<(DEFADR)>,US%COD)] ;GET THE NUMBER OF DATA ITEM IN THE MESSAGE
	LDB	T2,[POINTR (<(P3)>,US%COD)] ;GET DEFUS NUMBER OF RECORD'S DATA ITEM
	CAMN	T1,T2		;ARE THEY THE SAME?
	PUSHJ	P,PUTDAT	;YES. GO CONVERT DATA AND PUT IN USGBUF
	ADDI	P3,2		;STEP TO NEXT DATA ITEM DESCRIPTOR IN RECORD LIST
	SOJG	P4,DATFI3	;SCAN ALL DATA ITEMS IN THIS RECORD (P2)
	CAMGE	P2,(P1)		;HAVE ALL RECORDS BEEN SCANNED FOR THIS PIECE OF DATA?
	AOJA	P2,DATFI2	;NO. DO NEXT RECORD
	ADDI	DEFADR,2	;ADJUST POINTER TO MESSAGE TO POINT TO NEXT DATUM
	JRST	DATFI1		; AND SCAN OVER ALL RECORDS



;PUTDAT - ROUTINE TO CONVERT AND PUT DATA ITEMS INTO USGBUF BASED ON BYTE
;	POINTER STORED IN SECOND WORD OF RECORD MACRO FOR THE DATUM.
;	SEE DATFIL FOR CORRECT AC SET UP.

PUTDAT:	LDB	T3,[POINTR (<(P3)>,US%TYP)] ;GET CONVERSION TYPE
	LDB	LENGTH,[POINTR (<(P3)>,US%LEN)] ;GET LENGTH OF DATUM
	MOVE	T1,1(DEFADR)	;GET THE SECOND WORD OF THIS DATUM
	MOVE	T2,1(P3)	;GET THE BYTE POINTER INTO USGBUF WHERE DATA STARTS
	MOVEM	T2,USGPTR	;CONVRT USES USGPTR AS THE COMMON BYTE POINTER
	PJRST	CONVRT		;PLACE THE DATA INTO USGBUF AND RETURN TO DATFIL
;CONVRT - ROUTINE TO BE CALLED TO CONVERT AND PUT DATA INTO THE USAGE FILE
;	BLOCK (USGBUF) BASED ON THE BYTE POINTER USGPTR.
;CALL:	MOVE	T1,DATA IF BIT 0=1, OR
;		   ADDRESS OF DATA IF BIT 0=0
;	MOVE	LENGTH,LENGTH OF DATA ITEM
;	MOVE	T3,ITEM CONVERSION TYPE (SEE US%TYP DESCRIPTIONS IN ACCSYM)
;	PUSHJ	P,CONVRT
;	RETURN HERE

CONVRT:	PJRST	@CONVR1(T3)

CONVR1:	OUTASC			;PUT ASCII DATA IN ENTRY
	OUTSIX			;PUT SIXBIT DATA IN ENTRY
	OUTOCT			;PUT OCTAL NUMBER IN ENTRY
	OUTDEC			;PUT DECIMAL NUMBER IN ENTRY
	OUTDTM			;PUT DATE/TIME IN ENTRY (STANDARD FORMAT)
	OUTSPC			;PUT A SPECIAL ITEM IN ENTRY
	OUTVER			;PUT A VERSION NUMBER IN ENTRY (STANDARD FORMAT)
	OUTSPC			;PUT ALL SPACES IN ENTRY
	OUTODT			;PUT OLD FORMAT DATE/TIME
;OUTODT - ROUTINE TO OUTPUT OLD STYLE TOPS-10 DATE/TIME INTO FORMAT
;	"YYYYMMDDHHMMSS" AND PLACE INTO USAGE BUFFER.

OUTODT:	TXZN	T1,US%IMM	;DOES T1 CONTAIN DATA OR AN ADDRESS
	MOVE	T1,(T1)		;ADDRESS, GET DATUM
	JUMPE	T1,OUTZER	;ZERO FILL IF NULL ARGUMENT
	PUSH	P,T1		;SAVE DATE/TIME
	HLRZ	T1,T1		;GET DATE
	PUSHJ	P,OUTDAT	;OUTPUT DATE
	POP	P,T1		;RESTORE DATE/TIME
	HRRZ	T1,T1		;ONLY TIME
	IMULI	T1,^D60*^D1000	;CONVERT MINUTES AFTER TO MILLISECONDS
	PJRST	OUTTIM		;CONVERT TIME

;OUTASC - ROUTINE TO OUTPUT AN ASCIZ STRING INTO THE USAGE BUFFER.
;
;CALL:	SEE CONVRT ROUTINE FOR SETUP AND CALL

OUTASC:	TXZE	T1,US%IMM	;DOES T1 CONTAIN THE DATA?
	SKIPA	T2,[POINT 7,T1]	;YES.
	MOVSI	T2,(POINT 7,(T1)) ;NO.
OUTAS1:	ILDB	T3,T2		;NOW GET A CHARACTER
	JUMPE	T3,OUTSPC	;IF NULL, SPACE FILL THE REST
	CAIGE	T3,40		;VALID ASCII CHARACTER?
	MOVEI	T3,"\"		;NO. PROVIDE ONE
	IDPB	T3,USGPTR	;PUT THE CHARACTER IN THE USAGE BUFFER
	SOJG	LENGTH,OUTAS1	;LOOP BACK FOR NEXT CHARACTER
	POPJ	P,		;PERFECT FIT. RETURN.

;OUT8BT - ROUTINE TO OUTPUT AN 8-BIT ASCIZ STRING INTO THE USAGE BUFFER.
;
;CALL:	SEE CONVRT ROUTINE FOR SETUP AND CALL

OUT8BT:	TXZE	T1,US%IMM	;DOES T1 CONTAIN THE DATA?
	SKIPA	T2,[POINT 8,T1]	;YES.
	MOVSI	T2,(POINT 8,(T1)) ;NO.
OUT8B1:	ILDB	T3,T2		;NOW GET A CHARACTER
	JUMPE	T3,OUTSPC	;IF NULL, SPACE FILL THE REST
	CAIGE	T3,40		;VALID ASCII CHARACTER?
	MOVEI	T3,"\"		;NO. PROVIDE ONE
	IDPB	T3,USGPTR	;PUT THE CHARACTER IN THE USAGE BUFFER
	SOJG	LENGTH,OUT8B1	;LOOP BACK FOR NEXT CHARACTER
	POPJ	P,		;PERFECT FIT. RETURN.

;OUTSIX - ROUTINE TO FORMAT AND OUTPUT A SIXBIT WORD IN THE USAGE FILE.  NOTE THAT
;	THIS ROUTINE WILL ONLY HANDLE ONE WORD OF SIXBIT DATA.  IF MORE THAN THAT
;	IS REQUIRED, AN ASCIZ STRING SHOULD BE USED INSTEAD (SEE OUTASC).
;CALL:	SEE CONVRT ROUTINE FOR SET UP AND CALL

OUTSIX:	TXZN	T1,US%IMM	;DOES T1 CONTAIN THE DATA?
	MOVE	T1,(T1)		;NO. GET THE DATA
	MOVE	T2,[POINT 6,T1]	;MAKE THE BYTE POINTER
OUTSX1:	ILDB	T3,T2		;GET A CHARACTER
	ADDI	T3,40		;CONVERT TO ASCII
	IDPB	T3,USGPTR	;PUT IT IN THE USAGE BUFFER
	SOJG	LENGTH,OUTSX1	;LOOP UNTIL DONE
	POPJ	P,		;EXACT FIT OF SIX CHARACTERS. RETURN

;OUTOCT AND OUTDEC - ROUTINES TO PUT SIGNED NUMBERS INTO THE USAGE BUFFER.
;	SEE CONVRT ROUTINE FOR SETUP AND CALL.

OUTOCT:	SKIPA	T4,[10]		;OCTAL ENTRY
OUTDEC:	MOVEI	T4,^D10		;DECIMAL ENTRY
	TXZN	T1,US%IMM	;IS THE DATA IN T1 OR AN ADDRESS?
	MOVE	T1,(T1)		;IT'S AN ADDRESS. GET THE DATA.
	PJRST	NUMRHT		;CONVERT AND FORMAT THE NUMBER
;NUMRHT -- ROUTINE TO ENTER A NUMBER INTO USGBUF.
;	(NUMBERS ARE ALWAYS RIGHT-JUSTIFIED IN THE USAGE FILE.)
;CALL:	MOVE	LENGTH,THE MAXIMUM LENGTH OF THE DATA ITEM
;	MOVE	T1,NUMBER TO BE PROCESSED
;	MOVE	T4,RADIX USED TO CONVERT
;	PUSHJ	P,NUMRHT
;	RETURN HERE ALWAYS


NUMRHT:	JUMPLE	T1,OUTZER	;IF NUMBER IS ZERO THE FIELD HAS ALREADY BEEN FILLED
	MOVEM	T1,NUMBER	;SAVE NUMBER
	MOVEI	T3,1		;COUNT THE FIRST DIVIDE
	CAIGE	T1,0		;CHECK FOR NEGATIVE NUMBER
	ADDI	T3,1		;ALLOW FOR NEGATIVE SIGN
NUMRH0:	IDIVI	T1,(T4)
	SKIPE	T1
	AOJA	T3,NUMRH0	;T3 IS CHARACTER COUNT OF NUMBER
	MOVE	T1,NUMBER	;GET NUMBER
NUMRH1:	CAMG	T3,LENGTH	;SKIP IF NUMBER WON'T FIT IN FIELD
	JRST	[MOVEM	T1,NUMBER	;STORE ADJUSTED NUMBER
		JRST	NUMRH2]
	IDIVI	T1,(T4)		;THROW AWAY LEAST SIGNIFICANT DIGIT
	SOJA	T3,NUMRH1	;LOOP UNTIL NUMBER CAN FIT
NUMRH2:	SUBI	T3,(LENGTH)	;NUMBER OF CHARACTERS TO SKIP
	SKIPN	T3		;IF ZERO DON'T ADJUST THE BYTE POINTER
	JRST	NUMRH4
	MOVEI	T1,"0"		;FILL WITH ZERO
NUMRH3:	IDPB	T1,USGPTR	;SKIP OVER ALL FILLS
	AOJL	T3,NUMRH3
NUMRH4:	MOVE	T1,NUMBER	;RESTORE THE NUMBER BEFORE WE FALL INTO RDXSTR
;	PJRST	RDXSTR
;FALL INTO RDXSTR


;RDXSTR -- PUT SIGNED NUMBER INTO USGBUF (NOTE THAT NO FILL IS DONE HERE)
;CALL:	MOVE	T1,NUMBER
;	MOVE	T4,RADIX
;	PUSHJ	P,RDXSTR

RDXSTR::JUMPGE	T1,RDXST1	;CHECK FOR NEGATIVE
	MOVE	T2,T1		;SAVE AWAY ARGUMENT
	MOVEI	T1,"-"		;YES--GET MINUS
	IDPB	T1,USGPTR	;PUT IN STRING
	MOVE	T1,T2		;RESTORE NUMBER
RDXST1:	IDIV	T1,T4		;DIVIDE BY RADIX
	MOVMS	T2		;GET MAGNITUDE
	HRLM	T2,(P)		;SAVE REMAINDER
	SKIPE	T1		;SEE IF ANYTHING LEFT
	PUSHJ	P,RDXST1	;YES--LOOP BACK WITH PD LIST
	HLRZ	T1,(P)		;GET BACK A DIGIT
	ADDI	T1,"0"		;CONVERT TO ASCII
	IDPB	T1,USGPTR
	POPJ	P,
;OUTDTM -- ROUTINE TO PROCESS UNIVERSAL DATE AND TIME (GETTAB %CNDTM) INTO
;	FORMAT "YYYYMMDDHHMMSS" AND PLACE INTO USAGE BUFFER.
;CALL:	SEE CONVRT ROUTINE FOR SETUP.

OUTDTM:	TXZN	T1,US%IMM	;DOES T1 CONTAIN DATA OR AN ADDRESS?
	MOVE	T1,(T1)		;T1 CONTAINS AN ADDRESS. GET THE DATA IN T1
	JUMPE	T1,OUTZER	;ZERO FILL IF NULL ARGUMENT
	PUSHJ	P,CNGDAT	;CHANGE DATE/TIME
	PUSH	P,T1		;SAVE TIME
	MOVE	T1,T2		;GET DATE
	PUSHJ	P,OUTDAT	;OUTPUT DATE
	POP	P,T1		;RETURN TIME
	PJRST	OUTTIM		;CONVERT TIME

;OUTDAT -- OUTPUT ACCOUNTING DATE IN FORMAT OF YYYYMMDD
;CALL:	MOVE	T1,DATE RETURNED BY CNGDAT
;	PUSHJ	P,OUTDAT

OUTDAT:	IDIVI	T1,^D31*^D12	;GET YEAR - 1964
	PUSH	P,T2		;SAVE DAYS IN THE YEAR
	MOVEI	LENGTH,^D4	;SET UP LENGTH SINCE JUSTIFICATION IS NECESSARY WITHIN ITEM
	ADDI	T1,^D1964	;ADJUST YEAR
	TXO	T1,US%IMM	;INDICATE THAT THE DATA IS IN T1
	PUSHJ	P,OUTDEC	;ENTER NUMBER IN USGBUF
	POP	P,T1		;RETURN DAYS IN YEAR
	IDIVI	T1,^D31		;GET MONTHS
	ADDI	T1,1		;ADJUST MONTH #
	TXO	T1,US%IMM	;INDICATE THAT THE DATA IS IN T1
	PUSH	P,T2		;SAVE REMAINDER
	MOVEI	LENGTH,^D2	;FIELD LENGTH
	PUSHJ	P,OUTDEC	;PUT IN USGBUF
	POP	P,T1		;GET DAY NUMBER
	TXO	T1,US%IMM	;INDICATE THAT THE DATA IS IN T1
	AOJA	T1,OUTDEC	;FIELD LENGTH SAME AS MONTHS

;OUTTIM -- ACCOUNTING TIME IN FORMAT HHMMSS
;CALL:	MOVE	T1, TIME RETURNED BY CNGDAT (IN MILLISECONDS)
;	PUSHJ	P,OUTTIM

OUTTIM:	IDIV	T1,[^D3600000]	;GET HOURS
	TXO	T1,US%IMM	;INDICATE THAT THE DATA IS IN T1
	PUSH	P,T2		;SAVE REMAINDER
	MOVEI	LENGTH,^D2	;FIELD LENGTH
	PUSHJ	P,OUTDEC	;PUT IN USGBUF
	POP	P,T1		;GET THE REST
	IDIVI	T1,^D60000	;GET MINUTES
	TXO	T1,US%IMM	;INDICATE THAT THE DATA IS IN T1
	PUSH	P,T2		;SAVE REMAINDER
	MOVEI	LENGTH,2	;FIELD LENGTH
	PUSHJ	P,OUTDEC	;PUT IN USGBUF
	POP	P,T1		;GET THE REST
	IDIVI	T1,^D1000	;GET SECONDS
	TXO	T1,US%IMM	;INDICATE THAT THE DATA IS IN T1
	MOVEI	LENGTH,2	;FIELD LENGTH
	PJRST	OUTDEC		;PUT IN USGBUF
;CNGDAT -- SUBROUTINE TO CONVERT FROM INTERNAL DATE/TIME FORMAT
;CALL:	MOVE	T1,DATE/TIME
;	PUSHJ	P,CNGDAT
;	RETURN WITH T1=TIME IN MS., T2=DATE IN SYSTEM FORMAT (.LT. 0 IF ARG .LT. 0)
;BASED ON IDEAS BY JOHN BARNABY, DAVID ROSENBERG, PETER CONKLIN
;USES T1-4

CNGDAT::PUSH	P,T1		;SAVE TIME FOR LATER
	JUMPL	T1,CNGDT6	;DEFEND AGAINST JUNK INPUT
	HLRZ	T1,T1		;GET DATE PORTION (DAYS SINCE 1858)

	RADIX	10		;**** NOTE WELL ****

	ADDI	T1,<1857-1500>*365+<1857-1500>/4-<1857-1500>/100+<1857-1500>/400+31+28+31+30+31+30+31+31+30+31+17
				;T1=DAYS SINCE JAN 1, 1501
	IDIVI	T1,400*365+400/4-400/100+400/400
				;SPLIT INTO QUADRACENTURY
	LSH	T2,2		;CONVERT TO NUMBER OF QUARTER DAYS
	IDIVI	T2,<100*365+100/4-100/100>*4+400/400
				;SPLIT INTO CENTURY
	IORI	T3,3		;DISCARD FRACTIONS OF DAY
	IDIVI	T3,4*365+1	;SEPARATE INTO YEARS
	LSH	T4,-2		;T4=NO DAYS THIS YEAR
	LSH	T1,2		;T1=4*NO QUADRACENTURIES
	ADD	T1,T2		;T1=NO CENTURIES
	IMULI	T1,100		;T1=100*NO CENTURIES
	ADDI	T1,1501(T3)	;T1 HAS YEAR, T4 HAS DAY IN YEAR

	MOVE	T2,T1		;COPY YEAR TO SEE IF LEAP YEAR
	TRNE	T2,3		;IS THE YEAR A MULT OF 4?
	JRST	CNGDT0		;NO--JUST INDICATE NOT A LEAP YEAR
	IDIVI	T2,100		;SEE IF YEAR IS MULT OF 100
	SKIPN	T3		;IF NOT, THEN LEAP
	TRNN	T2,3		;IS YEAR MULT OF 400?
	TDZA	T3,T3		;YES--LEAP YEAR AFTER ALL
CNGDT0:	MOVEI	T3,1		;SET LEAP YEAR FLAG
				;T3 IS 0 IF LEAP YEAR
	;UNDER RADIX 10 **** NOTE WELL ****

CNGDT1:	SUBI	T1,1964		;SET TO SYSTEM ORIGIN
	IMULI	T1,31*12	;CHANGE TO SYSTEM PSEUDO DAYS
	JUMPN	T3,CNGDT2	;IF NOT LEAP YEAR, PROCEED
	CAIGE	T4,31+29	;LEAP YEAR--SEE IF BEYOND FEB 29
	JRST	CNGDT5		;NO--JUST INCLUDE IN ANSWER
	SOS	T4		;YES--BACK OFF ONE DAY
CNGDT2:	MOVSI	T2,-11		;LOOP FOR 11 MONTHS

CNGDT3:	CAMGE	T4,MONTAB+1(T2)	;SEE IF BEYOND THIS MONTH
	JRST	CNGDT4		;YES--GO FINISH UP
	ADDI	T1,31		;NO--COUNT SYSTEM MONTH
	AOBJN	T2,CNGDT3	;LOOP THROUGH NOVEMBER

CNGDT4:	SUB	T4,MONTAB(T2)	;GET DAYS IN THIS MONTH
CNGDT5:	ADD	T1,T4		;INCLUDE IN FINAL RESULT

CNGDT6:	EXCH	T1,(P)		;SAVE ANSWER, GET TIME
	TLZ	T1,-1		;CLEAR DATE
	MUL	T1,[24*60*60*1000]	;CONVERT TO MILLI-SEC.
	ASHC	T1,17		;POSITION RESULT
	POP	P,T2		;RECOVER DATE
	POPJ	P,		;RETURN

MONTAB:	EXP	0,31,59,90,120,151,181,212,243,273,304,334,365

	RADIX	8
;OUTVER -- PUT WORD IN VERSION NUMBER FORMAT. (NOTE THAT THIS ROUTINE ALWAYS
;	DOES ITS OWN FORMATTING.)
;CALL:	SEE CONVRT ROUTINE FOR THE SETUP.
;	PUSHJ	P,OUTVER
;	RETURN HERE ALWAYS

OUTVER:	TXZN	T1,US%IMM	;DOES T1 CONTAIN DATA OR AN ADDRESS?
	MOVE	T1,(T1)		;AN ADDRESS.  GET THE DATA
	SKIPN	T1		;IF ZERO JUST DO SPACE FILL
	JRST	OUTSPC
	MOVEI	T4,10		;SET UP RADIX HERE
	MOVEM	T1,NUMBER	;PUT THE VERSION NUMBER IN A SAFE PLACE
	LDB	T1,[POINT 9,NUMBER,11] ;GET MAJOR VERSION
	SKIPE	T1		;IF NON-ZERO,
	PUSHJ	P,RDXSTR	;PUT OCTAL NUMBER IN STRING
	LDB	T1,[POINT 6,NUMBER,17] ;GET MINOR VERSION
	JUMPE	T1,OUTVE2	;IF NON-ZERO,
	SOS	T1		;  PRINT IN MODIFIED
	IDIVI	T1,^D26		;  RADIX 26 ALPHA
	JUMPE	T1,OUTVE1	;  JUMP IF ONE CHAR
	MOVEI	T1,"A"-1(T1)	;  ISSUE FIRST OF TWO
	IDPB	T1,USGPTR		;  CHARACTERS
OUTVE1:	MOVEI	T1,"A"(T2)	;  ISSUE "UNITS"
	IDPB	T1,USGPTR	;  CHARACTER
OUTVE2:	HRRZ	T1,NUMBER	;GET EDIT NUMBER
	JUMPE	T1,OUTVE3	;IF NON-ZERO,
	MOVEI	T2,"("		;  ISSUE
	IDPB	T2,USGPTR	;  AS OCTAL WITHIN
	PUSHJ	P,RDXSTR	;  PARENTHESES
	MOVEI	T1,")"		;  ..
	IDPB	T1,USGPTR
OUTVE3:	LDB	T1,[POINT 3,NUMBER,2] ;GET "WHO" FIELD
	JUMPE	T1,.POPJ	;IF NON-ZERO,
	MOVEI	T2,"-"		;  PRINT -
	IDPB	T2,USGPTR	;  AND THEN
	PJRST	RDXSTR		;  AS OCTAL
;OUTSPC - ROUTINE TO OUTPUT (LENGTH) SPACES INTO THE USAGE BUFFER USGBUF.
;	THIS ROUTINE IS ALSO CALLED BY OTHERS TO FINISH UP ANY SPACE FILL.
;CALL:	SEE CONVRT ROUTINE FOR SET UP AND CALL.

OUTSPC:	JUMPLE	LENGTH,.POPJ	;DON'T DO ANYTHING.
	MOVEI	T1," "		;ASCII SPACE
OUTSP1:	IDPB	T1,USGPTR	;OUTPUT THE CHARACTER IN THE USAGE BUFFER
	SOJG	LENGTH,OUTSP1	;LOOP UNTIL DONE
	POPJ	P,


;OUTZER - ROUTINE TO OUTPUT (LENGTH) ZEROES INTO THE USAGE BUFFER USGBUF.
;CALL:	SEE CONVRT ROUTINE FOR SET UP AND CALL.

OUTZER:	JUMPLE	LENGTH,.POPJ	;DON'T DO ANYTHING
	MOVEI	T1,"0"		;ASCII ZERO
OUTZE1:	IDPB	T1,USGPTR	;OUTPUT THE CHARACTER IN THE USAGE BUFFER
	SOJG	LENGTH,OUTZE1	;LOOP UNTIL DONE
	POPJ	P,
;MAKSES - ROUTINE TO MAKE A SESSION ENTRY FROM THE PRIMARY AND AUXILIARY
;	JOB CHECKPOINT FILES.
;CALL:	JOBNUM CONTAINS THE JOB NUMBER
;	CURDTM CONTAINS THE DATE/TIME FOR THE ENTRY
;	CPJBUF AND CAJBUF CONTAIN THE JOBS INFORMATION
;	PUSHJ	P,MAKSES
;	RETURN HERE

MAKSES:	PUSHJ	P,DODIFF	;GET THE SESSION'S DATA
	MOVEI	T1,.UTSEN	;INDICATE A SESSION ENTRY
	MOVEI	DEFADR,SESSIO	;ADDRESS OF SESSION ENTRY'S DEFUS LIST
	PJRST	MAKENT

;MKISES - ROUTINE TO MAKE AN INCOMPLETE SESSION ENTRY FOR RESTART
;	SAME AS MAKSES EXCEPT DIFFERENT ENTRY CODE

MKISES:	PUSHJ	P,DODIFF	;GET THE SESSION DATA
	MOVEI	T1,.UTCKP	;INDICATE CAME FROM RESTART FILE
	MOVEI	DEFADR,SESSIO	;WHERE THE DEFUS LIST IS
	PJRST	MAKENT		;MAKE THE ENTRY


;DODIFF - ROUTINE TO CALCULATE THE ACTUAL VARIANT DATA OF ANY JOB'S SESSION.
;	TO DO THIS, A DATUM OF THE JOB'S SLOT IN THE AUXILLIARY CHECKPOINT
;	FILE IS SUBTRACTED FROM ITS COUNTERPART IN THE PRIMARY JOB CHECKPOINT
;	FILE.  IN SOME CASES, I.E., CONNECT TIME, CALCULATIONS HAVE TO BE
;	MADE.
;CALL:	A CHECKPOINT OF JOB HAS BEEN DONE (THIS IMPLIES THAT CURDTM, THE
;	CURRENT DATE/TIME HAS BEEN UPDATED), AND THE JOB'S SLOTS OF BOTH
;	CHECKPOINT FILES HAVE ALREADY BEEN READ INTO CPJBUF AND CAJBUF.

DODIFF:	MOVE	T1,CPJBUF+CRUNTM	;RUNTIME
	SUB	T1,CAJBUF+CRUNTM
	IDIVI	T1,^D100		;CONVERT TO MS. FROM 10-US.
	MOVEM	T1,SESBLK+CRUNTM
	MUL	T1,JIFSEC		; RUNTIME/TIME IN RUN QUEUE
	MULI	T1,[^D100000000]	;RQT=(RUNTIME IN JIFFIES * JIFFIES PER SECOND
	MOVE	T2,CPJBUF+CQUTIM	; * 10**11)/1000
	DIV	T1,T2
	MOVEM	T1,SESBLK+CQUTIM
	MOVE	T1,CPJBUF+CLSTCK	;CALCULATE CONNECT TIME
	SUB	T1,CPJBUF+CSESST
	MUL	T1,[^D24*^D60*^D60]
	DIV	T1,[1,,0]
	TRNE	T2,1B18			;SHOULD THE NUMBER BE ROUNDED?
	AOS	T1			;YES.
	MOVEM	T1,SESCCT
	MOVE	T1,CPJBUF+CDREAD	;DISK READS
	SUB	T1,CAJBUF+CDREAD
	MOVEM	T1,SESBLK+CDREAD
	MOVE	T1,CPJBUF+CDWRIT	;DISK WRITES
	SUB	T1,CAJBUF+CDWRIT
	MOVEM	T1,SESBLK+CDWRIT
	MOVE	T1,CPJBUF+CCTI		;CORE-TIME INTEGRAL
	SUB	T1,CAJBUF+CCTI
	IMULI	T1,^D100
	IDIV	T1,JIFSEC
	MOVEM	T1,SESBLK+CCTI
	MOVE	T1,CPJBUF+CVCTI		;VIRTUAL CORE-TIME INTEGRAL
	SUB	T1,CAJBUF+CVCTI
	IMULI	T1,^D100
	IDIV	T1,JIFSEC
	MOVEM	T1,SESBLK+CVCTI
	MOVE	T1,CPJBUF+CEBOX		;EBOX RUNTIME
	SUB	T1,CAJBUF+CEBOX
	MOVEM	T1,SESBLK+CEBOX
	MOVE	T1,CPJBUF+CMBOX		;MBOX RUNTIME
	SUB	T1,CAJBUF+CMBOX
	MOVEM	T1,SESBLK+CMBOX
	MOVE	T1,CPJBUF+CMCALL
	SUB	T1,CAJBUF+CMCALL
	MOVEM	T1,SESBLK+CMCALL
DOFDTT:	MOVE	T1,CPJBUF+CTTCMD	;MONITOR COMMANDS
	SUB	T1,CAJBUF+CTTCMD
	MOVEM	T1,SESBLK+CTTCMD
	MOVE	T1,CPJBUF+CTTYI		;TERMINAL INPUT CHARACTERS
	SUB	T1,CAJBUF+CTTYI
	MOVEM	T1,SESBLK+CTTYI
	MOVE	T1,CPJBUF+CTTYO		;TERMINAL OUTPUT CHARACTERS
	SUB	T1,CAJBUF+CTTYO
	MOVEM	T1,SESBLK+CTTYO
	MOVE	T1,CPJBUF+CTTYBR	;TERMINAL BREAK CHARACTERS USER TYPED
	SUB	T1,CAJBUF+CTTYBR
	MOVEM	T1,SESBLK+CTTYBR
	POPJ	P,
;DEFUS LIST FOR SESSION AND INCOMPLETE SESSION ENTRIES

SESSIO:	USJNO.	(CPJBUF+CJOB)	;JOB NUMBER
	USTAD.	(CURDTM)	;CURRENT DATE/TIME
	USTRM.	(CPJBUF+CTERDE)	;TERMINAL DESIGNATOR
	USLNO.	(CPJBUF+CLINNO)	;LINE NUMBER
	USPNM.	(CPJBUF+CPGNAM)	;NAME OF PROGRAM (USUALLY LOGIN)
	USPVR.	(CPJBUF+CPGVER)	;VERSION OF USPNM.
	USAMV.	(CPJBUF+CACVER)	;VERSION OF ACTDAE
	USNOD.	(CPJBUF+CNODE)	;NODE NAME
	USACT.	(CPJBUF+CACCT)	;ACCOUNT STRING
	USRTM.	(SESBLK+CRUNTM)	;RUNTIME
	USSST.	(CPJBUF+CSESST)	;SESSION START DATE/TIME
	USJTY.	(CPJBUF+CJBTYP)	;JOB TYPE
	USBJN.	(CPJBUF+CBTNAM)	;BATCH JOB NAME
	USBSN.	(CPJBUF+CBTSEQ)	;BATCH SEQUENCE NUMBER
	USCOM.	(CPJBUF+CRMRK)	;SESSION REMARK
	USCCT.	(SESCCT)	;SESSION CONNECT TIME
	USRIN.	(CPJBUF+CBTRID)	;BATCH REQUEST ID
	USDKR.	(SESBLK+CDREAD)	;DISK READS
	USDKW.	(SESBLK+CDWRIT)	;DISK WRITES
	USCTI.	(SESBLK+CCTI)	;CORE-TIME INTEGRAL
	USVTI.	(SESBLK+CVCTI)	;VIRTUAL CORE-TIME INTEGRAL
	USEBX.	(SESBLK+CEBOX)	;EBOX MEGACOUNTS
	USMBX.	(SESBLK+CMBOX)	;MBOX MEGACOUNTS
	USMCL.	(SESBLK+CMCALL)	;MONITOR CALLS
	USMCM.	(SESBLK+CTTCMD)	;MONITOR COMMANDS
	USSCL.	(CPJBUF+CCLASS)	;SCHEDULING CLASS
	USTYI.	(SESBLK+CTTYI)	;TERMINAL INPUT CHARACTERS
	USTYO.	(SESBLK+CTTYO)	;TERMINAL OUTPUT CHARACTERS
	USTYW.	(SESBLK+CTTYBR)	;COUNT OF BREAK CHARACTERS USER TYPED
	USRQQ.	(SESBLK+CQUTIM)	;RUN QUEUE QUOTIENT -- RUNTIME/TIME IN QUEUE
	USPPN.	(CPJBUF+CPPN)	;PROJECT-PROGRAMMER NUMBER
	USNM1.	(CPJBUF+CNAME1)	;USER NAME
	USNM3.	(CPJBUF+CNAME2)	;USER NAME (SECOND WORD)

	0			;AND A ZERO TO TERMINATE THE LIST






SESCCT:	BLOCK 1			;SESSION CONNECT TIME
SESBLK:	BLOCK CEND		;STORAGE FOR CALCULATED VALUES
;MAKFSR - Routine to make a user file structure entry based on the correct
;	data being in CPDBUF.

MAKFSR:	PUSHJ	P,FSRDIF	;CALCULATE ANY DIFFERENCES
	MOVEI	T1,.UTMNT	;ENTRY TYPE
	MOVEI	DEFADR,FILMNT	;DEFUS LIST
	PJRST	MAKENT


;FSRDIF - Routine to calculate data needed to make a user file structure entry.

FSRDIF:	MOVE	T1,CPDBUF+FLSTCK	;CALCULATE CONNECT TIME
	SUB	T1,CPDBUF+FSESST
	MUL	T1,[^D24*^D60*^D60]
	DIV	T1,[1,,0]
	TRNE	T2,1B18		;SHOULD THE NUMBER BE ROUNDED?
	AOS	T1		;YES.
	MOVEM	T1,CPDBUF+FCONNE
	POPJ	P,



;DEFUS LIST FOR USER FILE STRUCTURE ENTRY

FILMNT:	USJNO.	(CPDBUF+FJOB)	;JOB NUMBER
	USTAD.	(CURDTM)	;CURRENT DATE/TIME
	USTRM.	(CPDBUF+FTERDE);TERMINAL DESIGNATOR
	USLNO.	(CPDBUF+FLINNO);LINE NUMBER
	USPNM.	(CPDBUF+FPGNAM);PROGRAM NAME
	USPVR.	(CPDBUF+FPGVER);PROGRAM VERSION NUMBER
	USAMV.	(CPDBUF+FACVER);ACTDAE VERSION NUMBER
	USNOD.	(CPDBUF+FNODE)	;NODE NAME
	USFMA.	(CPDBUF+FACCT)	;ACCOUNT OF USER
	USSSI.	(CPDBUF+DEVICE);FILE STRUCTURE NAME
	USFST.	(CPDBUF+FFSTYP);FILE STRUCTURE TYPE
	USTNP.	(CPDBUF+FPCKNO);NUMBER OF PACKS IN FILE STRUCTURE
	USFCT.	(CPDBUF+FCONTY);CONTROLLER TYPE
	USFDT.	(CPDBUF+FDEVTY);DEVICE TYPE
	USFDS.	(CPDBUF+FDISPO);DISPOSITION
	USFOT.	(CPDBUF+FOTEXT);TEXT TO EXPLAIN DISPOSITION
	USFCD.	(CPDBUF+FCREDT);CREATION DATE/TIME
	USFSD.	(CPDBUF+FSCHDT);SCHEDULED DATE/TIME
	USSRV.	(CPDBUF+FSERDT);SERVICED DATE/TIME
	USMCT.	(CPDBUF+FMNTCT);MOUNT COUNT BEFORE MOUNT
	USDCT.	(CPDBUF+FDISCT);MOUNT COUNT AFTER DISMOUNT
	USATP.	(CPDBUF+FACCES);ACCESS TYPE
	USFCO.	(CPDBUF+FCONNE);CONNECT TIME IN SECONDS
	USPPN.	(CPDBUF+FPPN)	;PROJECT-PROGRAMMER NUMBER OF USER
	USNM1.	(CPDBUF+FNAME1);USER NAME
	USNM3.	(CPDBUF+FNAME2);USER NAME (CONT.)
	0			;TERMINATE LIST WITH A ZERO
;MAKMAG - Routine to make a user magtape entry based on the correct
;	data being in CPDBUF.

MAKMAG:	PUSHJ	P,MAGDIF	;CALCULATE ANY DIFFERENCES
	MOVEI	T1,.UTMMT	;ENTRY TYPE
	MOVEI	DEFADR,MAGMNT	;DEFUS LIST
	PJRST	MAKENT


;MAGDIF - Routine to calculate data needed to make a user magtape entry.

MAGDIF:	MOVE	T1,CPDBUF+MLSTCK	;CALCULATE CONNECT TIME
	SUB	T1,CPDBUF+MSESST
	MUL	T1,[^D24*^D60*^D60]
	DIV	T1,[1,,0]
	TRNE	T2,1B18		;SHOULD THE NUMBER BE ROUNDED?
	AOS	T1		;YES.
	MOVEM	T1,CPDBUF+MCONNE
	MOVE	T1,CPDBUF+MMREAD ;READS
	SUB	T1,CADBUF+MMREAD
	IDIVI	T1,^D1000	;IN THOUSANDS
	MOVEM	T1,MGREAD
	MOVE	T1,CPDBUF+MMWRIT ;WRITES
	SUB	T1,CADBUF+MMWRIT
	IDIVI	T1,^D1000	;IN THOUSANDS
	MOVEM	T1,MGWRIT
	MOVE	T1,CPDBUF+MRECRD ;RECORDS READ
	SUB	T1,CADBUF+MRECRD
	SKIPGE	T1		;COULD BE -1 IF NEVER READ
	SETZ	T1,		;MAKE DATA CONSISTANT
	MOVEM	T1,MRCRED
	MOVE	T1,CPDBUF+MNOSRE ;SOFT READ ERRORS
	SUB	T1,CADBUF+MNOSRE
	MOVEM	T1,MAGSRE
	MOVE	T1,CPDBUF+MNOSWE ;SOFT WRITE ERRORS
	SUB	T1,CADBUF+MNOSWE
	MOVEM	T1,MAGSWE
	MOVE	T1,CPDBUF+MNOHRE ;HARD READ ERRORS
	SUB	T1,CADBUF+MNOHRE
	MOVEM	T1,MAGHRE
	MOVE	T1,CPDBUF+MNOHWE ;HARD WRITE ERRORS
	SUB	T1,CADBUF+MNOHWE
	MOVEM	T1,MAGHWE
	POPJ	P,

;DEFUS LIST FOR USER MAGTAPE ENTRY

MAGMNT:	USJNO.	(CPDBUF+MJOB)	;JOB NUMBER
	USTAD.	(CURDTM)	;DATE/TIME ENTRY IS MADE
	USTRM.	(CPDBUF+MTERDE);TERMINAL DESIGNATOR
	USLNO.	(CPDBUF+MLINNO);LINE NUMBER
	USPNM.	(CPDBUF+MPGNAM);PROGRAM NAME
	USPVR.	(CPDBUF+MPGVER);PROGRAM VERSION NUMBER
	USAMV.	(CPDBUF+MACVER);ACTDAE VERSION NUMBER
	USNOD.	(CPDBUF+MNODE)	;NODE NAME
	USMAC.	(CPDBUF+MACCT)	;USER ACCOUNT STRING
	USVID.	(CPDBUF+MVOLID);VOLUME ID RECORDED IN VOL1 LABEL
	USVSN.	(CPDBUF+MRELID);VISUAL LABEL OF TAPE
	USMRF.	(MGREAD)	;MAGTAPE READS - THOUSANDS OF CHARS
	USMWF.	(MGWRIT)	;MAGTAPE WRITES - THOUSANDS OF CHARS
	USMDS.	(CPDBUF+MDISPO);DISPOSITION
	USMTX.	(CPDBUF+MOTEXT);TEXT TO EXPLAIN DISPOSITION
	USMCD.	(CPDBUF+MCREDT);CREATION DATE/TIME OF REQUEST
	USMSD.	(CPDBUF+MSCHDT);SCHEDULED DATE/TIME OF MOUNT REQUEST
	USMVD.	(CPDBUF+MSERDT);SERVICED DATE/TIME OF MOUNT REQUEST
	USMCO.	(CPDBUF+MCONTY);TYPE OF CONTROLLER
	USMLT.	(CPDBUF+MLABEL);LABEL TYPE
	USMLS.	(CPDBUF+MSTATE);VOLUME LABEL STATE
	USMRD.	(MRCRED)	;RECORDS READ
	USMWR.	(MRCWRI)	;RECORDS WRITTEN
	USFSI.	(CPDBUF+MFSTID);FILE SET IDENTIFIER
	USSRE.	(MAGSRE)	;NUMBER OF SOFT READ ERRORS
	USSWE.	(MAGSWE)	;NUMBER OF SOFT WRITE ERRORS
	USHRE.	(MAGHRE)	;NUMBER OF HARD READ ERRORS
	USHWE.	(MAGHWE)	;NUMBER OF HARD WRITE ERRORS
	USMCN.	(CPDBUF+MCONNE);CONNECT TIME IN SECONDS
	USDVN.	(CPDBUF+MEVICE)	;PHYSICAL DEVICE NAME
	USPPN.	(CPDBUF+MPPN)	;PROJECT PROGRAMMER NUMBER
	USNM1.	(CPDBUF+MNAME1);USER NAME
	USNM3.	(CPDBUF+MNAME2);USER NAME (CONT.)
	0			;TERMINATE LIST WITH A ZERO
;MAKDEC - Routine to make a user DECtape entry based on the correct
;	data being in CPDBUF.

MAKDEC:	PUSHJ	P,DECDIF	;CALCULATE ANY DIFFERENCES
	MOVEI	T1,.UTDMT	;ENTRY TYPE
	MOVEI	DEFADR,DECMNT	;DEFUS LIST
	PJRST	MAKENT


;DECDIF - Routine to calculate data needed to make a user DECtape entry.

DECDIF:	MOVE	T1,CPDBUF+DLSTCK	;CALCULATE CONNECT TIME
	SUB	T1,CPDBUF+DSESST
	MUL	T1,[^D24*^D60*^D60]
	DIV	T1,[1,,0]
	TRNE	T2,1B18		;SHOULD THE NUMBER BE ROUNDED?
	AOS	T1		;YES.
	MOVEM	T1,CPDBUF+DCONNE
	POPJ	P,

;DEFUS LIST FOR USER DECTAPE ENTRY

DECMNT:	USJNO.	(CPDBUF+DJOB)	;JOB NUMBER
	USTAD.	(CURDTM)	;DATE/TIME ENTRY IS MADE
	USTRM.	(CPDBUF+DTERDE);TERMINAL DESIGNATOR
	USLNO.	(CPDBUF+DLINNO);LINE NUMBER
	USPNM.	(CPDBUF+DPGNAM);PROGRAM NAME
	USPVR.	(CPDBUF+DPGVER);PROGRAM VERSION NUMBER
	USAMV.	(CPDBUF+DACVER);ACTDAE VERSION NUMBER
	USNOD.	(CPDBUF+DNODE)	;NODE NAME
	USDAN.	(CPDBUF+DACCT)	;ACCOUNT STRING OF USER
	USDVI.	(CPDBUF+DVOLID);DECTAPE LABEL
	USDRI.	(CPDBUF+DRELID);VISUAL LABEL OF DECTAPE
	USDTR.	(DECRED)	;DECTAPE READS
	USDTW.	(DECWRI)	;DECTAPE WRITES
	USDDS.	(CPDBUF+DDISPO);DISPOSITION
	USDTX.	(CPDBUF+DOTEXT);TEXT TO EXPLAIN DISPOSITION
	USDCE.	(CPDBUF+DCREDT);CREATION DATE/TIME
	USDSQ.	(CPDBUF+DSCHDT);SCHEDULED DATE/TIME
	USDSS.	(CPDBUF+DSERDT);SERVICED DATE/TIME
	USDCN.	(CPDBUF+DCONNE);CONNECT TIME
	USDVN.	(CPDBUF+DDVICE)	;PHYSICAL DEVICE NAME
	USPPN.	(CPDBUF+DPPN)	;PROJECT PROGRAMMER NUMBER
	USNM1.	(CPDBUF+DNAME1);USER NAME
	USNM3.	(CPDBUF+DNAME2);USER NAME (CONT.)
	0			;TERMINATE LIST WITH A ZERO
;MAKSPN - Routine to make a disk spindle entry based on the correct
;	data being in CPDBUF.

MAKSPN:	PUSHJ	P,SPNDIF	;CALCULATE ANY DIFFERENCES
	MOVEI	T1,.UTDSU	;ENTRY TYPE
	MOVEI	DEFADR,DSKMNT	;DEFUS LIST
	PJRST	MAKENT


;SPNDIF - Routine to calculate data needed to make a disk spindle entry.

SPNDIF:	MOVE	T1,CPDBUF+SLSTCK	;CALCULATE CONNECT TIME
	SUB	T1,CPDBUF+SCSHIF
	MUL	T1,[^D24*^D60*^D60]
	DIV	T1,[1,,0]
	TRNE	T2,1B18		;SHOULD THE NUMBER BE ROUNDED?
	AOS	T1		;YES.
	MOVEM	T1,CPDBUF+SCONNE
	POPJ	P,

;DEFUS LIST FOR DISK PACK SPINDLE ENTRY

DSKMNT:	USJNO.	(CPDBUF+SJOB)	;JOB NUMBER
	USTAD.	(CURDTM)	;DATE/TIME ENTRY IS MADE
	USTRM.	(CPDBUF+STERDE);TERMINAL DESIGNATOR
	USLNO.	(CPDBUF+SLINNO);LINE NUMBER
	USPNM.	(CPDBUF+SPGNAM);PROGRAM NAME
	USPVR.	(CPDBUF+SPGVER);PROGRAM VERSION NUMBER
	USAMV.	(CPDBUF+SACVER);ACTDAE VERSION NUMBER
	USNOD.	(CPDBUF+SNODE)	;NODE NAME
	USSFS.	(CPDBUF+SFSNAM);FILE STRUCTURE NAME
	USSFT.	(CPDBUF+SFSTYP);FILE STRUCTURE TYPE
	USSCT.	(CPDBUF+SCONTY);CONTROLLER TYPE
	USSDT.	(CPDBUF+SDEVTY);DEVICE TYPE
	USSID.	(CPDBUF+SPAKID);DISK PACK IDENTIFIER
	USSDU.	(CPDBUF+SEVICE)	;DISK UNIT NAME
	USSNP.	(CPDBUF+SPCKNO);TOTAL NUMBER OF PACKS IN FILE STRUCTURE
	USSMN.	(CPDBUF+SPKMTH);M OF N COUNT
	USDTF.	(CPDBUF+SMNTDT);DATE/TIME PACK WAS FIRST SPUN UP
	USDCC.	(CPDBUF+SCONNE);CONNECT TIME
	0			;TERMINATE LIST WITH A ZERO


;STORAGE FOR CALCULATED VALUES

DECRED:	BLOCK 1			;DECTAPE READS (NOT IMPLEMENTED)
DECWRI:	BLOCK 1			;DECTAPE WRITES (NOT IMPLEMENTED)
MAGHRE:	BLOCK 1			;MAGTAPE HARD READ ERRORS
MAGHWE:	BLOCK 1			;MAGTAPE HARD WRITE ERRORS
MAGSRE:	BLOCK 1			;MAGTAPE SOFT READ ERRORS
MAGSWE:	BLOCK 1			;MAGTAPE SOFT WRITE ERRORS
MGREAD:	BLOCK 1			;MAGTAPE READS
MGWRIT:	BLOCK 1			;MAGTAPE WRITES
MRCRED:	BLOCK 1			;MAGTAPE RECORDS READ
MRCWRI:	BLOCK 1			;MAGTAPE RECORDS WRITTEN (TOPS-20 ONLY)
;MAKUFH - ROUTINE TO MAKE A USAGE FILE HEADER WHEN USAGE.OUT IS INITIALLY CREATED.

MAKUFH:	PUSHJ	P,DOUFHD	;FILL IN A USAGE FILE HEADER
	MOVEI	T1,.UTUSB	;FIRST RECORD OF USAGE.OUT
	MOVEI	DEFADR,UFHLST	;POINT TO THE DEFUS LIST
	PJRST	MAKENT		;MAKE THE ENTRY AND RETURN

;MAKRES - ROUTINE TO MAKE A SYSTEM OR ACTDAE RESTART RECORD

MAKRES:	PUSHJ	P,DOREST	;FILL IN A RESTART RECORD
	MOVEI	T1,.UTRST	;SYSTEM/ACTDAE RESTART
	MOVEI	DEFADR,RESLST	;POINT TO THE DEFUS LIST
	PJRST	MAKENT

;DOREST - FILL IN THE DEFUS ITEMS FOR A SYSTEM RESTART RECORD
;	THE UFH RECORD IS THE SAME FORMAT SO THERE IS ONLY 1
;	ROUTINE TO GATHER THE DATA FOR BOTH OF THEM.

DOUFHD:				;OTHER NAME
DOREST:	MOVSI	T2,-.NXTAB	;MAKE AN AOBJN POINTER
RESTA1:	MOVE	T1,GTAB5(T2)	;GET AN ARGUMENT
	GETTAB	T1,		;DO THE GETTAB
	  SETZ	T1,		;ASSUME 0
	XCT	GTAB6(T2)	;STORE THE RESULT
	AOBJN	T2,RESTA1	;AND LOOP
	PJOB	T1,		;GET OUT JOB NUMBER
	MOVEM	T1,MONJNO	;SAVE IT AWAY
	PUSHJ	P,SETTNL	;SET UP TERMINAL, NODE AND LINE
	POPJ	P,		;AND RETURN

DEFINE TABS,<
	T	<%CNFG0>,<MOVEM T1,MONAME+0>
	T	<%CNFG1>,<MOVEM T1,MONAME+1>
	T	<%CNFG2>,<MOVEM T1,MONAME+2>
	T	<%CNFG3>,<MOVEM T1,MONAME+3>
	T	<%CNFG4>,<MOVEM T1,MONAME+4>
	T	<%CNVER>,<MOVEM T1,MONVER>
	T	<%CCSER+2*0>,<MOVEM T1,MONCPI+0>
	T	<%CCSER+2*1>,<MOVEM T1,MONCPI+1>
	T	<%CCSER+2*2>,<MOVEM T1,MONCPI+2>
	T	<%CCSER+2*3>,<MOVEM T1,MONCPI+3>
	T	<%CCSER+2*4>,<MOVEM T1,MONCPI+4>
	T	<%CCSER+2*5>,<MOVEM T1,MONCPI+5>
	T	<%CNCPU>,<MOVEM T1,MONCPN>
	T	<%CNSUP>,<PUSHJ P,SYSUPT>
> ;END DEFINE TABS

SYSUPT:	PUSH	P,T2		;SAVE LOOP INDEX
	IDIV	T1,JIFSEC	;CONVERT JIFFIES TO SECONDS
	MOVEM	T1,MONUPT	;SAVE UPTIME
	POP	P,T2		;RESTORE AC
	POPJ	P,		;RETURN TO GET ANOTHER

;NOW GENERATE THE TABLES

DEFINE T(A,B),<
	EXP	<A>
>

GTAB5:	TABS
	.NXTAB==.-GTAB5

DEFINE T(A,B),<
	EXP	<B>
>

GTAB6:	TABS

MONJNO:	BLOCK 1			;SPACE FOR ACTDAE JOB NUMBER
MONLNO:	BLOCK 1			;SPACE FOR ACTDAE LINE NUMBER
MONNOD:	BLOCK 1			;SPACE FOR ACTDAE NODE NUMBER
MONTDE:	BLOCK 1			;SPACE FOR ACTDAE TERMINAL DESIGNATOR
MONCPN:	BLOCK 1			;SPACE FOR NUMBER OF CPUS IN CONFIG
MONUPT:	BLOCK 1			;SPACE FOR SYSTEM UPTIME (IN SECONDS)
MONVER:	BLOCK 1			;SPACE FOR MONITOR VERSION NUMBER
MONAME:	BLOCK 10		;SPACE FOR MONITOR NAME IN ASCII
MONCPI:	BLOCK 6			;SPACE FOR CPU0 THROUGH CPU5 APR ID


;THE DEFUS LIST PROPER FOR RESTART AND NEW FILE RECORDS

UFHLST:
RESLST:	USJNO.	(MONJNO)	;OUR JOB NUMBER
	USTAD.	(CURDTM)	;CURRENT DATE/TIME
	USTRM.	(MONTDE)	;OUR TERMINAL DESIGNATOR
	USLNO.	(MONLNO)	;OUR LINE NUMBER
	USPNM.	([SIXBIT/ACTDAE/]) ;PROGRAM MAKING THE ENTRY
	USPVR.	(.JBVER)	;VERSION NUMBER
	USAMV.	(.JBVER)	;VERSION OF THE ACTDAE
	USNOD.	(MONNOD)	;OUR NODE NAME
	USSNM.	(MONAME)	;MONITOR NAME
	USMVR.	(MONVER)	;MONITOR VERSION NUMBER
	USMUP.	(MONUPT)	;SYSTEM UPTIME
	USCPN.	(MONCPN)	;NUMBER OF CPUS FROM MONGEN
	USCP0.	(MONCPI+0)	;APRID OF CPU0
	USCP1.	(MONCPI+1)	;APRID OF CPU1
	USCP2.	(MONCPI+2)	;APRID OF CPU2
	USCP3.	(MONCPI+3)	;APRID OF CPU3
	USCP4.	(MONCPI+4)	;APRID OF CPU4
	USCP5.	(MONCPI+5)	;APRID OF CPU5
	USLCK.	(CPJGEN+LASTCH)	;LAST CHECKPOINT DATE/TIME IS ALWAYS THERE
	0			;AND A ZERO THE TERMINATE THE DEFUS LIST
;DOUBC - CALLED BY QUASAR TO DO BILLING CLOSURE

DOUBC:	PUSHJ	P,SESAJB	;MAKE ENTRIES FOR ALL JOBS
	$WTOXX	<Session entries written for all jobs>
	$RETT			;AND RETURN
;DOUFC - ROUTINE TO CLOSE OUT BILLING SESSION FOR ALL ACTIVE JOBS AND THEN
;	CLOSE/RENAME/RE-OPEN USAGE.OUT. CALLED BY QUASAR (SET USAGE FILE-CLOSURE)

DOUFC:	$CALL	.SAVE1		;SAVE P1 FOR A MOMENT
	MOVE	T1,MMSADR	;GET MESSAGE ADDRESS
	MOVE	P1,.OFLAG(T1)	;GET FLAGS
	TXNN	P1,US.NOS	;DO WE WANT /SOME-SESSION-ENTRIES
	PUSHJ	P,SESAJB	;YES, CLOSE OUT ALL THE SESSIONS
	PUSHJ	P,USGCRN	;CLOSE, RENAME USAGE.OUT
	JUMPF	USGUF2		;GIVE DIFFERENT MESSAGE IF DIDN'T MAKE IT
	$WTOXX	<^F/USGFD/ closed and renamed to ^F/USGRFD/>
USGUF1:	PUSHJ	P,USGSAU	;RE-OPEN THE FILE
	SKIPN	USGOSZ		;DID THE RENAME WORK (NEW FILE SIZE = 0)
	PUSHJ	P,MAKUFH	;YES, MAKE A USAGE FILE HEADER RECORD
	$RETT			;AND RETURN
USGUF2:	$WTOXX	<Cannot rename ^F/USGFD/, continuing with old file>
	JRST	USGUF1		;RESUME
	SUBTTL	ACTIO -  MODULE CONTAINING COMMON INPUT/OUTPUT ROUTINES

;CPJSAU - ROUTINE TO OPEN THE PRIMARY JOB CHECKPOINT FILE, USEJOB.BIN, IN
;	SINGLE ACCESS UPDATE MODE.
;	CPJCHN - CHANNEL NUMBER
;	CPJBLK - FILOP. BLOCK
;	USEJOB - LOOPUP/ENTER BLOCK

CPJSAU:	SKIPN	T1,CPJCHN	;DOES A CHANNEL ALREADY EXIST?
	TXO	T1,FO.ASC	;NO. HAVE THE MONITOR GIVE US ONE.
	TXO	T1,FO.PRV	;GIVE US FULL FILE ACCESS TO ACT:
	HRRI	T1,.FOSAU	;OPEN FOR UPDATE MODE
	MOVEM	T1,CPJBLK+.FOFNC
	MOVEI	T1,.IODMP	;DUMP MODE
	TXO	T1,UU.RRC	;LET TONY KEEP THE RIB UP TO DATE
	MOVEM	T1,CPJBLK+.FOIOS
	HRLZ	T1,ACTDEV
	MOVEM	T1,CPJBLK+.FODEV
	SETZM	CPJBLK+.FOBRH
	SETZM	CPJBLK+.FONBF
	MOVEI	T1,USEJOB	;THIS IS THE EXTENDED LOOKUP BLOCK
	MOVEM	T1,CPJBLK+.FOLEB
	MOVEI	T1,.RBSIZ
	MOVEM	T1,USEJOB+.RBCNT
	SETZM	USEJOB+.RBPPN	;NO PPN SPECIFIED
	SETZM	USEJOB+.RBPRV	;CLEAR DATE/TIME WORD
	SETZM	USEJOB+.RBSIZ	;CLEAR OLD SIZE VALUE
	MOVE	T1,[SIXBIT /USEJOB/]
	MOVEM	T1,USEJOB+.RBNAM
	MOVSI	T1,'BIN'
	MOVEM	T1,USEJOB+.RBEXT
	MOVE	T1,[7,,CPJBLK]
	FILOP.	T1,
	$BOMB	<ACTCOB Cannot OPEN (^O/T1/) ^W/USEJOB+.RBNAM/.^W3/USEJOB+.RBEXT/>
	SKIPE	T1,CPJCHN	;WAS THERE A CHANNEL BEFORE?
	POPJ	P,		;YES. NO NEED TO STORE ONE
	MOVE	T1,CPJBLK+.FOFNC
	ANDX	T1,FO.CHN	;ISOLATE THE CHANNEL NUMBER
	MOVEM	T1,CPJCHN	;CPJCHN NOW CONTAINS CHANNEL # IN THE LEFT HALF
	POPJ	P,

;CPJCLS - ROUTINE TO CLOSE THE PRIMARY JOB CHECKPOINT FILE, USEJOB.BIN.
;	CPJCHN - CHANNEL NUMBER (ALREADY IN BIT POSITION)
;	CPJBLK - FILOP. BLOCK

CPJCLS:	MOVE	T1,CPJCHN	;YES.
	TXO	T1,FO.PRV	;ALLOW FULL FILE ACCESS TO ACT:
	HRRI	T1,.FOCLS	;NOW CLOSE THE FILE
	MOVEM	T1,CPJBLK+.FOFNC
	MOVE	T1,[1,,CPJBLK]
	FILOP.	T1,
	$BOMB	<ACTCCP Cannot close (^O/T1/) primary job checkpoint file>
	POPJ	P,
;CPDSAU - ROUTINE TO OPEN A DEVICE CHECKPOINT FILE FOR A JOB, JJJDEV.BIN,
;	WHERE JJJ IS THE JOB NUMBER, IN SINGLE ACCESS UPDATE MODE.
;	JOBNUM - JOB NUMBER
;	CPDCHN - CHANNEL NUMBER
;	CPDBLK - FILOP. BLOCK
;	JJJDEV - LOOPUP/ENTER BLOCK
;CPDDEL - ROUTINE TO DELETE THE DEVICE CHECKPOINT FILE, SAME CALL AS CPDSAU

CPDDEL:	MOVEI	T1,.RBSIZ	;NUMBER OF WORDS IN THE RENAME (DELETE) BLOCK
	MOVEM	T1,JJJDEL+.RBCNT ;SET COUNT (ALSO FLAG FOR DELETE)
	SETZM	JJJDEL+.RBNAM	;CLEAR THE NAME FOR DELETE
	SETZM	JJJDEL+.RBEXT	;AND THE EXTENSION FOR GOOD MEASURE
	SKIPA			;AND ENTER OPEN ROUTINE TO DO THE FILOP.
CPDSAU:	SETZM	JJJDEL+.RBCNT	;OPENING - NOT DELETING
	SKIPN	T1,CPDCHN	;DOES A CHANNEL ALREADY EXIST?
	TXO	T1,FO.ASC	;NO. HAVE THE MONITOR GIVE US ONE.
	TXO	T1,FO.PRV	;GIVE US FULL FILE ACCESS TO ACT:
	HRRI	T1,.FOSAU	;OPEN FOR UPDATE MODE
	SKIPE	JJJDEL+.RBCNT	;IS THIS DELETE
	HRRI	T1,.FODLT	;YES, USE DIFFERENT FUNCTION
	MOVEM	T1,CPDBLK+.FOFNC
	MOVEI	T1,.IODMP	;DUMP MODE
	TXO	T1,UU.RRC	;LET TONY KEEP THE RIB UP TO DATE
	MOVEM	T1,CPDBLK+.FOIOS
	HRLZ	T1,ACTDEV
	MOVEM	T1,CPDBLK+.FODEV
	SETZM	CPDBLK+.FOBRH
	SETZM	CPDBLK+.FONBF
	MOVEI	T1,JJJDEV	;THIS IS THE EXTENDED LOOKUP BLOCK
	SKIPE	JJJDEL+.RBCNT	;IS THIS DELETE
	HRLI	T1,JJJDEL	;THIS IS THE EXTENDED RENAME BLOCK
	MOVEM	T1,CPDBLK+.FOLEB
	SETZM	CPDBLK+.FOPAT	;NO PATH SPECIFIED
	MOVEI	T1,.RBSIZ
	MOVEM	T1,JJJDEV+.RBCNT
	SETZM	JJJDEV+.RBPPN	;NO PPN SPECIFIED
	SETZM	JJJDEV+.RBPRV	;CLEAR DATE/TIME WORD
	SETZM	JJJDEV+.RBSIZ	;CLEAR OLD SIZE VALUE
	MOVE	T1,[SIXBIT /SYSDEV/]
	MOVEM	T1,JJJDEV+.RBNAM
	MOVSI	T1,'BIN'
	MOVEM	T1,JJJDEV+.RBEXT
	SKIPN	JOBNUM		;HERE FOR SYSTEM SPINDLES
	JRST	CPDSA1		;YES, DON'T INSERT JOB NUMBER IN FILE NAME
	MOVE	T1,[POINT 6,JJJDEV+.RBNAM] ;POINT TO THE NAME FIELD
	MOVEM	T1,USGCRY	;SAVE BYTE POINTER
	$TEXT	(USGCRX,<^D3R0/JOBNUM/^0>) ;HAVE GLXLIB FILL IN THE JOB NUMBER
CPDSA1:	MOVE	T1,[7,,CPDBLK]
	FILOP.	T1,
	$BOMB	<ACTOPD Cannot OPEN (^O/T1/) ^W/JJJDEV+.RBNAM/.^W3/JJJDEV+.RBEXT/>
	SKIPE	T1,CPDCHN	;WAS THERE A CHANNEL BEFORE?
	POPJ	P,		;YES. NO NEED TO STORE ONE
	MOVE	T1,CPDBLK+.FOFNC
	ANDX	T1,FO.CHN	;ISOLATE THE CHANNEL NUMBER
	MOVEM	T1,CPDCHN	;CPDCHN NOW CONTAINS CHANNEL # IN THE LEFT HALF
	POPJ	P,


;CPDCLS - ROUTINE TO CLOSE A DEVICE CHECKPOINT FILE, JJJDEV.BIN.
;	CPDCHN - CHANNEL NUMBER (ALREADY IN BIT POSITION)
;	CPDBLK - FILOP. BLOCK

CPDCLS:	MOVE	T1,CPDCHN	;YES.
	TXO	T1,FO.PRV	;ALLOW FULL FILE ACCESS TO ACT:
	HRRI	T1,.FOCLS	;NOW CLOSE THE FILE
	MOVEM	T1,CPDBLK+.FOFNC
	MOVE	T1,[1,,CPDBLK]
	FILOP.	T1,
	$BOMB	<ACTCPD Cannot close (^O/T1/) device checkpoint file for job ^D/JOBNUM/>
	POPJ	P,
;PRJRED - ROUTINE TO OPEN THE ACCOUNT VALIDATION FILE, PROJCT.SYS, IN
;	READ MODE.
;	PRJCHN - CHANNEL NUMBER
;	PRJBLK - FILOP. BLOCK
;	PROJCT - LOOKUP/ENTER BLOCK

PRJRED:	SKIPN	T1,PRJCHN	;DOES A CHANNEL ALREADY EXIST?
	TXO	T1,FO.ASC	;NO. HAVE THE MONITOR GIVE US ONE.
	TXO	T1,FO.PRV	;GIVE US FULL FILE ACCESS TO SYS:
	HRRI	T1,.FORED	;MAKE SURE SYS:PROJCT.SYS EXISTS
	MOVEM	T1,PRJBLK+.FOFNC
	MOVEI	T1,.IODMP	;DUMP MODE
	MOVEM	T1,PRJBLK+.FOIOS
	HRLZ	T1,PRJDEV	;PROJCT.SYS NOW ON SYS:
	MOVEM	T1,PRJBLK+.FODEV
	SETZM	PRJBLK+.FOBRH
	SETZM	PRJBLK+.FONBF
	MOVEI	T1,PROJCT	;THIS IS THE EXTENDED LOOKUP BLOCK
	MOVEM	T1,PRJBLK+.FOLEB
	MOVEI	T1,35		; AND IS 35 WORDS LONG
	MOVEM	T1,PROJCT+.RBCNT
	MOVE	T1,[SIXBIT /PROJCT/]
	MOVEM	T1,PROJCT+.RBNAM
	MOVSI	T1,'SYS'
	MOVEM	T1,PROJCT+.RBEXT
	MOVE	T1,[7,,PRJBLK]
	FILOP.	T1,
	$BOMB	<ACTCOJ Cannot OPEN (^O/T1/) ^W/PROJCT+.RBNAM/.^W3/PROJCT+.RBEXT/>
	SKIPE	T1,PRJCHN	;WAS THERE A CHANNEL BEFORE?
	$RETT			;YES. NO NEED TO STORE ONE
	MOVE	T1,PRJBLK+.FOFNC
	ANDX	T1,FO.CHN	;ISOLATE THE CHANNEL NUMBER
	MOVEM	T1,PRJCHN	;PRJCHN NOW CONTAINS CHANNEL # IN THE LEFT HALF
	$RETT
;USGSAU - ROUTINE TO OPEN USAGE.OUT IN SINGLE-ACCESS UPDATE MODE USING DUMP MODE I/O.
;	USGIFN - GALAXY HANDLE FOR USAGE.OUT

USGSAU:	PUSHJ	P,.SAVE3	;SAVE P1 - P3
	SETZM	P3		;CLEAR COUNTER
USGSA1:	HRLZ	S1,ACTDEV	;GET THE DEVICE TO OPEN (ACT: OR DSK:)
	MOVEM	S1,USGFD+.FDSTR	;STORE IN FILE OPEN BLOCK
	MOVEI	S1,FOB.SZ	;FOB SIZE
	MOVEI	S2,USGFOB	;FOB ADDRESS
	PUSH	P,[EXP -1]	;STORAGE FOR IFN ON STACK
	$CALL	F%IOPN		;FIRST OPEN THE FILE FOR INPUT
	JUMPF	USGSA2		;FAILED--ASSUME IT DOESN'T EXIST
	MOVEM	S1,(P)		;SAVE IFN
	MOVEI	S2,FI.BSZ	;WANT TO READ BYTE SIZE
	$CALL	F%INFO		;DO IT
	JUMPF	USGSA3		;ASSUME IT'S NOT KNOWN
	CAIE	S1,^D7		;SOMETHING
	CAIN	S1,^D8		; REASONABLE?
	SKIPA			;YES

USGSA2:	MOVEI	S1,FILBSZ	;ELSE USE DEFAULT
	MOVEM	S1,USGBSZ	;SAVE AWAY
	STORE	S1,FAIFOB+FOB.CW,FB.BSZ ;SAVE IN FAILURE FILE FOB
	LSH	S1,^D24		;POSITION
	TLO	S1,440000	;MAKE A PARTIAL BYTE POINTER
	MOVEM	S1,USGBPT	;SAVE

USGSA3:	POP	P,S1		;GET IFN BACK
	SKIPL	S1		;SKIP IF CHANNEL NEVER OPENED
	$CALL	F%RREL		;CLOSE AND RELASE CHANNEL
	MOVEI	S1,FOB.SZ	;SIZE OF THE FOB
	MOVEI	S2,USGFOB	;WHERE IT IS
	$CALL	F%AOPN		;OPEN FILE IN APPEND MODE
	JUMPF	USGSA4		;GET COMPLAIN AND WAIT FOR A WHILE
	MOVEM	S1,USGIFN	;SAVE THE IFN FROM GALAXY
	MOVEI	S2,FI.SIZ	;ASK FOR THE FILE SIZE
	$CALL	F%INFO		;...
	MOVEM	S1,USGOSZ	;SAVE ORIGINAL FILE SIZE
	PUSHJ	P,OPNFAI	;GO OPEN VALIDATION FAILURE LOG FILE
	POPJ	P,		;NOW RETURN

USGSA4:	MOVE	P1,P3		;GET COUNTER
	IDIVI	P1,<^D60/SLPSEC>*WTOINT ;SEE IF TIME FOR A WTO
	SKIPN	P2		;NOT YET
	$WTOXX	(<Couldn't open usage file, ^F/USGFD/^M^JError: ^E/S1/>)
	MOVEI	S1,SLPSEC	;GET TIME TO SLEEP
	SLEEP	S1,		;ZZZZ
	AOJA	P3,USGSA1	;TRY TO OPEN THE FILE AGAIN

;USGAPP - ROUTINE TO APPEND AN ENTRY TO USAGE.OUT.  ENTRY MUST BE IN USGBUF.

USGAPP:	HRRZ	S2,SAVPTR	;HIGHEST BYTE FILLED IN THE ENTRY
	SUBI	S2,USGBUF-1	;COMPUTE LENGTH (WORDS) OF ENTRY
	CAILE	S2,USGBND-USGBUF ;CHECK FOR OVERFLOW
	STOPCD	(EBO,HALT,,<Entry buffer overflow>)
	HRLS	S2		;WANT LENGTH IN LH
	HRRI	S2,USGBUF	;WHERE IT IS
	MOVE	S1,USGIFN	;GALAXY HANDLE
	$CALL	F%OBUF		;OUTPUT THE ENTRY
	SKIPT			;EVERYTHING OK?
ACTATU:	STOPCD	(CAU,HALT,,<Cannot append to usage file>)
	MOVE	S1,USGIFN	;THE HANDLE AGAIN
	$CALL	F%CHKP		;MARK SURE DATA GETS TO FILE
	JUMPF	ACTATU		;GIVE UP IF DIDN'T WORK
	POPJ	P,		;AND RETURN
;USGCRN - ROUTINE TO CLOSE AND RENAME USAGE.OUT

USGCRN:	$CALL	.SAVE1		;SAVE A WORKING AC
	MOVE	S1,USGIFN	;THE HANDLE FOR THE FILE
	$CALL	F%REL		;CLOSE/RELEASE IT
	SKIPT			;DID THAT WORK
	STOPCD	(CCU,HALT,,<Cannot close usage file>)
	HRLZ	S1,ACTDEV	;GET DEVICE OPENED
	MOVEM	S1,USGRFD+.FDSTR ;FILL IN RENAME BLOCK
	DEVPPN	S1,		;FIND PPN ASSOCIATED WITH DEV
	$BOMB	<ACTCFO Cannot find owner of ^W/USGRFD+.FDSTR/>
	MOVEM	S1,USGRFD+.FDPPN ;STORE IT CAUSE GALAXY WANTS IT THERE
	PUSHJ	P,DATIM		;GET CURRENT DATE/TIME
	MOVE	T1,S1		;WANT IT IN T1
	PUSHJ	P,CNGDAT	;CONVERT TO DATE AND TIME
	PUSH	P,T2		;SAVE DATE FOR A SEC
	IDIV	T1,[^D3600000]	;T1 = HOURS PAST MIDNIGHT
	POP	P,T2		;RESTORE DATE
	IDIVI	T2,^D31*^D12	;T2 = YEARS AFTER 1964
	ADDI	T2,^D64		;CONVERT TO 2 DIGIT YEAR (WON'T WORK AFTER 21999)
	IDIVI	T3,^D31		;T3 = MONTH (ALMOST) T4 = DAY (ALMOST)
	AOS	T3		;NOW EXACT
	AOS	T4		;...
	MOVSI	P1,-^D10	;10 TRIES FOR UNIQUE DIGIT
USGCR1:	MOVE	S1,[POINT 6,USGRFD+.FDNAM]
	MOVEM	S1,USGCRY	;STUFF AWAY THE BYTE POINTER
	$TEXT	(USGCRX,<^D2R0/T2/^D2R0/T3/^D2R0/T4/^D2R0/T1/^D1R0/P1,RHMASK/^0>)
	MOVEI	S1,FRB.MZ	;SIZE OF THE RENAME BLOCK
	MOVEI	S2,USGFRB	;ADDRESS OF IT
	$CALL	F%REN		;RENAME USAGE.OUT TO "yymmdd.hhu"
	JUMPT	USGCR2		;IF OK, GO RENAME VALIDATION FAILURE FILE
	CAIN	S1,ERFAE$	;FILE ALREADY EXISTS ERROR
	AOBJN	P1,USGCR1	;YES, DO WE GET ANOTHER CHANCE

USGCR2:	PUSH	P,TF		;SAVE TF
	PUSHJ	P,RENFAI	;RENAME VALIDATION FAILURE LOG FILE
	POP	P,TF		;GET TF BACK
	POPJ	P,		;RETURN PREVIOUS FLAG

USGCRX:	CAIG	S1,"9"		;ONLY DIGITS
	CAIGE	S1,"0"		; IN FILE NAME
	$RETT			;IGNORE IT
	SUBI	S1," "-' '	;CONVERT TO SIXBIT
	IDPB	S1,USGCRY	;STORE CHARACTER IN FILE NAME
	$RETT			;AND RETURN
USGCRY:	BLOCK	1		;PLACE TO HOLD THE POINTER
SUBTTL	ROUTINES TO OPEN, CLOSE AND RENAME VALIDATION FAILURE LOG FILE

;Note: The validation failure log file is opened and renamed at the same
;      time the usage file is opened and renamed. However, if the validation
;      log file can't be opened or renamed, it's not fatal. If the file can't
;      be opened, logging is not done. If the file can't be renamed, the
;      current file open will be appended to if possible.

;OPNFAI - Open validation failure log file, ACT:FAILUR.LOG, or DSK:FAILUR.LOG
;	if debugging. ACTDEV should already be set.

OPNFAI:	SKIPE	FAIIFN		;IFN ALREADY?
	 $RETT			;YES, JUST RETURN
	HRLZ	S1,ACTDEV	;GET THE DEVICE (ACT: OR DSK:)
	MOVEM	S1,FAIFD+.FDSTR	;STORE IT
	MOVEI	S1,FOB.SZ	;GET SIZE OF THE FOB
	MOVEI	S2,FAIFOB	;AND ITS ADDRESS
	$CALL	F%AOPN		;OPEN THE FILE IN APPEND MODE
	JUMPF	OPNF.1		;IF IT FAILED GO COMPLAIN
	MOVEM	S1,FAIIFN	;SAVE THE IFN
	$RETT			;RETURN SUCCESS

OPNF.1:	$WTOXX	(<Validation failure file open error, ^F/FAIFD/^M^JError: ^E/S1/>)
	SETZM	FAIIFN		;MAKE SURE WE DON'T THINK FILE IS OPEN
	$RETT

;RENFAI - Rename the validation failure log file to FAILUR.nnn where nnn
;	is 000 - 999. If FAILUR.000 exists, then FAILUR.001 is used, etc.

RENFAI:	SKIPN	S1,FAIIFN	;FILE OPEN?
	$RETT			;NO, NOTHING TO DO
	$CALL	F%REL		;CLOSE THE FILE
	JUMPF	RENF.1		;IF WE COULDN'T, GO COMPLAIN
	SETZM	FAIIFN		;CLEAR THE IFN WORD
	HRLZ	S1,ACTDEV	;GET THE DEVICE FILE IS OPENED ON
	MOVEM	S1,FAIRFD+.FDSTR ;STORE IT
	DEVPPN	S1,		;GET THE PPN ASSOCIATED
	 MOVE	S1,[1,,7]	;SHOULD NOT HAPPEN
	MOVEM	S1,FAIRFD+.FDPPN ;STORE IT
	PUSHJ	P,.SAVET	;GET SOME SCRATCH ACS
	SETZB	T1,FAIRFD+.FDEXT ;START WITH 000
RENF.0:	CAIL	T1,^D1000	;LESS THAN MAX?
	JRST	RENF.2		;NO, PHEW!
	MOVE	T4,[POINT 6,FAIRFD+.FDEXT] ;GET BP FOR EXTENSION
	PUSH	P,T1		;SAVE COUNT
	IDIVI	T1,^D100	;GET 1ST DIGIT IN T1
	ADDI	T1,'  0'	;MAKE SIXBIT
	IDIVI	T2,^D10		;GET 2ND AND 3RD IN T2 AND T3 RESPECTIVELY
	ADDI	T2,'  0'
	ADDI	T3,'  0'
	IDPB	T1,T4
	IDPB	T2,T4
	IDPB	T3,T4		;STORE THE EXTENSION
	POP	P,T1		;GET COUNT BACK
	MOVEI	S1,FRB.SZ	;GET SIZE OF RENAME BLOCK
	MOVEI	S2,FAIFRB	;GET ADDRESS OF RENAME BLOCK
	$CALL	F%REN		;TRY THE RENAME
	$RETIT			;RETURN IF SUCCESS
	CAIN	S1,ERFAE$	;FILE ALREADY THERE?
	AOJA	T1,RENF.0	;YES, TRY NEXT EXTENSION
	$WTOXX	(<Couldn't rename validation failure file, ^F/FAIFD/^M^JError: ^E/S1/>)
	PJRST	OPNFAI		;GO OPEN OLD ONE IF WE CAN

RENF.1:	$WTOXX	(<Couldn't close validation failure file, ^F/FAIFD/^M^JError: ^E/S1/>)
	$RETT			;JUST RETURN AFTER COMPLAINING

RENF.2:	$WTOXX	(<Too many validation failure files^M^JWill continue to write to ^F/FAIFD/>)
	PJRST	OPNFAI		;TRY TO OPEN FAILUR.LOG AGAIN
;LOGFAI - Routine to stuff characters in the buffer for the validation
;	failure log file. This routine is setup to be called by $TEXT macros,
;	i.e. S1 contains char to be logged.

LOGFAI:	SKIPE	FAIPAG		;IS THE LOG PAGE THERE?
	JRST	LOGF.1		;YES, GO DEPOSIT TEXT
	PUSH	P,S1		;NO, SAVE CHAR
	PUSHJ	P,FBFINI	;GET INTIAILIZE PAGE AND BP
	POP	P,S1		;GET CHAR BACK
LOGF.1:	SOSGE	FAICNT		;ROOM LEFT IN BUFFER?
	JRST	LOGF.2		;NO, GO DUMP IT AND CONTINUE
	IDPB	S1,FAIPTR	;YES, STORE CHAR AND EXIT
	POPJ	P,		;RETURN
LOGF.2:	PUSH	P,S1		;SAVE CHAR
	PUSHJ	P,FAIOUT	;DUMP BUFFER
	POP	P,S1		;GET CHAR BACK
	JRST	LOGF.1		;AND PUT IT IN BUFFER

;FBFINI - Routine to get a buffer page for validation failure file and
;	setup byte pointer and char count.

FBFINI:	SKIPE	S1,FAIPAG	;HAVE A PAGE?
	JRST	FBFI.1		;YES, JUST DO BP AND COUNT
	$CALL	M%GPAG		;GET A PAGE
	JUMPF	FBFI.2		;CAN'T GO COMPLAIN
	MOVEM	S1,FAIPAG	;SAVE ADDRESS
FBFI.1:	HLL	S1,USGBPT	;FORM A BYTE POINTER
	MOVEM	S1,FAIPTR	;SAVE IT
	MOVEI	S1,^D36		;BITS PER WORD
	LDB	S2,[POINT 6,USGBPT,11] ;GET CURRENT BYTE SIZE
	IDIVI	S1,(S2)		;GET BYTES PER WORD
	IMULI	S1,PAGSIZ	;AND BYTES PER BUFFER
	MOVEM	S1,FAICNT	;SAVE
	MOVEM	S1,FAIMAX	;SAVE AS MAXIMUM TOO
	POPJ	P,		;RETURN

FBFI.2:	$RETF			;FOR NOW

;FAIOUT - Routine to write whatever is in the buffer to the validation
;	failure log file and checkpoint the file.

FAIOUT:	SKIPE	S1,FAIIFN	;GET THE IFN
	SKIPN	S2,FAIPAG	;GET THE BUFFER ADDRESS
	$RETF			;BOTH HAVE TO BE THERE TO WORK
	MOVN	TF,FAICNT	;GET NEGATIVE FREE CHAR COUNT
	ADD	TF,FAIMAX	;COMPUTE HOW MANY IN BUFFER
	JUMPE	TF,.RETT	;NO BYTES IN BUFFER, JUST RETURN
	HRL	S2,TF		;GET COUNT,,ADDRESS IN S2
	$CALL	F%OBUF		;OUTPUT BUFFER
	JUMPF	FAIO.1		;GO COMPLAIN IF WE CAN'T
	PUSHJ	P,FBFINI	;GET REINIT COUNT AND BP
	MOVE	S1,FAIIFN	;GET IFN AGAIN
	$CALL	F%CHKP		;CHECKPOINT THE FILE
	$RETIT			;RETURN IF OK
	$WTOXX	(<Couldn't checkpoint validation failure file, ^F/FAIFD/^M^JError: ^E/S1/>)
	$RETF

FAIO.1:	$WTOXX	(<Couldn't output to validation failure file, ^F/FAIFD/^M^JError: ^E/S1/>)
	$RETF
SUBTTL	FILE PARAMETER BLOCKS FOR GALAXY INTERFACE

;Usage file paramter blocks
USGFOB:	$BUILD	(FOB.SZ)		;BUILD THE FILE OPEN BLOCK
	  $SET	(FOB.FD,,USGFD)		;POINT TO THE FD
	  $SET	(FOB.CW,FB.BSZ,^D36)	;USE FULL WORD MODE
	  $SET	(FOB.AB,,USGFAB)	;POINT TO THE FAB
	$EOB

USGFAB:	$BUILD	(7)			;FILE ATTRIBUTE BLOCK FOR PROT CODE
	  $SET	(0,,7)			;SIZE OF ENTIRE BLOCK
	  $SET	(1,FI.IMM,1)		;IMMEDIATE ARGUMENT
	  $SET	(1,FI.LEN,1)		;LENGTH OF ARGUMENT
	  $SET	(1,FI.ATR,.FIPRO)	;PROTECTION CODE
	  $SET	(2,,<EXP FILPRO>)	;PROTECTION CODE DEFINED IN ACTSYM
	  $SET	(3,FI.LEN,1)		;LENGTH IS ONE WORD
	  $SET	(3,FI.ATR,.FIBSZ)	;THE LOGICAL DATA BYTE SIZE
	  $SET	(4,,USGBSZ)		;DEFINED VALUE
	  $SET	(5,FI.IMM,1)		;IMMEDIATE ARGUMENT
	  $SET	(5,FI.LEN,1)		;LENGTH IS ONE WORD
	  $SET	(5,FI.ATR,.FIDTY)	;THE DATA TYPE
	  $SET	(6,,.RBDAS)		;ASCII
	$EOB

USGFD:	$BUILD	(FDMSIZ)		;BUILD THE FILE DESCRIPTOR BLOCK
	  $SET	(.FDLEN,FD.LEN,FDMSIZ)	;FILL IN THE LENGTH
	  $SET	(.FDLEN,FD.TYP,.FDNAT)	;NATIVE MODE FILE SPEC
	  $SET	(.FDNAM,,<SIXBIT/USAGE/>) ;THE FILE NAME
	  $SET	(.FDEXT,,<SIXBIT/OUT/>)	;THE EXTENSION
	$EOB

USGRFD:	$BUILD	(FDMSIZ)		;BUILD A FILE DESCRIPTOR BLOCK FOR RENAME
	  $SET	(.FDLEN,FD.LEN,FDMSIZ)	;FILL IN THE LENGTH
	  $SET	(.FDLEN,FD.TYP,.FDNAT)	;NATIVE MODE FILE SPEC
	$EOB

USGFRB:	$BUILD	(FRB.MZ)		;BUILD A FILE RENAME BLOCK
	  $SET	(FRB.SF,,USGFD)		;POINTER TO SOURCE FILE
	  $SET	(FRB.DF,,USGRFD)	;DESTINATION FILE
	$EOB

;Validation failure file parameter blocks
FAIFOB:	$BUILD	(FOB.SZ)		;BUILD THE FILE OPEN BLOCK
	  $SET	(FOB.FD,,FAIFD)		;POINT TO THE FD
	  $SET	(FOB.CW,FB.BSZ,0)	;FILLED IN AT RUNTIME
	  $SET	(FOB.AB,,USGFAB)	;FILE ATTRIBUTE BLOCK
	$EOB

FAIFD:	$BUILD	(FDMSIZ)		;BUILD THE FILE DESCRIPTOR BLOCK
	  $SET	(.FDLEN,FD.LEN,FDMSIZ)	;FILL IN THE LENGTH
	  $SET	(.FDLEN,FD.TYP,.FDNAT)	;NATIVE MODE FILE SPEC
	  $SET	(.FDNAM,,<SIXBIT/FAILUR/>) ;THE FILE NAME
	  $SET	(.FDEXT,,<SIXBIT/LOG/>)	;THE EXTENSION
	$EOB

FAIRFD:	$BUILD	(FDMSIZ)		;BUILD A FILE DESCRIPTOR BLOCK FOR RENAME
	  $SET	(.FDLEN,FD.LEN,FDMSIZ)	;FILL IN THE LENGTH
	  $SET	(.FDLEN,FD.TYP,.FDNAT)	;NATIVE MODE FILE SPEC
	  $SET	(.FDNAM,,<SIXBIT/FAILUR/>) ;THE FILE NAME
	$EOB

FAIFRB:	$BUILD	(FRB.SZ)		;BUILD A FILE RENAME BLOCK
	  $SET	(FRB.SF,,FAIFD)		;POINTER TO SOURCE FILE
	  $SET	(FRB.DF,,FAIRFD)	;DESTINATION FILE
	  $SET	(FRB.AB,,USGFAB)	;ATTRIBUTE BLOCK
	$EOB
SUBTTL	ERRACK - ROUTINE TO SEND ERROR ACKS TO USERS

	EXTERN	ERRPFX,	ERRTXT

ERRACK:	SKIPE	GFRFLG		;WAS THE MESSAGE FROM [SYSTEM]GOPHER?
	PJRST	ERRAC1		;YES. RETURN MESSAGE IS A DIFFERENT FORMAT
	MOVE	T1,SABADR	;GET THE PAGE ADDRESS
	MOVEI	T2,UGFAL$	;INDICATE THE VALIDATION MODULE HAS AN ERROR
	MOVEM	T2,UC$RES(T1)	;STORE IT IN THE IPCF SEND MESSAGE
	ADDI	T1,UC$ERR	;RELOCATE T1 TO POINT TO WHERE ERROR GOES
	MOVEM	T1,ACKETX	;SAVE ADDRESS
	PUSHJ	P,ERRAC4	;SETUP ACK STUFF
	$TEXT	(<-1,,@ACKETX>,<^I/@ACKITX/^0>)
	POPJ	P,		;RETURN

ERRAC1:	MOVE	T1,SABADR	;GET THE PAGE ADDRESS
	MOVEI	T2,1		;ONE BLOCK FOLLOWING
	STORE	T2,.OARGC(T1)	;STORE AS NUMBER OF BLOCKS
	SETZM	.OFLAG(T1)	;NO FLAGS HAVE BEEN DEFINED YET
	ADDI	T1,.OHDRS+1	;ERROR MESSAGE STARTS HERE
	MOVEM	T1,ACKETX	;SAVE ADDRESS
	PUSHJ	P,ERRAC4	;SET UP ACK STUFF
	$TEXT	(<-1,,@ACKETX>,<^I/@ACKITX/^0>)
	MOVEI	T1,2		;MIN IS HEADER WORD + A NULL FOR STRING
	SKIPE	ACKEFL		;NEW-STYLE ACK?
	SOSA	T2,ACKETX	;YES--ADJUST ADDRESS
	MOVE	T2,ACKETX	;GET TEXT ADDRESS BACK
	MOVE	T3,ERRICH##	;GET INITIAL CHARACTER
	CAIE	T3,"?"		;FATAL ERROR?
	TDZA	T3,T3		;NO
	MOVX	T3,MF.FAT	;LITE THE BIT

ERRAC2:	SKIPN	(T2)		;HAVE WE REACHED THE END OF THE ERROR MESSAGE?
	JRST	ERRAC3		;YES. STORE THE COUNTS IN THE PROPER PLACES
	AOS	T2		;LOOK AT THE NEXT WORD
	AOJA	T1,ERRAC2	;COUNT IT

ERRAC3:	MOVE	T2,SABADR	;GET THE BEGINNING ADDRESS AGAIN
	MOVEM	T3,.MSFLG(T2)	;STORE FLAGS IN THE MESSAGE
	LOAD	T3,.MSTYP(T2),MS.CNT ;GET THE MESSAGE COUNT
	ADD	T3,T1		;ADD IN THE ERROR MESSAGE LENGTH
	STORE	T3,.MSTYP(T2),MS.CNT ;JUST COUNT WHAT WAS ADDED IN THIS ROUTINE
	HRLM	T1,.OHDRS(T2)	;WORD COUNT OF THE ERROR BLOCK
	MOVEI	T1,.CMTXT	;TYPE OF RESPONSE BLOCK
	HRRM	T1,.OHDRS(T2)	;SAVE IN MESSAGE
	JRST	ERRAC5		;GO RESTORE ACS AND RETURN

; SET UP FOR NEW-STYLE ACKS IF NECESSARY
ERRAC4:	MOVE	T1,ERRPFX##	;GET SIXBIT PREFIX
	HRL	T1,ERRDAT##	;AND ERROR CODE
	MOVEM	T1,@ACKETX	;SAVE IN FIRST WORD
	SKIPE	ACKEFL		;WANT NEW-STYLE ACKS?
	AOSA	ACKETX		;YES--ADVANCE POINTER TO NEXT WORD
	SKIPA	T1,[[ITEXT (<^W/ERRPFX/ ^I/@ERRTXT/^0>)]]
	MOVEI	T1,[ITEXT (<^I/@ERRTXT/^0>)]
	MOVEM	T1,ACKITX	;SAVE

ERRAC5:	DMOVE	T1,ERRACS##+0	;RESTORE T1 AND T2
	DMOVE	T3,ERRACS##+2	;RESTORE T3 AND T4
	POPJ	P,		;RETURN
SUBTTL	ERRPRO - OLD USER ERROR ROUTINES


;ERROR CODES

ERCODE	ERROR0,ACNPP%		;(0) NONEXISTENT PPN
ERCODE	ERROR1,ACIVA%		;(1) OBSOLETE
ERCODE	ERROR2,ACILP%		;(2) ILLEGAL PPN
ERCODE	ERROR3,ACJNP%		;(3) JOB NOT PRIVILEGED
ERCODE	ERROR4,ACJCE%		;(4) JOB CAPACITY EXCEEDED

;ERRPRO - ROUTINE TO BE CALLED WHEN AN ERROR OCCURS.  THIS ROUTINE WILL
;	STORE THE ERROR MESSAGE IN AN IPCF MESSAGE. T1 CONTAINS THE ERROR NUMBER.

ERRPRO:	SKIPE	GFRFLG		;WAS THE MESSAGE FROM [SYSTEM]GOPHER?
	PJRST	ERRQUE		;YES. RETURN MESSAGE IS A DIFFERENT FORMAT
	MOVE	T2,SABADR	;GET THE PAGE ADDRESS
	MOVEI	T3,UGFAL$	;INDICATE THE VALIDATION MODULE HAS AN ERROR
	MOVEM	T3,UC$RES(T2)	;STORE IT IN THE IPCF SEND MESSAGE
	ADDI	T2,UC$ERR	;RELOCATE T2 TO POINT TO WHERE ERROR GOES
	PUSHJ	P,@ERRDSP(T1)	;PUT THE MESSAGE INTO THE IPCF PAGE
	MOVEI	T3,UGFAL$^!UGTRU$ ;VALUE TO CONVERT FAILURE TO SUCCESS
	SKIPF			;IF APPROPRIATE,
	XORM	T3,UC$RES-UC$ERR(T2) ;DO SO
	$RETF
;ERRQUE - ROUTINE THAT WILL SET UP AN ERROR MESSAGE IN QUEUE. UUO FORMAT
;
;CALL:	T1/DISPATCH IN ERRDSP

ERRQUE:	MOVE	T2,SABADR	;GET THE PAGE ADDRESS
	MOVEI	T3,1		;ONE BLOCK FOLLOWING
	STORE	T3,.OARGC(T2)	;STORE AS NUMBER OF BLOCKS
	SETZM	.OFLAG(T2)	;NO FLAGS HAVE BEEN DEFINED YET
	ADDI	T2,.OHDRS+1	;ERROR MESSAGE STARTS HERE
	PUSHJ	P,@ERRDSP(T1)	;PUT THE RIGHT MESSAGE IN
	$RETIF			;FATAL HAS ALREADY BEEN SET UP
	SETZ	T3,		;NO FLAGS
	ADDI	T1,1		;INCLUDE HEADER WORD IN LENGTH OF RESPONSE
ERRQU2:	MOVE	T2,SABADR	;GET THE BEGINNING ADDRESS AGAIN
	MOVEM	T3,.MSFLG(T2)	;STORE FLAGS IN THE MESSAGE
	LOAD	T3,.MSTYP(T2),MS.CNT	;GET THE MESSAGE COUNT
	ADD	T3,T1		;ADD IN THE ERROR MESSAGE LENGTH
	STORE	T3,.MSTYP(T2),MS.CNT	;JUST COUNT WHAT WAS ADDED IN THIS ROUTINE
	HRLM	T1,.OHDRS(T2)	;WORD COUNT OF THE ERROR BLOCK
	MOVEI	T1,.CMTXT	;TYPE OF RESPONSE BLOCK
	HRRM	T1,.OHDRS(T2)
	$RETF			;ALL DONE
;ERROR DISPATCH TABLE FOR PUTTING ERROR MESSAGES IN IPCF PAGE

	ERRMAP			;(-4) MOVE MAPPING DATA
	ERRMVB			;(-3) MOVE VALIDATION BLOCK
	ERRMUP			;(-2) MOVE USER PROFILE
	ERRMAS			;(-1) MOVE ACCOUNT STRING TO RESPONSE BLOCK
ERRDSP:	ERRFOO			;(0) OBSOLETE
	ERRFOO			;(1) OBSOLETE
	ERRILP			;(2) ILLEGAL PPN
	ERRJNP			;(3) JOB NOT PRIVILEGED
ERRFOO:	HALT	.

ERRMAP:	HRLZ	T3,ACOMAP	;POINT TO MAPPING BLOCKS
	HRRI	T3,(T2)		;WHERE IT GOES IN THE RESPONSE BLOCK
	MOVE	T1,TMPCNT	;GET BLOCK COUNT
	IMULI	T1,UU$LEN	;LENGTH OF RESPONSE DATA
	ADDI	T1,(T2)		;COMPUTE END OF BLT
	BLT	T3,-1(T1)	;COPY DATA
	SUBI	T1,(T2)		;GET LENGTH AGAIN
	$RETT			;RETURN

ERRMVB:	HRLZ	T1,DATADR	;POINT TO OUR VALIDATION BLOCK
	HRRI	T1,(T2)		;WHERE IT GOES IN THE RESPONSE BLOCK
	BLT	T1,UV$ACE(T2)	;COPY IT
	MOVEI	T1,UV$ACE+1	;GET LENGTH
	$RETT			;AND RETURN

ERRMUP:	PUSH	P,T3		;SAVE T3
	HRLI	T1,ACOPRO	;POINT TO THE PROFILE
	HRRI	T1,(T2)		;WHERE IT GOES IN RESPONSE BLOCK
	HRRZ	T3,ACOPRO+.AEVRS ;GET LENGTH OF THIS PROFILE
	ADDI	T3,(T2)		;COMPUTE END OF BLT
	BLT	T1,-1(T3)	;COPY
	HRRZ	T1,ACOPRO+.AEVRS ;GET LENGTH OF BLOCK WE'RE RETURING
	POP	P,T3		;RESTORE T3
	$RETT			;GOOD RETURN

ERRMAS:	MOVE	T1,DATADR	;WHERE VALIDATION REQUEST MESSAGE LIVES
	HRLI	T1,UV$ACT(T1)	;ORIGINAL (OR MODIFIED) ACCOUNT STRING
	HRRI	T1,(T2)		;WHERE TO PUT IT IN ASSEMBLED MESSAGE
	BLT	T1,7(T2)	;MOVE THE ACCOUNT STRING
	MOVEI	T1,10		;NUMBER OF WORDS FOR RESPONSE BLOCK
	$RETT			;THIS IS NOT REALLY AN ERROR ROUTINE

ERRILP:	FATAL	(ILP,<Illegal ppn ^U/PPN/>,ACILP%,.RETF)

ERRJNP:	PUSHJ	P,LOGUSR	;GET USER INDEPENDENT INFO
	MOVEI	S1,.CHCRT	;JUST APPEND A <CR><LF>
	PUSHJ	P,LOGFAI
	MOVEI	S1,.CHLFD
	PUSHJ	P,LOGFAI
	PUSHJ	P,FAIOUT	;JOB NUMBER ALREADY LOGGED, WRITE FAILURE FILE
	FATAL	(JNP,<Job not privileged>,ACJNP%,.RETF)
;LOGUSR - Routine to get a lot of info about current IPCF sender (user)
;	and log it in the validation failure log file.
;	MDB pointed to by MDBADR is assumed valid.
;	Call with T1=error code

LOGUSR:	PUSHJ	P,.SAVET	;SAVE T1 - T4
	MOVE	S1,[XWD USRZER,USRZER+1] ;ZERO OUR STORAGE
	SETZM	USRZER
	BLT	S1,USRZEN
	MOVEM	T1,USRERR	;SAVE ERROR CODE
	MOVE	S1,MDBADR	;GET MDB ADDRESS
	LOAD 	S1,MDB.PV(S1),MD.PJB ;GET JOB NUMBER OF SENDER
	JUMPE	S1,.RETF	;HMMMM
	MOVEM	S1,USRJOB	;SAVE IT
	HRLZS	S1,S1		;GET JOB NUMBER IN LH
	HRRI	S1,.GTPPN	;GET TABLE NUMBER
	GETTAB	S1,		;GET PPN OF THE GUY DOING IT
	 TRNA			;USE ZEROS
	MOVEM	S1,USRPPN	;STORE IT
	HRLZ	S1,USRJOB	;GET JOB NUMBER AGAIN
	HRRI	S1,.GTPRG	;GET TABLE NUMBER
	GETTAB	S1,		;GET PROGRAM NAME
	 TRNA			;USER BLANKS
	MOVEM	S1,USRPRG	;SAVE IT
	HRLZ	S1,USRJOB	;GET JOB NUMBER
	HRRI	S1,.GTNM1	;GET 1ST WORD OF USERNAME
	GETTAB	S1,
	 TRNA			;USE BLANKS
	MOVEM	S1,USRNAM	;SAVE IT
	HRLZ	S1,USRJOB	;GET JOB NUMBER AGAIN
	HRRI	S1,.GTNM2	;GET 2ND WORD OF USER NAME
	GETTAB	S1,
	 TRNA
	MOVEM	S1,USRNAM+1	;SAVE IT
	HRRZ	S1,USRJOB	;GET JOB NUMBER AGAIN
	TRMNO.	S1,		;GET THE UDX
	 TRNA
	MOVEM	S1,USRUDX	;STORE
	JUMPE	S1,LOGU.1	;IF ERROR, SKIP OTHER THINGS BASED ON UDX
	DEVNAM	S1,		;GET TTY NAME
	 SETZM	S1		;ZERO WILL BE SIXBIT BLANKS
	MOVEM	S1,USRTTY	;SAVE IT
	MOVE	S2,USRUDX	;GET UDX AGAIN
	MOVEI	S1,.TOAPC	;WANT ASYNCH PORT CHARACTERISTIC
	MOVE	TF,[XWD 2,S1]	;GET LENGTH,,ADDR
	TRMOP.	TF,		;GET THE APC CODE
	 TRNA			;ASSUME UNKNOWN
	MOVEM	TF,USRAPC
;Here to get node and line information on the offender
	MOVE	S1,[7,,.NOGDI]	;FUNCTION TO GET TTY INFO
	MOVEM	S1,USRARG	;FROM NETOP. UUO (7.03 AND LATER)
	XMOVEI	S1,USRNDN	;SET UP POINTER TO STRING BLOCK
	MOVEM	S1,USRNDA
	XMOVEI	S1,USRLIN	;POINT TO LINE NAME BLOCK
	MOVEM	S1,USRLNA
	MOVEI	S1,<<^D16/4>+1>	;SIZE OF THOSE STRING BLOCKS
	MOVEM	S1,USRLIN
	MOVEM	S1,USRNDN
	XMOVEI	S1,USRARG
	NETOP.	S1,		;ASK FOR TTY INFO
	  JRST	LOGU.0		;DO IT THE OLD-FASHIONED WAY
	MOVEI	S1,USRNDN	;CRAM INTO 7 BIT
	PUSHJ	P,C8TO7
	MOVEI	S1,USRLIN	;SAME FOR LINE NAME
	PUSHJ	P,C8TO7
	JRST	LOGU.1		;SKIP THE OLD-FASHIONED WAY
LOGU.0:	MOVE	S1,USRUDX	;GET UDX AGAIN
	GTNTN.	S1,		;GET NODE,,LINE NUMBER
	 SETZM	S1
	HLRZM	S1,USRNOD	;SAVE NODE NUMBER
	HRRZS	S1		;ISOLATE LINE NUMBER
	MOVE	S2,[ASCII /TTY/]	;CONSTRUCT LINE NAME
	MOVEM	S2,USRLIN
	MOVE	T1,[POINT 7,USRLIN,20]	;POINT TO START OF NUMBER PART
	PUSHJ	P,COTO7		;AND STUFF IT IN
	MOVE	S2,USRNOD	;GET NODE NUMBER IN RH OF S2
	MOVEI	S1,2		;GET LEGNTH FOR UUO
	MOVE	TF,[XWD .NDRNN,S1] ;GET ARG FOR NODE. UUO
	NODE.	TF,		;GET NODE NAME
	 TRNA			;USE BLANKS
	MOVEM	TF,S1		;STORE NODE NAME IN SIXBIT
	MOVE	T1,[POINT 7,USRNDN]	;POINT TO NODE NAME STORAGE
	PUSHJ	P,C6TO7		;AND STORE IT IN ASCII
;Here to log the data
LOGU.1:	PUSHJ	P,.SAVE1	;SAVE P1
	$TEXT	(LOGFAI,<^O2R0/USRERR/>^A) ;DUMP ERROR CODE
	MOVE	P1,FAIPTR	;TRICK ACTDAE'S USAGE OUTPUT ROUTINES
	EXCH	P1,USGPTR	;MAKE THEM WRITE IN OUR BUFFER
	$CALL	I%NOW		;GET CURRENT UDT
	MOVEI	T1,S1		;POINT T1 AT ARG
	PUSHJ	P,OUTDTM	;OUTPUT DATE/TIME AS "YYYYMMDDHHMMSS"
	EXCH	P1,USGPTR	;PUT USAGE POINTER BACK AND GET OURS
	MOVEM	P1,FAIPTR	;STORE IT FOR LOGFAI
	MOVE	S1,FAICNT	;UPDATE OUR BUFFER COUNT
	SUBI	S1,^D14
	MOVEM	S1,FAICNT
;Log the rest
	$TEXT	(LOGFAI,<^D3R0/USRJOB/^O6R0/USRPPN,LHMASK/^O6R0/USRPPN,RHMASK/^W6L/USRNAM/^W6L/USRNAM+1/^W6L/USRPRG/^W6L/USRTTY/^T20L/USRNDN/^T20L/USRLIN/^O2R0/USRAPC/^A>)
	$RETT
SUBTTL	Convert the user's node and line names to 7 bit ASCII

;C8TO7 - Convert 8 bit string in string block into 7 bit ASCIZ
;	in place
; Call:	MOVEI	S1,address of string block
;	PUSHJ	P,C8TO7

C8TO7:	$SAVE <<S1+2>,<S1+3>,<S1+4>,<S1+5>>	;SAVE ACS USED IN MOVSLJ
	MOVE	S2,S1		;GET ADDRESS OF BLOCK
	HRLI	S2,041000	;MAKE ILDB BYTE POINTER TO NEXT WORD
	MOVE	S1+4,S1		;AND MAKE DESTINATION POINTER TOO
	HRLI	S1+4,(POINT 7,)	;TO OVERWRITE THE BLOCK
	HLRZ	S1,(S1)		;GET NUMBER OF SOURCE BYTES
	MOVEI	S1+3,1(S1)	;ONE EXTRA DESTINATION BYTE FOR 0 FILL
	EXTEND	S1,[MOVSLJ 0
			   0]	;MOVE THE SLUDGE WITH A 0 TERMINATOR
	JFCL
	POPJ	P,

;C6TO7 - CONVERT SIXBIT NAME IN S1 TO ASCIZ STRING
;CALL:	MOVE	T1,[BYTE POINTER]
;	MOVE	S1,SIXBIT
;	PUSHJ	P,C6TO7

C6TO7:	SETZ	S2,
	ROTC	S1,6		;GET A CHARACTER IN S2
	ADDI	S2,40		;CONVERT TO ASCII
	IDPB	S2,T1		;STUFF IT
	JUMPN	S1,C6TO7	;AND CONTINUE IF NOT DONE
	IDPB	S1,T1		;TERMINATE WITH A 0 BYTE (S1 CONTAINS 0)
	POPJ	P,

;COTO7 - CONVERT OCTAL NUMBER IN S1 TO ASCII STRING
;CALL:	MOVE	T1,[BYTE POINTER]
;	MOVE	S1,NUMBER
;	PUSHJ	P,COTO7

COTO7:	IDIVI	S1,^D8
	PUSH	P,S2		;SAVE REMAINDER
	SKIPE	S1		;CONTINUE IF NOT DONE
	PUSHJ	P,COTO7		;RECURSE
	POP	P,S1		;GET A DIGIT
	ADDI	S1,"0"		;CONVERT TO OCTAL
	IDPB	S1,T1		;STUFF IT
	POPJ	P,


SUBTTL	Account string synonyms -- Initialize file


; Initialize ACT:SYN.ACT
; Call:	PUSHJ	P,SYNFIL

SYNFIL:	SKIPN	SYNFLG		;WANT SYNONYMS?
	POPJ	P,		;NOPE
	MOVEI	S1,FOB.MZ	;FOB SIZE
	MOVEI	S2,SYNFOB	;FOB ADDRESS
	$CALL	F%IOPN		;OPEN THE FILE
	JUMPT	SYNFI1		;JUMP IF NO ERRORS
	$WTOXX	(<^E/[-1]/; ^F/SYNFD/>) ;REPORT ERROR
	SETZM	SYNFLG		;CAN'T DO SYNONYMS
	POPJ	P,		;RETURN

SYNFI1:	MOVEM	S1,SYNIFN	;SAVE IFN
	MOVEI	S1,^D100	;START OFF WITH 100 ENTRIES
	$CALL	M%GMEM		;GET CORE
	MOVEM	S1,(S2)		;SAVE FOR S%TBLK
	MOVEM	S2,SYNTAB	;SAVE TABLE ADDRESS
	SETZM	SYNLIN		;INIT LINE NUMBER

SYNFI2:	PUSHJ	P,SYNRED	;READ A SYNONYM
	JUMPF	SYNFI6		;JUMP IF EOF

SYNFI3:	MOVE	S1,SYNTAB	;GET TABLE POINTER
	MOVE	S2,SYNARG	;AND ARGUMENT
	$CALL	S%TBAD		;ADD TO TABLE
	JUMPT	SYNFI2		;LOOP IF OK
	CAIE	S1,EREIT$	;ALREADY IN TABLE?
	JRST	SYNFI4		;NO
	HLRZ	S1,SYNARG	;GET DUPLICATE NAME ADDR
	$WTOXX	(<Duplicate synosym "^T/(S1)/" ignored>)
	JRST	SYNFI2		;ON TO THE NEXT ONE
SYNFI4:	CAIE	S1,ERTBF$	;TABLE FULL?
	JRST	SYNFI5		;NO
	MOVE	TF,SYNTAB	;GET ADDRESS OF TABLE
	HRRZ	TF,@TF		;GET LENGTH
	MOVE	S2,TF		;MAKE A COPY
	LSH	TF,-1		;DIVIDE BY TWO
	MOVE	S1,TF		;GET RESULT
	ADDI	S1,(S2)		;INCREASE TABLE LENGTH BY THIS MUCH
	$CALL	M%GMEM		;GET CORE
	PUSH	P,S1		;SAVE NEW LENGTH
	PUSH	P,S2		;SAVE NEW ADDRESS
	HRLZ	S1,SYNTAB	;POINT TO EXISTING TABLE
	HRR	S1,S2		;MAKE A BLT POINTER
	MOVE	S2,SYNTAB	;GET OLD POINTER AGAIN
	HRRZ	S2,(S2)		;AND LENGTH
	ADD	S2,(P)		;COMPUTE END OF BLT
	BLT	S1,-1(S2)	;COPY OLD TABLE INTO NEW TABLE
	MOVE	S1,-1(P)	;GET NEW TABLE LENGTH
	HRRM	S1,@(P)		;SET IN NEW TABLE
	MOVE	S2,SYNTAB	;POINT TO OLD TABLE
	HRRZ	S1,(S2)		;GET ITS LENGTH
	$CALL	M%RMEM		;RELEASE CORE
	POP	P,SYNTAB	;SET NEW TABLE ADDRESS
	POP	P,(P)		;PHASE STACK
	JRST	SYNFI3		;LOOP BACK AND ADD NEW ENTRY TO TABLE

SYNFI5:	$WTOXX	(<Unexpected error processing synonyms; ^E/[S1]/>)

SYNFI6:	MOVE	S1,SYNIFN	;GET IFN
	$CALL	F%REL		;RELEASE IT
	$RETT			;AND RETURN

; FOB for synonym file
SYNFOB:	$BUILD	FOB.MZ
	 $SET(FOB.FD,,SYNFD)
	 $SET(FOB.CW,FB.BSZ,^D7)
	 $SET(FOB.CW,FB.LSN,1)
	$EOB

; FD for synonym file
SYNFD:	$BUILD	FDMSIZ
	 $SET(.FDLEN,FD.LEN,FDMSIZ)
	 $SET(.FDSTR,,<SIXBIT/ACT/>)
	 $SET(.FDNAM,,<SIXBIT/SYN/>)
	 $SET(.FDEXT,,<SIXBIT/ACT/>)
	$EOB
SUBTTL	Account string synonyms -- Read a line from the file


; Read synonym-string=account-string
; Call:	PUSHJ	P,SYNRED
;
; TRUE return:	SYNARG = synonym-string,,account-string
; FALSE return:	EOF

SYNRED:	PUSHJ	P,SYNSTR	;READ FIRST STRING
	$RETIF			;RETURN IF EOF
	CAIE	S2,"="		;SYN STRING COMING?
	JRST	SYNRE1		;ERROR
	HRLZM	S1,SYNARG	;SAVE SYNONYN
	PUSHJ	P,SYNSTR	;READ SYNONYM
	JUMPF	SYNRE1		;ERROR IF EOF NOW
	CAIE	S2,.CHLFD	;<LF>?
	JRST	SYNRE1		;ERROR
	HRRM	S1,SYNARG	;SAVE ACCOUNT STRING ADDRESS
	AOS	SYNLIN		;COUNT THE LINE
	$RETT			;RETURN

SYNRE1:	$WTOXX	(<Bad format in synonym file following line ^D/SYNLIN/>)
	$RETF			;AND FAIL
SUBTTL	Account string synonyms -- Read a string


; Read an arbitrary ASCII string;CALL:
; Call:	PUSHJ	P,SYNSTR
;
; TRUE return:	S1 = address of string, S2 = break character
; FALSE return:	EOF

SYNSTR:	$SAVE	<P1,P2>		;SAVE SOME ACS
	MOVE	P1,[POINT 7,SYNTMP] ;P1 CONTAINS POINTER TO STORAGE
	MOVEI	P2,0		;P2 COUNTS CHARACTERS
	MOVE	S1,SYNIFN	;GET IFN

SYNST1:	$CALL	F%IBYTE		;GET A CHAR
	JUMPF	SYNST3		;JUMP IF ERRORS
	CAIE	S2,.CHNUL	;NULL
	CAIN	S2,.CHCRT	;OR <CR>
	JRST	SYNST1		;YES--BORING
	CAIE	S2,.CHTAB	;TAB 
	CAIN	S2," "		;OR SPACE?
	JRST	SYNST1		;YES--SKIP THEM TOO
	CAIE	S2,"="		;DELIMITER?
	CAIN	S2,.CHLFD	;<LF>?
	JRST	SYNST2		;YES--FOUND END
	CAIGE	P2,.AACLC	;IF NOT TOO LONG
	IDPB	S2,P1		;STORE CHARACTER
	CAIN	P2,.AACLC	;TOO LONG?
	$WTOXX	(<Truncating long string following line ^D/SYNLIN/>)
 	AOJA	P2,SYNST1	;LOOP

SYNST2:	PUSH	P,S2		;SAVE BREAK CHAR
	MOVEI	S2,0		;MAKE ASCIZ
	IDPB	S2,P1		;STORE
	ADDI	P2,5		;ROUND UP
	IDIVI	P2,5		;TO WORDS
	MOVEI	S1,(P2)		;GET WORDS REQUIRED
	$CALL	M%GMEM		;GO GET THEM
	MOVEI	S1,(S2)		;POINT TO STORAGE
	HRLZI	S2,SYNTMP	;POINT TO START OF STRING
	HRR	S2,S1		;POINT TO DESTINATION
	MOVEI	P1,(S1)		;GET DESTINATION
	ADDI	P1,(P2)		;PLUS SIZE
	BLT	S2,-1(P1)	;MOVE STRING
	POP	P,S2		;RESTORE BREAK CHAR
	$RETT			;AND RETURN

SYNST3:	CAIE	S1,EREOF$	;END OF FILE?
	$WTOXX	(<Unexpected error processing ^F/SYNFD/; ^E/[S1]/>)
	$RETF			;RETURN EOF
SUBTTL	Account string synonyms -- Translate synonym to account string


; This routine wil translate a possible synonynm to a real
; account string.
; Call:	MOVE	S1, address of possible synonym
;	PUSHJ	P,SYNCHK
;
; TRUE return:	Synonym converted to account string if necessary
; FALSE return:	Never

SYNCHK:	SKIPN	SYNFLG		;WANT SYNONYMS?
	$RETT			;NO
	$SAVE	<P1>		;SAVE P1
	MOVEI	P1,(S1)		;SAVE POINTER TO STRING
	MOVEI	S2,(S1)		;COPY POINTER
	MOVE	S1,SYNTAB	;POINT TO TABLE
	$CALL	S%TBLK		;LOOKUP IN TABLE
	TXNN	S2,TL%EXM!TL%ABR ;EXACT OR UNIQUE ABBREVIATION?
	$RETT			;NO--NOTHING TO TRANSLATE
	HRRZ	S1,(S1)		;GET NEW STRING POINTER
	HRLI	S1,(POINT 7,)	;FORM POINTER TO NEW STRING
	HRLI	P1,(POINT 7,)	;FORM POINTER TO USER STRING

SYNCH1:	ILDB	S2,S1		;GET A CHAR
	IDPB	S2,P1		;STORE
	JUMPN	S2,SYNCH1	;LOOP FOR ALL
	$RETT			;AND RETURN
SUBTTL	End


	END	ACTDAE