;;;-*-Midas-*-

TITLE PFTHMG DRAGON II

define setf text,flg
ifdef flg,.stop
.tag foobar 
printc "text
flg="
.ttymac flag
.ttyflg==.ttyflg+1
printx/flag
/
.ttyflg==.ttyflg-1
ifse flag,YES,flg==1
ifse flag,NO,flg==0
ifse flag,Y,flg==1
ifse flag,N,flg==0
ifndef flg,flg==flag
termin
ifndef flg,.go foobar
termin

setf [Macsyma and decimal time features for MC KL10]$$mcp
;setf [Puff internal stats collection (say yes)]$$stat

;$$MCP==1			;non-zero if for MC
$$STAT==1			;non-zero for PUFF internal stats collection

A=1
B=2
C=3
D=4
E=5
T=6
TT=7
H=10
I=11
J=12
Q=13
R=14
P=17

ERRCH==0
CLICH==1
DMPCH==2
maxch==3
batusr==4	;See BATCH
batdsk==5	;Same Bat time, same Bat channel!
INCH=15
OUCH=16


define syscal oper,args
	.call [setz ? sixbit /oper/ ? args ((setz))] termin

busrc==100000			   ;user control bit in USTP

EDITF:	0		;NONZERO => WAITING FOR EDITING
IFN EDITF-100, .ERR EDITF MUST BE LOCATION 100

SNAME:	sixbit /DRAGON/		;DIRECTORY TO USE
LOTSNM:	sixbit /CHANNA/		;DIRECTORY FOR LOGOUT TIMES FILE
MALSNM:	sixbit /.MAIL./		;DIRECTORY FOR SENDING MAIL

DEBUG:	-1		;NONZERO => EXTRA CHECKS, .VALUES, ETC.
FIXIND:	0		;NONZERO => FIX INDENTATION TO STANDARD VALUES

PDL:	-120,,.
	BLOCK 122

DEFINE INSIRP INS,VALS
	IRPS VAL,,[VALS]
	 INS,VAL
	TERMIN
TERMIN

ZZ==.
	LOC 40
FORTY:	0
	JSR UUOH
	-intlen,,TSINT
	LOC ZZ

;UUOS

WCHI=1000,,		;WRITE-CHAR-IMMEDIATE
XBUGCH=62000,,		;BUGCHK MACRO - MUST BE MUUO SO NO CLOBBER JPC
XCLBFI=3000,,		;CLBFIL MACRO

;Give bug message if debugging, and continue
DEFINE BUGCHK MESS/
	XBUGCH [ASCIZ:MESS

]
TERMIN

;Give clobbered file message, pop AC times, and jump to GVLFLS to flush rest of line.
DEFINE CLBFIL AC,MESS/
	XCLBFI AC,[ASCIZMESS]
TERMIN

SUBTTL	How it works

;Accounting information is saved in the DRAGON HOARD file, which is a printable
;ascii file.  At the end of the month, this file becomes the monthly report, and
;a new file is started by zeroing all the useages.

;Accounting information is obtained from the system by looking for "dmn-push"
;messages.  The system has a circular buffer which can hold several of these
;messages.  When it is idle, the daemon hangs waiting for the count of the
;number of messages pushed to change.  It then copies out the messages.
;While it is updating the file, the daemon also checks the dmn-push buffer
;every 1/2 second to try to avoid missing any messages.
;The system pushes these messages when a user logs in or out, and when
;a job tree's name changes e.g. due to detach or attach.  In future it may
;also do it spontaneously every fifteen minutes or so to keep the
;accounting up to date.

;The daemon generates two tables from the dmn-push messages.  The logged-in
;tree table has an entry for each job tree that it saw log in to the system.
;The table is indexed by the user-index of the top-level job of the tree.
;This table simply remembers the information from the login message until
;logout time.  The cruft table is a sequence of "pieces of cruft."  Each
;piece of cruft contains the accounting information for one user session.
;When it comes time to update the accounting file, the pieces of cruft
;are matched against the accounting entries.  When a match occurs, the
;usage in the piece of cruft is added to the usage in the accounting file
;for that user.

;Editing the accounting file.
;It is necessary to lock out updates of the file while it is being
;edited.  The TS GEORGE program does this.  The dragon remembers any
;dmn-push messages that happen while updating is locked out, so no
;information is lost.  GEORGE should also be run just before shooting down
;the dragon to put up a new one, to insure that the file gets updated.

;The format of the accounting file is as follows:
;It is processed a line at a time.
;A line that begins with white space (actually, space or any control character)
;is a comment, which is not processed by this program.  This is used to insert
;headings, page throws (^L), etc.

;Various special markers are indicated by a whole mess of dashes surrounding
;a keyword.  The following keywords exist:
; TOTALS - the next line is for the "TOTALS" user whose usage is the sum of all
;	   the usage in the file.
; PAGE SUBTOTALS - the next line is for the "TOTALS" user whose usage
;	   is the sum of all the usage on the same page.
; RANDOMS - the remainder of the page containing this line is for
;	   "random users".  Users not otherwise accounted for
;	   will be automatically inserted at the bottom of the page.
;	   Random users who don't log in for a month will be automatically
;	   deleted.


;^L is considered to separate pages.  The only difference this makes is
;that "page subtotals" are reset by ^L.  Also, ^L ends the randoms section.

;All other lines are accounting entries.  Each accounting entry starts
;with a uname spec, which says what user or set of users will be accounted
;for on this line.  It can be further narrowed down through the use of
;JOB, ACCT, and/or HOURS specs.  (See below.)  The remainder of the line
;contains usage informations, separated by spaces and tabs.  These are, in
;order from left to right:  the connect-time, the run-time, the swap-in count,
;the logout-date, and the logout-time.  Times are in the form dd!hh:mm:ss.
;The swap-in count is just a decimal number.  The logout date is in the
;form yy.mm.dd.

;The uname, job, and acct specs are, in the simplest case, just a sixbit
;name.  Legal characters are letters (A-Z and a-z), numbers (0-9),
;underscore (_), and dot (.).
;If any other character is required it should be preceded by a slash (/).
;It is also possible to make a spec that matches a class of names, by use
;of the wild-card characters star (*) and sharp (#).  Sharp matches any
;numeric digit.  Star matches any character (even space).  As a special
;case, a spec consisting of just a star matches anything (normally it would
;just match any one-character name.)

;The uname spec is matched against the XUNAME (what the user logged in as.)
;The job spec, which is optional and written as "JOB=spec", matches against
;the JNAME of the top-level job.  This could be used to separate HACTRNs
;from HACTROs, account separately for job-device jobs ("JOB=JOB.##"), or
;hack daemons.  The account spec, which is optional and written as "ACCT=spec",
;matches against the account-name logged in to.  [There is currently no
;way for a user to log in to an account name, however.]  This is used by
;users who want to split up their usage into several categories.
;The hours spec, which is optional and written as "HOURS=m-n", is used
;to account only for usage between certain hours (currently, it goes
;by the time of login.)  E.g. "HOURS=9-5".

;When initially setting up a file, it is necessary to list all the
;users on the system who should be accounted for, dividing them up
;into pages by groups.  Then there should be a page containing just
;-randoms-, and finally a page starting with -totals-, and containing
;a line TOTALS 0.0 0.0 0 0.0 0.0 76.08.01 on MC or
;a line TOTALS 0 0 0 76.08.01 00:00:00
;(or whatever date is appropriate.)
;If special jobnames, (e.g. job devices) or daemons are to be accounted
;for, they should go at the front.

;The following files are used by this program:
;
;	DRAGON HOARD	current accounting file
;	DRAGON SAVE	previous accounting file
;	DRAGON YESTER	yesterday's accounting file
;	_DRGN_ BUFFER	temporary name for output file
;	month REPORT	completed accounting file for that month
;	LOGOUT TIMES	old style logout-times file.
;
;	It also sends mail to MAGIC-DRAGON-KEEPER and to the last
;	person who edited the accounting file if it runs into
;	any problems.

;blah blah

SUBTTL	Program starts here

GO:	.CLOSE 1,
	.SUSET [.RSUPPRO,,T]
	JUMPGE T,GO1		;I prefer GO1, changing the directory to ZZ
	SETZM DEBUG		;top level => running as daemon	
	.SUSET [.SXUNAME,,['PFTHMG]]
	.SUSET [.SXJNAME,,['DRAGON]]
	.SUSET [.SUNAME,,['PFTHMG]]  ;UNAME first since TARAKA DRAGON may exist
	.SUSET [.SJNAME,,['DRAGON]]
	.suset [.shsname,,['DRAGON]]  ; for the sake of file authors
	JRST GO1

GO0:	SETZM SNAME		;use debugger's directory.
	SETZM LOTSNM
GO1:	MOVE P,PDL
GO2:	.RDATIM T,		;Wait until ITS thinks it knows the time
	IOR T,TT
	JUMPGE T,GO3
	MOVEI T,5*30.
	.SLEEP T,
	JRST GO2

GO3:	.suset [.ruind,,RUIND]
	MOVE T,[SQUOZE 0,LIOBLK]
	.EVAL T,
	 .LOSE
	HRLOI T,-1(T)
	EQVI T,SYCORE_-10.
	SETZ TT,
	.CALL [	SETZ		;Map in system core
		'CORBLK
		MOVEI %CBRED+%CBNDR
		MOVEI %JSELF
		MOVE T
		MOVEI %JSABS
		SETZ TT ]
	 .LOSE 1000
	IRPS SYM,RELOC,[DMNBC+DMNBD+DMNBF+DMNBFE+DMNBEL DEDTIM+USRHI+LUBLK 
			UNAME#XUNAME#JNAME#USTP#UTRNTM#USIPRQ#TRUNTM#TSIPRQ#
			SUPPRO#TIME+SYSMPT+SYSMLNG SYSMBF+]
	 MOVE T,[SQUOZE 0,SYM]	;Evaluate necessary system symbols
	 .EVAL T,
	  .LOSE
	 IFSE RELOC,+, ADDI T,SYCORE		;reloc address to system core
	 IFSE RELOC,#, ADD T,[SYCORE(R)]	;also index by user index in R
	 MOVEM T,SYM
	TERMIN
	MOVE T,DMNBFE
	SUB T,DMNBF
	IDIV T,DMNBEL
	MOVEM T,DMNBSZ
IFN $$MCP,	pushj p,maxget		;get a page of MACSYMA

	;; Don't set %OPLIV until we are fairly certain we will live
	.SUSET [.ROPTION,,T]
	TLO T,%opopc+%opliv+%opint
	.SUSET [.SOPTION,,T]
	move t,[%rlfls\%rlset,,rltinv]
	.realt t,		;give ourself periodic interrupts
	JRST IDLE		;Go enter idle loop

SUBTTL UUO Handler

UUOH:	0			;PC stored here
	PUSH P,UUOH		;this must be first instruction, see TSINT
	PUSH P,T		;UUOs shouldn't clobber any ACs.
	LDB T,[331100,,FORTY]
	CAIN T,WCHI_-33
	 JRST UWCHI
	CAIN T,XCLBFI_-33
	 JRST UCLBFI
	CAIE T,XBUGCH_-33
	 .VALUE
	.SUSET [.RJPC,,BUGJPC]
	JRST UBUGCH

UWCHI:	HRRZ T,FORTY
	PUSHJ P,WCH
UUORET:	POP P,T
	POPJ P,

;BUGCHK message in debug mode returns to DDT, in non-debug PDUMPs and continues
UBUGCH:	SKIPE DEBUG
	 JRST UBUGC1
	MOVEM 0,BUGACS		;saves ACs (already saved JPC)
	MOVE 0,[1,,BUGACS+1]
	BLT 0,BUGACS+17
	MOVE 0,BUGACS
	.CALL [	SETZ		;save core image
		SIXBIT/OPEN/
		[.UIO,,DMPCH]
		['DSK,,]
		['DRAGON]
		[SIXBIT/>/]
		SETZ [SIXBIT/CRASH/] ]
	 JRST UUORET		;tough noogies, more important to keep running
	SETZ T,
	.CALL [	SETZ
		SIXBIT/PDUMP/
		MOVEI %JSELF
		MOVEI DMPCH
		SETZ T ]
	 JRST UUORET		;tough noogies, more important to keep running
	.IOT DMPCH,[JUMPA BUGHAK]
	.IOT DMPCH,[JUMPA BUGHAK]
	.CLOSE DMPCH,
UBUGCX:	JRST UUORET

BUGHAK:	.SUSET [.SJPC,,BUGJPC]	;here restore state so can look at dump
	MOVSI 17,BUGACS
	BLT 17,17
	.VALUE [ASCIZ\:SL CHANNA;RAKASH PFTHMG
P\]
UBUGC1:	POP P,T
	.VALUE @FORTY
	POPJ P,

;CLBFIL message in debug mode returns to DDT, in non-debug mode generates mail.
UCLBFI:	SKIPE DEBUG
	 .VALUE [ASCIZ\: CLBFIL 
\]
	AOS C,NERRS		;save message and line number in buffer
	CAILE C,LERRBF
	 JRST UCLBF2
	SOS C
	HRRZ T,FORTY
	MOVEM T,ERRBFZ(C)
	MOVE T,LINENO
	MOVEM T,ERRBFL(C)
	MOVE T,UNAMEA
	MOVEM T,ERRBFA(C)
	MOVE T,UNAMEB
	MOVEM T,ERRBFB(C)
	MOVE T,UNAMEC
	MOVEM T,ERRBFC(C)
UCLBF2:	LDB T,[270400,,FORTY]	;flush pdl
	CAIN T,17
	 JRST UUORET
	ADDI T,2
	HRLS T
	SUB P,T
	JRST GVLFL1		;flush rest of input line

SUBTTL Routines to gobble dmn-push messages

;Procedure to gobble any dmn-push messages available from the system.
;Clobbers pretty much all ACs.

DMNPOP:	MOVE Q,LDMNBD		;last message processed
	CAML Q,@DMNBD
	 POPJ P,		;buffer empty
	ADD Q,DMNBSZ
	CAML Q,@DMNBC
	 JRST DMNPO1		;jump if haven't missed any
;Following 2 lines commented out.  We're never going to get rid of
;these and I'm tired of deleting the crash files.  11/30/77
;	SKIPE LDMNBD		;if just started up, mumble this always happens
;	 BUGCHK Missed dmn-push message.
	MOVE Q,@DMNBC		;get as many as we can
	SUB Q,DMNBSZ
	MOVEM Q,LDMNBD
DMNPO1:	AOS Q,LDMNBD		;take next one after previous
	SUBI Q,1		;fudge (DMNBD=1 after msg 0 pushed).
	IDIV Q,DMNBSZ		;mod size of buffer
	MOVE Q,R
	IMUL Q,DMNBEL
	ADD Q,DMNBF		;now Q -> the dmn-push message
	HRLZ T,Q		;save the message in case of a BUGCHK
	HRRI T,BUGMSG
	BLT T,BUGMSG+7
	MOVE T,LI.IDX(Q)	;figure out what type of message it is
	TLNE T,LI%COD
	 JRST DMNPO2
	TLNE T,LO%COD
	 JRST DMNPO3
	BUGCHK Unknown dmn-push message.
	JRST DMNPOP		;try for more messages

DMNPO2:	PUSHJ P,LOGIN
	JRST DMNPOP		;try for more messages

DMNPO3:	PUSHJ P,LOGOUT
	JRST DMNPOP		;try for more messages

;Procedure to process dmn-push message for log in.
;Q -> the message.  The logged-in tree table is updated.
;Uses T, TT, I, J, A, B.

LOGIN:	MOVE I,LI.IDX(Q)	;get user index of top job of tree
	TLZE I,LI%COD		;check that it's really a login.
	 TLNE I,-1
	  BUGCHK Message-type not Login.
	IDIV I,LUBLK
	CAIL I,NTREES		;make sure logged in trees table is big enough
	 .LOSE
	SKIPN TR.UNM(I)		;make sure table entry not already in use
	 JRST LOGIN0
	HLRO TT,TR.UNM(I)	;but watch out for wierd screw in ITS.
	MOVE T,TR.JNM(I)	;where not logged in job sets its jname
	CAMN T,LI.JNM(Q)	;(causing an accounting update) then logs in
	 AOJE TT,LOGIN0		;(causing log in of already logged in job).
;		;Following line commented out until bugs in ATTACH/DETACH fixed up.
;	BUGCHK That user index already logged in.

LOGIN0:	MOVE T,LI.UNM(Q)	;fill in the table entry
	MOVEM T,TR.UNM(I)
	MOVE T,LI.JNM(Q)
	MOVEM T,TR.JNM(I)	
	MOVE T,LI.TNM(Q)
	MOVEM T,TR.TNM(I)
	MOVE TT,LI.TIM(Q)
	MOVEM TT,TR.TIM(I)
;	MOVE T,LI.XUN(Q)
	MOVE T,LI.UNM(Q)	; XUNAME not pushed by sys, screw around
	MOVEM T,TR.XUN(I)	; Guess that UNAME is correct
	MOVE TT,[-6,,[	SIXBIT /XUNAME/	? MOVEM T
			SIXBIT /UNAME/	? MOVEM A
			SIXBIT /JNAME/	? MOVEM B ]]
	SYSCALL USRVAR,[MOVEI %JSNUM(I) ? MOVE TT]
	 POPJ P,		; Gone?  Give up.
	CAMN A,TR.UNM(I)
	 CAME B,TR.JNM(I)
	  POPJ P,		; Not the same job?  Give up.
	TLC T,-1
	TLCE T,-1
	 MOVEM T,TR.XUN(I)	; Logged in?  Use it!
	POPJ P,

;Procedure to process dmn-push message for log out.
;Q -> the message.  Corresponding entry in logged-in tree table
;is found, and cruft is pushed.  Later the file will be updated
;from the cruft.
;Uses T, TT, H, I, J, A, B, C.

LOGOUT:	MOVE I,LO.IDX(Q)	;get user index of top job of tree
	TLZE I,LO%COD		;check that it's really a logout.
	 TLNE I,-1
	  BUGCHK Message-type not Logout.
	IDIV I,LUBLK
	CAIL I,NTREES		;make sure logged in trees table is big enough
	 .LOSE
	SETZB A,C		;no connect time nor trmnam if missed log in.
	MOVE B,LO.UNM(Q)	;no xuname if missed log in.
	MOVEI TT,9		;no hour if missed log in.
	SKIPN TR.UNM(I)
	 JRST LOGOU0		;jump if missed log in.
	MOVE TT,LO.UNM(Q)	;make some checks
	CAME TT,TR.UNM(I)
	 JRST LOGOU3
	MOVE TT,LO.JNM(Q)
	CAME TT,TR.JNM(I)
	 JRST LOGOU3
	MOVE A,LO.TIM(Q)	;get connect time
	SUB A,TR.TIM(I)
	JUMPL A,[ BUGCHK Negative connect time.
		  MOVEI A,0
		  JRST .+1 ]
	MOVE B,TR.XUN(I)	;get XUNAME
	MOVE C,TR.TNM(I)	;get TRMNAM
	SETZM TR.UNM(I)		;guy is now logged out
	.CALL [	SETZ		;compute hour of login in TT
		'RQDATE
		MOVEM T
		SETZM T ]	;QDATE system came up
	 SETO T,
	MOVEI TT,9		;9 AM is default if
	AOJE T,LOGOU0		; sys doesn't know time
	MOVEI T,-1(T)
	IMULI T,15.		;30ths since midnight when system came up
	ADD T,TR.TIM(I)		;30ths since midnight of login
	IDIVI T,3600.*30.	;hours since midnight of login
	IDIVI T,24.		;modulo 24 in case system up more than one day

ifn $$STAT,aos crftys		;count this for statistical purposes

logou0:	pushj p,getcor		;allocate one crufty
	  popj p,		;  no core.
	MOVEM A,CF.CTM(H)
	MOVEM B,CF.UNM(H)
	MOVEM C,CF.TNM(H)
	MOVEM TT,CF.HOR(H)
	MOVE T,LO.JNM(Q)
	MOVEM T,CF.JNM(H)
	MOVE T,LO.RTM(Q)	;HLRZ if still had old 4.069 usec clock
	addi t,4166.		;round,
	idivi t,8333.		;converting to seconds*30.
	MOVEM T,CF.RTM(H)
	MOVE T,LO.SWI(Q)
	MOVEM T,CF.SWI(H)
ifn $$mcp,[
	setzm cf.mct(h)		;MACSYMA type is counted elsewhen
	setzm cf.mrt(h)		
];mcp
	PUSHJ P,RDATIM		;TT gets date, T gets time
	MOVEM TT,CF.DAT(H)
	MOVEM T,CF.TIM(H)
	SETOM CF.VAL(H)
	POPJ P,

;This BUGCHK commented out due to ITS lossage....8/9/76
LOGOU3:	;BUGCHK Logout Tree doesn't match logged-in tree.
	SETZM TR.UNM(I)
	JRST LOGOU0


getcor:	MOVEI H,CF.LEN		;push onto end of cruft table
	ADDB H,CRUFTP
getc1a:	CAMG H,CRUFTE
	  jrst getcr1

ifn $$STAT,aos coraos		;count how often we
	MOVE T,CRUFTE		;need more core

	LSH T,-10.
	CAIL T,<SYCORE_-10.>-3
	 .LOSE			;URK
	.CALL [	SETZ
		'CORBLK
		MOVEI %CBRED+%CBWRT+%CBNDR+%CBNDW
		MOVEI %JSELF
		MOVEI (T)
		SETZI %JSNEW ]
	 JRST [	MOVEI TT,5*30.	;CAN'T GET CORE, WAIT 5 SEC
		.SLEEP TT,
		XCT .-1		;TRY AGAIN
		  JRST getcrb	;STILL LOSING, BARF
		JRST .+1 ]	;WON AFTER ALL
	MOVEI T,1_10.
	ADDM T,CRUFTE
	JRST getc1a

getcrb:	BUGCHK Can't get core to expand cruft table.
	MOVEI H,10.*30.		;WAIT 10 SECONDS BEFORE TRYING ANYTHING
	.SLEEP H,
	MOVNI H,CF.LEN
	ADDM H,CRUFTP
	POPJ P,

getcr1:	cail h,cruftz+<crfsiz*2000>-<crfmrg*cf.len>  ;is it getting big?
	  jrst [setom dmpcnt	;  yes, request dump soon
	        .suset [.sipirqc,,[%pirlt]]  ;And speed things up a bit
		jrst .+1]
	subi h,cf.len		;h -> new cruft.
	jrst popj1		;return

;Defensive routine to find out the date and time.
;TT gets date, T gets time, nothing clobbered.
;Sets stuff to -1 if it wouldn't have done for the duke.

RDATIM:	.RDATIM T,
	PUSH P,A
	PUSH P,B
	PUSHJ P,RDATI1
	EXCH T,TT
	PUSHJ P,RDATI1
	EXCH T,TT
	POP P,B
	POP P,A
	POPJ P,

;Subroutine to fix T
RDATI1:	JUMPLE T,RDATI9		;ITS knows it's losing
	MOVE B,[440600,,T]
RDATI2:	ILDB A,B		;Check for garbage characters
	CAIL A,'0
	 CAILE A,'9
	  JRST RDATI9
	TLNE B,770000
	 JRST RDATI2		;Needn't check for wrong year, month 13, etc.
	POPJ P,			;since dragon doesn't flame at those anyway.

RDATI9:	SETOB T,TT		;lossage, don't trust any of it
	POPJ P,

SUBTTL	Idle Loop

;This is where we sit when there's nothing to do.
;.HANG on the daemon buffer.
;Also, every 15 minutes do a file-update.
;If system goes down, wait for full down-ness then log out self, sys, and
;core and perform final file-update.

IDLE:	PUSHJ P,DSKLOG		;Log disk errors.
	MOVE T,@TIME		;Check for need to run hourly daemon kludge
	SUB T,RYGTIM
	CAIGE T,3600.*30.
	 JRST IDLE01
	move a,[sixbit /hourly/]
	pushj p,batch
IDLE1B:	MOVE T,@TIME
	MOVEM T,RYGTIM'
IDLE01:	move t,dmpinv		;15 minutes (unless sys going down)
	movem t,dmpcnt		;   from now do update
	.SUSET [.SMASK,,[%PIRLT+%PICLI+%PIDWN+%PIPDL]]
IDLE00:	MOVE T,LDMNBD
IDLE0:	CAMN T,@DMNBD		; PC will be IDLE0
IDLE1:	 .HANG			; or IDLE1 if interrupt out
SHUTD0:	PUSHJ P,DMNPOP		;gobble down any daemon messages
	SKIPL @DEDTIM		;unless ITS is down,
	 JRST IDLE00		;wait more.
	MOVE T,@TIME		;save time that system went down.
	SKIPN DWNTIM
	 MOVEM T,DWNTIM
	SUB T,DWNTIM		;# 30ths since shutdown
	CAIL T,5*30.		;wait at most 5 seconds for other daemons to logout.
	 JRST SHUTD2
	MOVE R,@USRHI		;see how many jobs are still on the system
	SETZ A,
SHUTD1:	SUB R,LUBLK
	SKIPE @UNAME
	 AOS A
	JUMPG R,SHUTD1
	CAILE A,3
	 JRST IDLE00		;some OPTLIV daemon hasn't logged out yet.
SHUTD2:	MOVE R,@USRHI		;Time to go.
SHUTD3:	SUB R,LUBLK		;Log out the jobs that are left (sys, core, us).
	SKIPE @UNAME
	 SKIPL @SUPPRO
	  JRST SHUTD4
	MOVEI Q,FAKMSG		;fake up the dmn-push message
	HRRM R,LO.IDX(Q)	;see DMNPLO and LOGUSE in ITS.
	MOVE T,@TIME
	MOVEM T,LO.TIM(Q)
	MOVE T,@UNAME
	MOVEM T,LO.UNM(Q)
	MOVE T,@JNAME
	MOVEM T,LO.JNM(Q)
	MOVE TT,@UTRNTM
	ADD TT,@TRUNTM
	MOVE T,@USIPRQ
	ADD T,@TSIPRQ
	MOVEM TT,LO.RTM(Q)
	MOVEM T,LO.SWI(Q)
	PUSHJ P,LOGOUT		;gobble down this fake message
SHUTD4:	JUMPG R,SHUTD3
	JRST FILEUP		;Do final FILEUP and .LOGOUT

SUBTTL	Interrupt handler

TSINT:	setz p			;push the .JPC and .SUUOH and miscellany ...
	%piioc ? 0 ? -1 ? -1 ? iocint  ;IOC errors
	%pidwn ? 0 ?  0 ?  0 ? dwnint  ;only sets flags, don't defer anything
				;%PIRLT and %PICLK defer each other to prevent
				;contention for pushing cruft
				;%PIRLT's get delayed if they happen while not
				;idle, so there can be no contention from
				;%PIRLT's vs. MP cruft pushes.
	%piclk ? 0 ? %piclk\%pirlt\%picli ?  0 ? clkint  ;finish up first
	%pirlt ? 0 ? %pirlt\%picli\%pidwn\%piclk ? 0 ? rltint  ;time-critical
	%picli ? 0 ? %pirlt\%picli ? 0 ? cliint	 ;defer %PIRLT so handler exits
				;before getting %PIRLT it gives itself.
intlen==.-tsint


;Here on system-going-down interrupt

DWNINT:	PUSH P,T
	SKIPE T,@DEDTIM
	 JRST DWNIN1
	SETZM DWNTIM		;ITS revived.
	movni t,31		;back to normal intervals
	movem t,dmpinv
	setom maxinv		;count out once per clock tick.
	setom maxcnt
	movei t,60.*3600./100.	;interval under normalcy
	movem t,rltinv
	jrst dismst		;return

DWNIN1:	CAIL T,2*60.*60.	;Going down in less than two minutes?
	  jrst dismst		;No, don't panic.
	movni t,4		;Yes, start checking world every 2 seconds.
	came t,dmpinv		;is it already schedualled sooner?
	  movem t,dmpcnt	;  no, schedual it sooner
	movem t,dmpinv		;and make rest of world use this interval
	movni t,18.		;every 18 ticks (36 secs) do the MACSYMA update
	came t,maxinv		;so they don't happen at our accelerated rate.
	  movem t,maxcnt
	movni t,60.*2.		;2. seconds,
	movem t,maxinv		;set the interval
	move t,[%rlfls\%rlset,,rltinv]	;spead up the clock
	.realt t,
	jrst dismst

;Here on real-time interrupt

RLTINT:	PUSH P,T
	hrrz t,-4(p)		;get the PC we interrupted from.  Idle?
	CAIE T,IDLE0
	 CAIN T,IDLE1
	  JRST RLTIN0
	MOVE T,[%rlfls\%rlset,,[60.]]	;no, check again in a second
	.REALT T,
	jrst dismst		;dismis from the interrupt

RLTIN0:	move t,[%rlfls\%rlset,,rltinv]	;and hack it for that interval
	.realt t,
ifn $$mcp,[
	aosl maxcnt		;if this is a for-real interrupt
	  pushj p,maxscn	;  update MACSYMA's
];mcp

	pop p,t			;restore T
	syscal dismis,[%clbit,,(setz)  ;dismis to our hack
	       p
	       %climm,,rlthak]	;hack our from directly.
	  .lose %lssys
rlthak:	aosge dmpcnt		;time to update the file?
	  jrst idle00		;  nope, just go hack them some more

	SKIPE T,@DEDTIM		;system down or going down soon?
	  CAIL T,2*60.*60.	;  i.e. 2 minutes
	    jrst FILEUP		;    no, run file update generator
	jrst shutd0		;yes, do shutdown checks

;Here on slow-clock interrupt.  While we aren't idle, every 1/2
;second we check for dmn messages this way, to avoid losing any.

CLKINT:	INSIRP PUSH P,A B C D E T TT H I J Q R FORTY UUOH
	PUSHJ P,DMNPOP
	INSIRP POP P,UUOH FORTY R Q J I H TT T E D C B A
	jrst dismis		;return


;Here on IOCER interrupt.  If device full, wait ten seconds then try
;again.  If anything else, we're screwed.

IOCINT:	PUSH P,T
	.SUSET [.RBCHN,,T]
	MOVSI T,.RIOS(T)
	HRRI T,T
	.SUSET T
	LDB T,[.BP (37000),T]	; Get IOCER code
	CAIE T,11		; DEVICE FULL?
	 jrst [	move t,-4(p)	; No, dismis and instantly .LOSE
	        hrlm t,losadr	; RH has 1+.LZ %PIIOC
		pop p,t
	        syscal dismis,[movsi (setz) ? move p
				move -3(p)	; Restore PC
				move -5(p)	; Restore DF1
				move -4(p)	; Restore DF2
				move losadr]	; Do .LOSE
		  .lose %lssys]
	MOVEI T,10.*30.
	.SLEEP T,
	hrrz t,-4(p)		;get the PC off the stack
	CAIL T,UBUGCH
	 CAILE T,UBUGCX
	  jrst dismst		;retry the failing instruction.
	MOVEI T,UUORET		;disk full while dumping crash file, give up!
	movem t,-4(p)
	.CLOSE DMPCH,
dismst:	pop p,t			;restore T
dismis:	syscal dismis,[%clbit,,(setz) ? p]
	  .lose %lssys
       
losadr:	0,,1+.lz %piioc

;Here on CLI interrupt.  Someone wants to edit the accounting file.
;Latch onto the job in question, do a file update, tell the job
;to proceed, and wait for it to go away.

CLIINT:	.OPEN CLICH,[.UII,,'CLA]
	 JRST CLIINL
	.IOT CLICH,UPDATR
	.IOT CLICH,UPDATJ
	.CALL [	SETZ
		SIXBIT/OPEN/
		[10,,CLICH]
		['USR,,]
		UPDATR
		SETZ UPDATJ ]
	 JRST CLIINL
	.CALL [	SETZ
		'CORBLK
		MOVEI %CBRED+%CBNDR
		MOVEI %JSELF
		MOVEI CMPAGE_-10.
		MOVEI CLICH
		SETZI 0 ]
	 JRST CLIINL
	SETOM CLIFLG		;cause it to hang up
	setom dmpcnt		;make next real-time int cause file update
	.suset [.sipirqc,,[%pirlt]]  ;cause immediate real-time interrupt
	CAIA
CLIINL:	 BUGCHK CLI Lossage.
	.CLOSE CLICH,
	jrst dismis

SUBTTL	File update generator

FILEUP:
ifn $$mcp,[
	pushj p,maxget		;re-map MACSYMA in case it has changed
	pushj p,maxlog		;first, log all the MACSYMA's up to now.
];mcp
	MOVE Q,CRUFTP
	MOVEM Q,CRUFTF		;all cruft to here will be gobbled, more may come in
	SUBI Q,CRUFT		;now sort the cruft table by uname
	SKIPN CHKALL		;do anyway if need to check file syntax.
	 SKIPE CLIFLG		;or if hacking CLI
	  CAIA
	   JUMPE Q,IDLE		;well, not much to do here

	.SUSET [.SMASK,,[%PICLK+%PIIOC+%PIDWN+%PICLI+%PIPDL]]
FILEU1:	MOVEI A,CRUFT		;Start a sort pass
	SETZ R,			;this used to be a shell sort, but now it's a bubble.
FILEU2:	MOVE B,A		;next step of pass		;maybe it will work.
	ADDI B,CF.LEN
	CAML B,CRUFTF
	 JRST FILEU5		;pass done
	MOVE T,CF.UNM(A)	;compare two crufties
	CAMG T,CF.UNM(B)
	 JRST FILEU4
	HRLI A,-CF.LEN		;out of order, exchange
FILEU3:	MOVE T,(A)
	EXCH T,(B)
	MOVEM T,(A)
	AOS B
	AOBJN A,FILEU3
	AOJA R,FILEU2

FILEU4:	ADDI A,CF.LEN
	JRST FILEU2

FILEU5:	JUMPG R,FILEU1		;if exchanged any, do another pass
	MOVEI A,CRUFT		;should now be sorted, but I feel insecure...
FILEU6:	MOVEI B,CF.LEN(A)
	CAML B,CRUFTF
	 JRST FILEU7
	MOVE T,CF.UNM(A)
	CAMLE T,CF.UNM(B)
	 .VALUE
	ADDI A,CF.LEN
	JRST FILEU6

;Open input and output files
FILEU7:	.SUSET [.SSNAME,,SNAME]
	.OPEN INCH,[.BAI,,'DSK ? 'DRAGON ? SIXBIT/HOARD/]
	 .LOSE 1400
	.OPEN OUCH,[.BAO,,'DSK ? SIXBIT/_DRGN_/ ? SIXBIT/BUFFER/]
	 .LOSE 1400
	SETZM INBC
	SETOM BBC
	SETOM OUTBC
	SETZM NERRS
	SETZM LINENO
	IRPS FIG,,STCTIM STRTIM STSWAP STLODT STLOTM
	 SETZM FIG		;reset subtotals since starting a page
	 TERMIN
	IRPS FIG,,GTCTIM GTRTIM GTSWAP
	 SETZM FIG		;reset grand totals since starting a page
	 TERMIN
ifn $$mcp,[
	IRPS FIG,,STMCTM STMRTM GTMCTM GTMRTM
	 SETZM FIG
	 TERMIN
];mcp
	PUSHJ P,GROVEL
;drops through
;drops in
;Here on EOF.  Finish up and return to the idle loop.

FILFIN:	SKIPGE TOTLF
	 CLBFIL 17,Totals section does not contain TOTALS line.
	SETZM TOTLF
	SKIPE STOTLF
	 CLBFIL 17,Sub-total screw.
	SETZM STOTLF
	SKIPE RANDF
	 CLBFIL 17,Randoms section doesn't end, probably no Totals section.
	SETZM RANDF
	SETZM DRANDF
	PUSHJ P,WBUF		;dump out the last buffer
	.CALL [	SETZ		;and close the output file
		'RENMWO
		MOVEI OUCH
		[SIXBIT/DRAGON/]
		SETZ [SIXBIT/HOARD/] ]
	 .LOSE 1000
	SKIPE MNTHLF
	 JRST MNTHL1
	SKIPE NERRS
	 JRST FILEU8		;jump if clobbered file to be saved and mail to be sent
	.CALL [	SETZ		;save one version back of input file
		'DELETE
		[SIXBIT/DSK/]
		[SIXBIT/DRAGON/]
		SETZ [SIXBIT/SAVE/] ]
	 JFCL
	.CALL [	SETZ
		'RENMWO
		MOVEI INCH
		[SIXBIT/DRAGON/]
		SETZ [SIXBIT/SAVE/] ]
	 BUGCHK Can't rename input file.
FILU8X:	.CLOSE INCH,		;Here when file update complete.
	.CLOSE OUCH,
	PUSHJ P,LOTUP		;do the LOGOUT TIMES file.
	HRRZS CHKALL		;delete program file-checking request
	PUSHJ P,GOIDLE		;clean up cruft table
	SKIPE YEARF		;perform periodic special processing
	 PUSHJ P,YEARLY
	SKIPE MONTHF
	 PUSHJ P,MNTHLY
	SKIPE DAYF
	 PUSHJ P,DAILY
	SKIPGE @DEDTIM
	 .LOGOUT		;ka-zap. (finally)
	SETZM EDITF		;any previous edits have now been gobbled down
	SKIPL CLIFLG
	 JRST IDLE		;exit from file update stuff
	.SUSET [.SIMASK,,[%PICLK]]  ;going to hang up waiting for user to edit file.
	MOVE T,CMPAGE+100	;check user's "password"
	CAME T,['GEORGE]
	 JRST FILU8Z
	MOVNS CLIFLG		;initial file-update complete.
	SETOM EDITF		;tell the user to go ahead.
	SKIPE CMPAGE+100	;wait for user to edit the file.
	 .HANG
FILU8Z:	SETZM CLIFLG		;clear out interlocks
	HRROS CHKALL		;check user's edits.
	.CALL [	SETZ		;flush communications page.
		'CORBLK
		MOVEI 0
		MOVEI %JSELF
		SETZI CMPAGE_-10. ]
	 JFCL
	JRST FILEUP		;gobble down edited file and check for errors,
				;also file away cruft accumulated during editing.

;Call here to restore environment after doing a file-update.
;Clobber T, TT.

GOIDLE:	.SUSET [.SAMASK,,[%PICLK]]	;no more 1/2 second breaks please
	movei t,-25.		;reset back to time to wait for next update
	movem t,dmpcnt
	MOVE T,CRUFTP		;find amount of cruft pushed while updating
	SUB T,CRUFTF
	JUMPE T,GOIDL1
	HRLZ TT,CRUFTF		;copy it down to beginning of cruft buffer
	HRRI TT,CRUFT
	BLT TT,CRUFT-1(T)
GOIDL1:	MOVEM T,CRUFTP		;fix pointers
	MOVEI T,CRUFT
	MOVEM T,CRUFTF
	ADDB T,CRUFTP
	CAIL T,CRUFTZ-100	;If there are unused
	 POPJ P,
	MOVE T,CRUFTE		; excess cruft pages,
	SUBI T,CRUFTZ		; flush them.
	JUMPLE T,CPOPJ
	LSH T,-10.		;number of such pages.
	MOVNS T
	MOVSS T
	HRRI T,CRUFTZ_-10.	;aobjn pointer to pages
	.CALL [	SETZ
		'CORBLK
		MOVEI 0
		MOVEI %JSELF
		SETZ T ]
	 .LOSE 1000		;should never fail.
	MOVEI T,CRUFTZ		;reset pointer to end of core
	MOVEM T,CRUFTE
	POPJ P,

;Yearly processing

YEARLY:	SETZM YEARF
	.SUSET [.SIMASK,,[%PICLK]]
	move a,[sixbit /yearly/]
	pushj p,batch
	.CALL [	SETZ
		SIXBIT/OPEN/
		[.BAO,,OUCH]
		[SIXBIT/DSK/]
		[SIXBIT/MAIL/]
		[SIXBIT/>/]
		SETZ MALSNM ]
	 JRST YEARLX
	SETOM OUTBC
	MOVEI TT,[ASCIZFROM-JOB:DRAGON
SENT-BY:PFTHMG-DRAGON
RCPT:(MAGIC-DRAGON-KEEPER)
SUBJECT:Happy New Year
TEXT;-1
Well, I couldn't think of anything else that needed to be in the yearly processing.
]
	PUSHJ P,ASZW
	PUSHJ P,WBUF
YEARLX:	.CLOSE OUCH,
	.SUSET [.SAMASK,,[%PICLK]]
	POPJ P,

FILEU8:	.CALL [	SETZ
		'RENMWO
		MOVEI INCH
		[SIXBIT/CLBFIL/]
		SETZ [SIXBIT/>/] ]
	 .LOSE 1000
	.CALL [	SETZ		;Send mail
		SIXBIT/OPEN/
		[.BAO,,OUCH]
		[SIXBIT/DSK/]
		[SIXBIT/MAIL/]
		[SIXBIT/>/]
		SETZ MALSNM ]
	 .LOSE 1400
	SETOM OUTBC
	SKIPN A,UPDATR
	 JRST FILU8E
	MOVEI TT,[ASCIZTO:CM"]
	PUSHJ P,ASZW
	SETZB B,C
	PUSHJ P,SIXW
	WCHI 15
	WCHI 12
FILU8E:	MOVEI TT,[ASCIZFROM-JOB:DRAGON
SENT-BY:PFTHMG-DRAGON
RCPT:(MAGIC-DRAGON-KEEPER)
SUBJECT:Clobberage of accounting file
TEXT;-1
Bad syntax was detected in the accounting file.  The offending lines
have been deleted.  The bad file is saved as ]
	PUSHJ P,ASZW
	PUSHJ P,FLNMW
	MOVEI TT,[ASCIZ
The errors encountered were:
]
	PUSHJ P,ASZW
FILU8A:	CAML C,NERRS
	 JRST FILU8C
	CAIL C,LERRBF
	 JRST FILU8B
	MOVE TT,ERRBFZ(C)
	PUSHJ P,ASZW
	MOVEI TT,[ASCIZ  (Line ]
	PUSHJ P,ASZW
	MOVE A,ERRBFL(C)
	PUSHJ P,NUMW
	MOVE A,ERRBFA(C)
	MOVE B,ERRBFB(C)
	SKIPN ERRBFC(C)
	 JUMPE A,[ JUMPE B,FILU8D ? JRST .+1 ]
	WCHI ",
	WCHI 40
	PUSH P,C
	MOVE C,ERRBFC(C)
	PUSHJ P,SIXW
	POP P,C
FILU8D:	MOVEI TT,[ASCIZ)
]
	PUSHJ P,ASZW
	AOJA C,FILU8A

FILU8B:	MOVEI TT,[ASCIZetc ad nauseam]
	PUSHJ P,ASZW
FILU8C:	PUSHJ P,WBUF
	JRST FILU8X

SUBTTL	Daily processing

;Save the accounting file.
;Check for clobbered LOGOUT TIMES file.
;Maybe this should also sort the randoms?

DAILY:	SETZM DAYF
	HRROS CHKALL		;Check whole file once a day.
	.SUSET [.SIMASK,,[%PICLK]]
	move a,[sixbit /daily/]
	pushj p,batch
	.OPEN INCH,[.BAI,,'DSK ? 'DRAGON ? SIXBIT/SAVE/ ]
	 JRST DAILX
	.OPEN OUCH,[.BAO,,'DSK ? 'DRAGON ? 'YESTER ]
	 JRST DAILX
DAILY1:	MOVE T,[-OUTBFL,,OUTBUF]
	.IOT INCH,T
	HRLOI TT,-OUTBUF-1(T)
	EQVI TT,OUTBUF
	.IOT OUCH,TT
	JUMPGE T,DAILY1
DAILX:	.CLOSE INCH,
	.CLOSE OUCH,
	SETOM LOTCHF'
	.CALL [	SETZ
		SIXBIT/OPEN/
		[.BAI,,INCH]
		[SIXBIT/DSK/]
		[SIXBIT/LOGOUT/]
		[SIXBIT/TIMES/]
		SETZ LOTSNM ]
	 JRST DAILX1
LOTCH1:	MOVE T,[-5,,LOTOLD]
	.IOT INCH,T
	JUMPGE T,LOTCH2
	CAMN T,[-5,,LOTOLD]
	 JRST DAILX1
	PUSHJ P,LOTCH9
	MOVEI TT,[ASCIZ\File length not a multiple of 5 words.
\]
	PUSHJ P,ASZW
	JRST DAILX1

LOTCH9:	AOSE LOTCHF
	 POPJ P,
	.CALL [	SETZ
		SIXBIT/OPEN/
		[.BAO,,OUCH]
		[SIXBIT/DSK/]
		[SIXBIT/MAIL/]
		[SIXBIT/>/]
		SETZ MALSNM]
	 .LOSE 1400
	SETOM OUTBC
	MOVEI TT,[ASCIZ\FROM-JOB:DRAGON
SENT-BY:PFTHMG-DRAGON
RCPT:(MAGIC-DRAGON-KEEPER)
TEXT;-1
The file \]
	PUSHJ P,ASZW
	PUSHJ P,FLNMW
	MOVEI TT,[ASCIZ\ seems to be clobbered.  Please check it.
The offensive material follows:
\]
	PUSHJ P,ASZW
	POPJ P,
	
LOTCH2:	MOVE B,[440700,,LOTOLD]
	MOVE C,[440700,,[ASCIZ\ZZZZZZ99/99/99 99:99:99
\]]
LOTCH3:	ILDB T,B
	ILDB TT,C
	JUMPE TT,LOTCH1
	CAIN TT,"Z
	 JRST [	CAIL T,40
		 CAIL T,140
		  JRST LOTCH4
		JRST LOTCH3 ]
	CAIN TT,"9
	 JRST [	CAIL T,"0
		 CAILE T,"9
		  JRST LOTCH4
		JRST LOTCH3 ]
	CAMN T,TT
	 JRST LOTCH3
LOTCH4:	PUSHJ P,LOTCH9
	MOVEI TT,LOTOLD
	PUSHJ P,ASZW
	MOVEI TT,[ASCIZ/
/]
	PUSHJ P,ASZW
	JRST LOTCH1

DAILX1:	SKIPL LOTCHF
	 PUSHJ P,WBUF
	.CLOSE INCH,
	.CLOSE OUCH,
	.SUSET [.SAMASK,,[%PICLK]]
	POPJ P,

SUBTTL	Monthly processing

;Save the accounting file as DRAGON lastmonth,
;and reset all the usages to zero.

MNTHLY:	SETZM MONTHF
	.SUSET [.SIMASK,,[%PICLK]]
	move a,[sixbit /mnthly/]
	pushj p,batch
	SETOM MNTHLF			;Use regular grovel routines, almost...
	HRROS CHKALL			; [this is needed to get all lines processed]
	JRST FILEU7			; upon completion, they will return to MNTHL1

;Trap here from GROVL9 for each line in the file.
MNTHL2:	SKIPE DRANDF			;if random user with no usage last month,
	 SKIPE CTIME
	  JRST MNTHL3
	SKIPN RTIME
	 SKIPE SWAPS
	  JRST MNTHL3
	JRST GROVEL			;flush him completely

MNTHL3:	SETZM CTIME			;reset usage
	SETZM RTIME
	SETZM SWAPS
ifn $$mcp,[
	setzm mctime
	setzm mrtime
];mcp
	PUSHJ P,WBACK			;write it back out
	SKIPN TOTLF			;unless this was final totals line
	 JRST GROVEL			;and go process more

ifn $$STAT,[
ifn $$MCP,[
	MOVEI TT,[ASCIZ\

  STATS fups crfts crftm core runt   nomax normax newfls newcnt oldfls oldcnt

STATS   0    0	   0	 0    0.0    0.0   0.0	  0	 0	0      0 

  FUPS    - # of file updates
  CRFTYS  - # of pieces of cruft pushed from logouts
  CRFTYM  - # of pieces of cruft pushed from MACSYMAs
  CORE	  - # of times core has been grown.  Core is flushed when file updated.
  RUNT	  - runtime consumed by PFTHMG
  NOMAX	  - amount of time during which there were no MACSYMA's on the machine
  NORMAX  - amount of time during which no one accrued MACSYMA connect time
  NEWFLS  - # of times new entries in MACSYMA table had to be invalidated
  NEWCNT  - # of entries so invalidated
  OLDFLS  - # of times all entries in MACSYMA table had to be invalidated
  OLDCNT  - # of entries so invalidated

  This file is for period beginning \]
]; END IFN $$MPC,
IFE $$MCP,[
	movei tt,[asciz /

  STATS fups crfts core runt

STATS	0    0	   0	0.0

  FUPS    - # of file updates
  CRFTYS  - # of pieces of cruft pushed from logouts
  CORE	  - # of times core has been grown.  Core is flushed when file updated.
  RUNT	  - runtime consumed by PFTHMG

  This file is for period beginning /]
]; END IFE $$MCP,
]; END IFN $$STAT,

ife $$stat,[
	movei tt,[asciz /

  This file is for period beginning /]
]; END IFE $$STAT

	PUSHJ P,ASZW
	MOVE A,LODAT
	PUSHJ P,DATW
	WCHI 40
	.rtime a,		;gotta get the time from system
	movem a,lotim		;and use it; we don't remember logout times
	PUSHJ P,TIMW
	MOVEI TT,[ASCIZ\

	System Uptime Report

OK   \]
	MOVE D,LODAT
	MOVEM D,OKDAT
	MOVE D,LOTIM
	MOVEM D,OKTIM
	SETZM OKTMUP
	MOVEI D,OKDAT
	PUSHJ P,UPTRWW
	SETZM TOTLF			;avoid screw at FILFIN
	POPJ P,				;end file, return out of grovel

MNTHL1:	SKIPE NERRS
	 .VALUE				;we really fucked-up somehow
	LDB T,[141400,,LASDAT]		;rename the file to be last month's report
	ANDI T,717
	TRZE T,100
	 ADDI T,10.
	CAIL T,1
	 CAILE T,12.
	  .VALUE			;random month
	.CALL [	SETZ
		'RENMWO
		MOVEI INCH
		MONTHS(T)
		SETZ ['REPORT] ]
	 .LOSE 1000
	.CALL [	SETZ
		SIXBIT/OPEN/
		[.BAO,,OUCH]
		[SIXBIT/DSK/]
		[SIXBIT/MAIL/]
		[SIXBIT/>/]
		SETZ MALSNM ]
	 JRST MNTHL7
	SETOM OUTBC
	MOVEI TT,[ASCIZFROM-JOB:DRAGON
SENT-BY:PFTHMG-DRAGON
RCPT:(MAGIC-DRAGON-KEEPER)
SUBJECT:That Time Again
TEXT;-1
Don't forget to print out the ]
	PUSHJ P,ASZW
	PUSHJ P,FLNMW
	MOVEI TT,[ASCIZ file and
give it to the appropriate people.
	 - puff
]
	PUSHJ P,ASZW
	PUSHJ P,WBUF
MNTHL7:	.CLOSE OUCH,
	.CLOSE INCH,
	SETZM MNTHLF
	HRRZS CHKALL
	.SUSET [.SAMASK,,[%PICLK]]
	POPJ P,				;end of monthly processing

MONTHS:	'FOOBAR
	SIXBIT/JAN/
	SIXBIT/FEB/
	SIXBIT/MARCH/
	SIXBIT/APRIL/
	SIXBIT/MAY/
	SIXBIT/JUNE/
	SIXBIT/JULY/
	SIXBIT/AUGUST/
	SIXBIT/SEP/
	SIXBIT/OCT/
	SIXBIT/NOV/
	SIXBIT/DEC/

SUBTTL	Hack the LOGOUT TIMES file

;For compatibility with old & name dragons.

LOTUP:	MOVEI Q,CRUFT
	CAML Q,CRUFTF
	 POPJ P,
LOTUP0:	SKIPL CF.UNM(Q)		;old dragon used different sort, fix up
	 JRST LOTUP1		;make Q point to lowest positive
	ADDI Q,CF.LEN
	CAMGE Q,CRUFTF
	 JRST LOTUP0
	MOVE Q,CRUFTF
LOTUP1:	MOVE R,Q		;save pntr to lowest positive
	MOVE J,CRUFTF		;remember haven't done negatives yet
	.CALL [	SETZ
		SIXBIT/OPEN/
		[.BAI,,INCH]
		[SIXBIT/DSK/]
		[SIXBIT/LOGOUT/]
		[SIXBIT/TIMES/]
		SETZ LOTSNM ]
	 POPJ P,		;tough rocks
	.CALL [	SETZ
		SIXBIT/OPEN/
		[.BAO,,OUCH]
		[SIXBIT/DSK/]
		[SIXBIT/_DRGN_/]
		[SIXBIT/TIMES/]
		SETZ LOTSNM ]
	 .LOSE 1400
	PUSHJ P,LOTFILL		;fill LOTNEW
LOTUP5:	MOVE T,[-5,,LOTOLD]	;get entry from file
	.IOT INCH,T
	JUMPL T,LOTEOF
LOTUP4:	MOVE A,LOTOLD		;quick compare
	LSH A,-1
	SUB A,LOTCMP
	JUMPG A,LOTUP6		;LOTNEW goes first
	JUMPL A,LOTUP7		;LOTOLD goes first
	LDB A,[350700,,LOTOLD+1];hmm, check 6th char
	LDB B,[350700,,LOTNEW+1]
	CAMLE B,A
	 JRST LOTUP7		;LOTOLD goes first
	CAMN B,A
	 JRST LOTUP8		;LOTNEW replaces LOTOLD
LOTUP6:	MOVE T,[-5,,LOTNEW]	;Put LOTNEW first
	.IOT OUCH,T
	MOVE D,LOTCMP
LOTUP3:	MOVE E,LOTNEW+1
	PUSHJ P,LOTFILL		;get new LOTNEW
	CAME D,LOTCMP
	 JRST LOTUP4
	XOR E,LOTNEW+1
	TLNN E,774000
	 JRST LOTUP3		;this one the same, get another
	JRST LOTUP4

LOTUP7:	MOVE T,[-5,,LOTOLD]	;put LOTOLD first
	.IOT OUCH,T
	JRST LOTUP5		;and get a new one

LOTUP8:	MOVE T,[LOTNEW,,LOTOLD]	;LOTNEW replaces LOTOLD
	BLT T,LOTOLD+4
	PUSHJ P,LOTFILL		;get new LOTNEW
	JRST LOTUP4

LOTEOF:	MOVSI A,377777		;EOF on old file
	CAME A,LOTCMP		;See if EOF on new also
	 JRST LOTUP8		;No, read through rest of new
	.CLOSE INCH,
	.CALL [	SETZ		;Install new version
		'RENMWO
		MOVEI OUCH
		['LOGOUT]
		SETZ [SIXBIT/TIMES/]]
	 .LOSE 1000
	.CLOSE OUCH,
	POPJ P,

LOTFILL:CAML Q,J
	 JRST LOTFL1		;This section of cruft table exhausted
	MOVE A,CF.JNM(Q)
	CAME A,['HACTRN]	;Only do legitimate human users
	 JRST LOTFL4
	HLRO A,CF.UNM(Q)
	AOJE A,LOTFL4		;Bastard
	MOVE B,[440600,,CF.UNM(Q)]
	MOVE C,[440700,,LOTNEW]
LOTFL0:	ILDB A,B
	ADDI A,40'
	IDPB A,C
	TLNE B,770000
	 JRST LOTFL0
	MOVE A,CF.DAT(Q)	;MMDDYY
	ROT A,14
	MOVEM A,CF.DAT(Q)
	MOVE B,[440600,,CF.DAT(Q)]
	MOVEI TT,6+6
LOTFL3:	ILDB A,B
	ADDI A,40'
	IDPB A,C
	TRNE TT,1
	 IBP C
	SOJG TT,LOTFL3
	MOVE A,LOTNEW		;set up quick compare word
	LSH A,-1
	MOVEM A,LOTCMP'
	ADDI Q,CF.LEN
	POPJ P,

LOTFL1:	JUMPE R,LOTFL2
	MOVEI Q,CRUFT		;do negatives
	MOVE J,R
	MOVEI R,0
	JRST LOTFILL

LOTFL2:	MOVSI A,377777		;make dummy entry
	MOVEM A,LOTCMP
	POPJ P,

LOTFL4:	ADDI Q,CF.LEN		;next
	JRST LOTFILL

SUBTTL	Grovel over accounting file

;Grovel over the accounting file, updating according to the cruft table.

GROVEL:	AOS LINENO		;starting a new line of input
	SETZM UNAMEA		;haven't seen a uname yet
	SETZM UNAMEB		;..
	SETZM UNAMEC		;..
GROVL0:	PUSHJ P,RCH		;get first character in line
	CAILE T,40		;skip if white space or eof
	 JRST GROVL1
	CAIE T,^L		;formfeed is considered to end the randoms section...
	 JRST GVLCPY
	IRPS FIG,,STCTIM STRTIM STSWAP STLODT STLOTM
	 SETZM FIG		;reset subtotals since ending a page
	 TERMIN
ifn $$mcp,[
	SETZM STMCTM
	SETZM STMRTM
];mcp
	SKIPE RANDF		;if ending randoms section, add new randoms here
	 PUSHJ P,SPECR1
	SETZM DRANDF
	WCHI ^L
	JRST GROVL0

GVLCPY:	JUMPL T,CPOPJ		;return from GROVEL if EOF.
	PUSHJ P,WCH		;copy the line over verbatim
	CAIN T,12
	 JRST GROVEL
GVLCP1:	PUSHJ P,RCH
	JRST GVLCPY

GVLFL1:	PUSHJ P,RCH
GVLFLS:	JUMPL T,CPOPJ		;similar to GVLCPY but flush rest of line
	CAIN T,12
	 JRST GROVEL
	jrst gvlfl1

GROVL1:	CAIN T,"-		;lines of dashes introduce special sections
	 JRST SPEC0
	pushj p,untyi		;buffer back the first char of line
	SETZM LVRBEG		;default unspecified stuff to 0
	MOVE T,[LVRBEG,,LVRBEG+1]
	BLT T,LVREND-1
	PUSHJ P,SIXR		;get the uname spec of this line
	pushj p,untyi		;buffer back delimiter
	MOVEM A,UNAMEA
	MOVEM B,UNAMEB
	MOVEM C,UNAMEC
	SKIPE TOTLF
	 JRST GRVL1T
	SKIPE STOTLF
	 JRST GRVL1S
	MOVE E,P
	SKIPN CHKALL
	 PUSHJ P,UCRUFT		;see if this line's getting hacked
	    JRST GROVL2
GRVLNH:	MOVE A,UNAMEA		;nope, just put out the uname
	MOVE B,UNAMEB
	MOVE C,UNAMEC
	PUSHJ P,SIXW
	JRST GVLCP1		;and copy the rest verbatim

GROVL2:	MOVE P,E		;this line's getting hacked, gobble it down
GROVL3:	PUSHJ P,SIGCH
	CAIG T,40
	 JRST GROVL9		;end of line
	CAIE T,"j
	CAIN T,"J
	 JRST GROVL4
	CAIE T,"a
	CAIN T,"A
	 JRST GROVL5
	CAIE T,"h
	CAIN T,"H
	 JRST GROVL6
;drops through
;drops in
	pushj p,untyi
	move tt,column		;column before delimiter
	movem tt,ctimep
	pushj p,numf
	 clbfil 0,Garbage character where connect-time expected.
ifn $$MCP,pushj p,hread		;get connect time from hours.xx
ife $$MCP,pushj p,dhmsr		;get connect time from D!HH:MM:SS
	movem a,ctime
	pushj p,sigch
	caig t,40
	 jrst grovl9		;end of line
	pushj p,untyi
	move tt,column
	movem tt,rtimep
	pushj p,numf
	 clbfil 0,Garbage character where run-time expected.
ifn $$MCP,pushj p,hread		;get run time from HOURS.xxx
ife $$MCP,pushj p,dhmsr		;get run time from D!HH:MM:SS
	movem a,rtime
	pushj p,sigch
	caig t,40
	 jrst grovl9		;end of line
	pushj p,untyi
	move tt,column
	movem tt,swapsp
	pushj p,numf
	 clbfil 0,Garbage character where swap-in count expected.
	pushj p,numr		;get number of swaps
	movem a,swaps
	pushj p,sigch
	caig t,40
	 jrst grovl9		;end of line
ifn $$MCP,[
	pushj p,untyi
	move tt,column
	movem tt,mctimp		;remember where we found this
	pushj p,numf		;check for bad char
	  clbfil 0,Garbage character where MACSYMA connect time expected.
	pushj p,hread		;read the time
	movem a,mctime		;and remember it
	pushj p,sigch		;get next significant char
	caig t,40		;end of line?
	  jrst grovl9		;  yes
	pushj p,untyi		;buffer it back
	move tt,column
	movem tt,mrtimp		;remember where we found this
	pushj p,numf		;check it for bad
	  clbfil 0,Garbage character where MACSYMA run time expected.
	pushj p,hread		;read the run time
	movem a,mrtime		;remember it
	pushj p,sigch		;and get the next significant char
	caig t,40		;end of line?
	  jrst grovl9		;  yes
] ; END IFN $$MCP,
	pushj p,untyi		;buffer it back
	move tt,column
	movem tt,lodatp
	pushj p,numf
	 clbfil 0,Garbage character where logout date expected.
	pushj p,datr		;get logout date
	movem a,lodat
	pushj p,sigch
	caig t,40
	 jrst grovl9		;end of line
	pushj p,untyi
	move tt,column
	movem tt,lotimp
	pushj p,numf
	 clbfil 0,Garbage character where logout time expected.
	pushj p,timr		;get logout time
	movem a,lotim
grvl9a:	pushj p,sigch		;get char that ends line
grovl9:	caie t,15
	 clbfil 0,Garbage character.
	pushj p,rch
	caie t,12
	  pushj p,untyi
	pushj p,coldef		;default columns of unspecified data.
	SKIPE MNTHLF
	 JRST MNTHL2		;if monthly processing, zero usage before writing back
	SKIPE TOTLF
	 JRST SPECT2		;This is totals line, go add in all crufts.
	SKIPE STOTLF
	 JRST SPECS2		;Process subtotals
	MOVE A,UNAMEA
	MOVE B,UNAMEB
	MOVE C,UNAMEC
	PUSHJ P,UCRUFT
	    UPDUSR		;update this line from cruft
	PUSHJ P,WBACK		;write it back out
	JRST GROVEL		;go do next line

;Skip if char in T is a numeric digit.

NUMF:	CAIL T,"0
	 CAILE T,"9
	  POPJ P,
	JRST POPJ1

GROVL4:	pushj p,untyi
	PUSHJ P,SIXR
	CAMN A,[SIXBIT/JOB/]
	 CAIE T,"=
	  CLBFIL 0,Bad JOB= Spec.
	PUSHJ P,SIXR
	pushj p,untyi
	MOVEM A,JNAMEA
	MOVEM B,JNAMEB
	MOVEM C,JNAMEC
	SETOM JNAMEF
	JUMPN A,GROVL3
	JUMPN B,GROVL3
	JUMPN C,GROVL3
	CLBFIL 0,Null JOB= Spec.

GROVL5:	pushj p,untyi
	PUSHJ P,SIXR
	CAMN A,[SIXBIT/ACCT/]
	 CAIE T,"=
	  CLBFIL 0,Bad ACCT= Spec.
	PUSHJ P,SIXR
	pushj p,untyi
	MOVEM A,ACCNTA
	MOVEM B,ACCNTB
	MOVEM C,ACCNTC
	SETOM ACCNTF
	JUMPN A,GROVL3
	JUMPN B,GROVL3
	JUMPN C,GROVL3
	CLBFIL 0,Null ACCT= Spec.

GROVL6:	pushj p,untyi
	PUSHJ P,SIXR
	CAMN A,[SIXBIT/HOURS/]
	 CAIE T,"=
	  CLBFIL 0,Bad HOURS= Spec.
	PUSHJ P,NUMR
	MOVEM A,FHOUR
	PUSHJ P,RCH
	CAIE T,"-
	 CLBFIL 0,HOURS= Spec not followed by two numbers.
	PUSHJ P,NUMR
	MOVEM A,LHOUR
	SETOM HOURSF
	JRST GROVL3

;Default columns of unspecified items.
;Clobber T.

IFN $$MCP,[
COLDEF:	MOVEI T,21.		;default starting places for unspecified fields
	IRPS FIG,,[CTIMEP RTIMEP SWAPSP mctimp mrtimp LODATP
	    ]WID,,[10.    11.    7.     10.     11.     10.   ]
	 SKIPE FIXIND
	  SETZM FIG
	 SKIPE FIG
	  SKIPA T,FIG
	   MOVEM T,FIG
	 ADDI T,WID
	 TERMIN
	POPJ P,
] ; END IFN $$MCP,
IFE $$MCP,[
COLDEF:	MOVEI T,21.		;default starting places for unspecified fields
	IRPS FIG,,[CTIMEP RTIMEP SWAPSP LODATP LOTIMP
	    ]WID,,[13.    10.    7.     10.    10.   ]
	 SKIPE FIXIND
	  SETZM FIG
	 SKIPE FIG
	  SKIPA T,FIG
	   MOVEM T,FIG
	 ADDI T,WID
	 TERMIN
	POPJ P,
] ; END IFE $$MCP,


SUBTTL	Write back a line

wback:	setzm column
	MOVE A,UNAMEA
	jumpe a,cpopj		   ;don't write wild-care UNAMEs
	MOVE B,UNAMEB
	MOVE C,UNAMEC
	PUSHJ P,SIXW
	SKIPN JNAMEF
	 JRST WBACK1
	MOVEI TT,[ASCIZ/  JOB=/]
	PUSHJ P,ASZW
	MOVE A,JNAMEA
	MOVE B,JNAMEB
	MOVE C,JNAMEC
	PUSHJ P,SIXW
WBACK1:	SKIPN ACCNTF
	 JRST WBACK2
	MOVEI TT,[ASCIZ/  ACCT=/]
	PUSHJ P,ASZW
	MOVE A,ACCNTA
	MOVE B,ACCNTB
	MOVE C,ACCNTC
	PUSHJ P,SIXW
WBACK2:	SKIPN HOURSF
	 JRST WBACK3
	MOVEI TT,[ASCIZ/  HOURS=/]
	PUSHJ P,ASZW
	MOVE A,FHOUR
	PUSHJ P,NUMW
	WCHI "-
	MOVE A,LHOUR
	PUSHJ P,NUMW
WBACK3:	SKIPN CTIME		;if all 0, don't write any figures
	 SKIPE RTIME
	  JRST WBACK4
	SKIPN LODAT
	 SKIPE LOTIM
	  JRST WBACK4
ifn $$mcp,[
	skipn mctime
	  skipe mrtime
	    jrst wback4
];mcp
	SKIPN SWAPS
	 JRST CRLF
wback4:	move t,ctimep
	PUSHJ P,COLADJ
	MOVE A,CTIME
	SKIPE CHKALL
	 ADDM A,STCTIM
ifn $$MCP,pushj p,hwrit2	;connect time in hours.xx
ife $$MCP,pushj p,dhmsw		;connect time in D!HH:MM:SS
	move t,rtimep
	PUSHJ P,COLADJ
	MOVE A,RTIME
	SKIPE CHKALL
	 ADDM A,STRTIM
ifn $$MCP,pushj p,hwrit3	;run time in hours.xx
ife $$MCP,pushj p,dhmsw		;run time in D!HH:MM:SS
	move t,swapsp
	PUSHJ P,COLADJ
	MOVE A,SWAPS
	SKIPE CHKALL
	 ADDM A,STSWAP
	PUSHJ P,NUMW

ifn $$MCP,[
	move t,mctimp		;get column to start MACSYMA connect time in
	pushj p,coladj		;and adjust our position
	move a,mctime		;get the user's MACSYMA connect time
	skipe chkall		;if we're counting them
	  addm a,stmctm		;  add it into the subtotal
	pushj p,hwrit2		;write it with 2 post-decimal digits
	move t,mrtimp		;get column to start MACSYMA run time in
	pushj p,coladj		;and adjust our posititon
	move a,mrtime		;get the user's MACSYMA run time
	skipe chkall		;if we're taking subtotals
	  addm a,stmrtm		;  count this in the subtotal
	pushj p,hwrit3		;write it with 3 post-decimal digits
] ; END IFN $$MCP,

	move t,lodatp
	PUSHJ P,COLADJ
	SKIPE A,LODAT
	 CAMN A,[-1]
	   JRST WBACK5
	SKIPE CHKALL
	 MOVEM A,STLODT
	PUSHJ P,DATW
WBACK5:
ife $$MCP,[
	move t,lotimp
	PUSHJ P,COLADJ
	SKIPE A,LOTIM
	 CAMN A,[-1]
	  JRST CRLF
	SKIPE CHKALL
	 MOVEM A,STLOTM
	PUSHJ P,TIMW
] ;END IFE $$MCP,

CRLF:	WCHI 15
	WCHI 12
	POPJ P,

SUBTTL	Special sections

;Here from GROVEL when row of dashes encountered.  Begin special section.

SPEC0:	PUSHJ P,RCH		;find non-dash
	CAIE T,40
	 CAIN T,"-
	  JRST SPEC0
	MOVE Q,T		;save section type letter
	CAIL Q,"a
	 SUBI Q,40
	SKIPE TOTLF
	 CLBFIL 0,Totals section does not contain TOTALS line.
	SKIPE STOTLF
	 CLBFIL 0,Subtotal screw.
	SKIPE RANDF		;if ending randoms section, add new randoms
	 PUSHJ P,SPECR1
	SETZM DRANDF
SPEC1:	PUSHJ P,RCH		;gobble down rest of line
	CAIE T,12
	 JUMPGE T,SPEC1
	MOVSI I,-LSPECT		;look up section type in table
	LDB T,[350700,,@SPECNM(I)]
	CAME T,Q
	 AOBJN I,.-2
	SKIPL I
	 CLBFIL 0,Row of dashes of unrecognized type.
	MOVE TT,SPECNM(I)	;compute length of special name
	HRLI TT,440700
	MOVNI A,1		;A will get (- (+ 2 length))
SPEC1A:	ILDB T,TT
	SOS A
	JUMPN T,SPEC1A
	ADDI A,68.		;number of dashes to fill in with
	LSH A,-1
	MOVE T,A		;copy dashes into output file
	WCHI "-
	SOJG T,.-1
	WCHI 40
	MOVE TT,SPECNM(I)
	PUSHJ P,ASZW
	WCHI 40
	MOVE T,A
	WCHI "-
	SOJG T,.-1
	WCHI 15
	WCHI 12
	SKIPN MNTHLF
	 JRST @SPECAD(I)	;dispatch to appropriate handler
	JRST @SPECMO(I)

SPECNM:	[ASCIZ/RANDOMS/]	;names of special sections
	[ASCIZ/TOTALS/]
	[ASCIZ/PAGE SUBTOTALS/]
LSPECT==.-SPECNM

SPECAD:	SPECRN			;normal dispatch for special section
	SPECTT
	SPECST
IFN .-SPECAD-LSPECT, .ERR SPECAD DISAGREES WITH SPECNM

SPECMO:	SPECMR			;dispatch for during monthly processing
	SPECTT
	GROVEL
IFN .-SPECMO-LSPECT, .ERR SPECMO DISAGREES WITH SPECNM

SPECMR:	SETOM DRANDF		;delete useless randoms
	JRST GROVEL

;Random users begin here.  What we do is put any otherwise
;unaccounted for users at the END of this section.

SPECRN:	SETOM RANDF
	JRST GROVEL

;Call here when end of randoms section is reached.  Preserves only Q.

SPECR1:	PUSH P,Q
	SETZM RANDF
	JRST SPECR9

SPECR2:	SKIPN CF.VAL(Q)
	 JRST SPECR7
	SETZM LVRBEG		;Found a user not yet accounted for.
	MOVE T,[LVRBEG,,LVRBEG+1]
	BLT T,LVREND-1		;So fake up an accounting entry for that user.
	MOVE T,CF.UNM(Q)
	MOVEM T,UNAMEA
	MOVE T,CF.JNM(Q)
	MOVEM T,JNAMEA
	PUSHJ P,NRJNMP
	 SETOM JNAMEF		;unusual jname, record it
	PUSHJ P,COLDEF		;using default columns
SPECR3:	PUSHJ P,UPDUS3		;update fake accounting from the data
SPECR4:	ADDI Q,CF.LEN		;look for more cruft for the same user
	CAML Q,CRUFTF
	 JRST SPECR8
	MOVE T,CF.UNM(Q)
	SKIPE CF.VAL(Q)
	 CAME T,UNAMEA
	  JRST SPECR4
	MOVE T,CF.JNM(Q)
	SKIPN JNAMEF
	 JRST [	PUSHJ P,NRJNMP
		 JRST SPECR4
		JRST SPECR3 ]
	CAME T,JNAMEA
	 JRST SPECR4
	JRST SPECR3		;found some, gobble it down too

SPECR8:	PUSHJ P,WBACK		;write it out
SPECR9:	MOVEI Q,CRUFT-CF.LEN	;Look for more randoms.
SPECR7:	ADDI Q,CF.LEN
	CAMGE Q,CRUFTF
	 JRST SPECR2
	POP P,Q
	POPJ P,

;Skip if T is a prosaic jname.  Clobber T.
NRJNMP:	CAMN T,['HACTRN]
	 JRST POPJ1
	AND T,[777777,,767070]
	CAMN T,['JOB.00]
	 JRST POPJ1
	POPJ P,

;Here for page subtotals.  Next line is like a totals line
;but only stuff on this page is updated into it.

SPECST:	SETOM STOTLF		;tell groveler to process a little differently
	JRST GROVEL

GRVL1S:	CAME A,['TOTALS]
	 CLBFIL 0,TOTALS line missing after PAGE SUBTOTALS header.
	JRST GROVL3

SPECS2:
ifn $$mcp,[
	IRPS FIG1,OP,[CTIME+RTIME+SWAPS+MCTIME+MRTIME+LODAT LOTIM]FIG2,,[STCTIM STRTIM STSWAP STMCTM STMRTM STLODT STLOTM]
	 MOVE T,FIG2		;increase page subtotals
	 SKIPE CHKALL
	  SETZM FIG1		;recalculating
	 IFSE OP,+, ADDM T,FIG1
	 .ELSE [    SKIPE T
	   	     MOVEM T,FIG1 ]
	 TERMIN
];$$mcp
ife $$mcp,[
	IRPS FIG1,OP,[CTIME+RTIME+SWAPS+LODAT LOTIM]FIG2,,[STCTIM STRTIM STSWAP STLODT STLOTM]
	 MOVE T,FIG2		;increase page subtotals
	 SKIPE CHKALL
	  SETZM FIG1		;recalculating
	 IFSE OP,+, ADDM T,FIG1
	 .ELSE [    SKIPE T
	   	     MOVEM T,FIG1 ]
	 TERMIN
];$$mcp
	PUSHJ P,WBACK		;write it back out
ifn $$mcp,[
	IRPS FIG1,,[GTCTIM GTRTIM GTSWAP GTMCTM GTMRTM]FIG2,,[CTIME RTIME SWAPS MCTIME MRTIME]
	 MOVE T,FIG2		;update grand totals
	 ADDM T,FIG1
	 TERMIN
];mcp
ife $$mcp,[
	IRPS FIG1,,[GTCTIM GTRTIM GTSWAP]FIG2,,[CTIME RTIME SWAPS]
	 MOVE T,FIG2		;update grand totals
	 ADDM T,FIG1
	 TERMIN
];mcp
	SETZM STOTLF		;end subtotal processing
	JRST GROVEL		;continue normal grovelling

;Come here to generate totals section.
;First comes a line for user "TOTALS" which totals all the usage.
;then comes the STATS line which gets statistics on PUFF's operation,
;then the system uptime report

SPECTT:	SETOM TOTLF		;flag for totals mode.
	JRST GROVEL

GRVL1T:	SKIPL TOTLF
	 JRST GRVL2T
	CAME A,['TOTALS]
	 CLBFIL 0,Totals section does not begin with TOTALS line.
	JRST GROVL3

SPECT2:	MOVEI Q,CRUFT		;Returns here after gobbling existing totals.
	MOVE T,LODAT		;Save date file last updated.
	MOVEM T,LASDAT
SPECT3:	CAML Q,CRUFTF
	 JRST SPECT4
	PUSHJ P,UPDUS3		;Add in this piece of cruft
	ADDI Q,CF.LEN
	JRST SPECT3

SPECT4:	SKIPN CHKALL		;If recalculating total
	 JRST SPCT4A
	MOVE T,GTCTIM
	MOVEM T,CTIME
	MOVE T,GTRTIM
	MOVEM T,RTIME
	MOVE T,GTSWAP
	MOVEM T,SWAPS
ifn $$mcp,[
	move t,gtmctm
	movem t,mctime
	move t,gtmrtm
	movem t,mrtime
];$$mcp
SPCT4A:	PUSHJ P,RDATIM		;T gets time, TT gets date
	SKIPLE T
	 MOVEM T,LOTIM		;set date/time file last updated.
	JUMPLE TT,SPECT5
	 MOVEM TT,LODAT
	SKIPG T,LASDAT		;compare this date against previous
	 JRST SPECT5		;if known
	XOR T,TT		;see what differs
	TLNE T,777700
	 SETOM YEARF
	TDNE T,[77770000]
	 SETOM MONTHF
	TRNE T,7777
	 SETOM DAYF
SPECT5:	PUSHJ P,WBACK		;write it out
	MOVNS TOTLF		;set TOTLF to 1 to process uptime cruft
	JRST GROVEL

;Here for a line of uptime report.  Should be UP, DOWN, or OK

GRVL2T:	CAME A,[SIXBIT/UP/]	;UP and DOWN we just copy over
	 CAMN A,[SIXBIT/DOWN/]
	  JRST GRVLNH
ifn $$STAT,[
	camn a,[sixbit /STATS/]	;PUFF's internal stats
	  jrst grvlst		;  go handle them
];  END $$STAT,

	CAME A,[SIXBIT/OK/]
	 CLBFIL 0,Word other than UP, DOWN, or OK in uptime report.
	PUSHJ P,SIGCH
	pushj p,untyi
	PUSHJ P,DATR
	MOVEM A,NOKDAT
	PUSHJ P,SIGCH
	pushj p,untyi
	PUSHJ P,TIMR
	MOVEM A,NOKTIM
	PUSHJ P,SIGCH
	pushj p,untyi
ifn $$MCP,pushj p,hread		;get run time from HOURS.xxx
ife $$MCP,pushj p,dhmsr		;get run time from D!HH:MM:SS
	MOVEM A,NOKTMU
	PUSHJ P,RDATIM		;TT gets date, T gets time
	EXCH TT,OKDAT
	EXCH T,OKTIM
	CAMN T,NOKTIM
	 CAME TT,NOKDAT
	  JRST WASDWN
STILUP:	.RDTIME T,
	MOVEM T,OKTMUP
	MOVEI TT,[ASCIZ/OK   /]
	MOVEI D,OKDAT
	PUSHJ P,UPTRWW
	JRST GVLFL1


WASDWN:	MOVEI TT,[ASCIZ/DOWN /]
	MOVEI D,NOKDAT
	PUSHJ P,UPTRWW
	MOVE D,NOKDAT
	PUSHJ P,6DTCNV
	MOVE B,A
	MOVE D,OKDAT
	PUSHJ P,6DTCNV
	SUBM A,B		;B:=NUMBER OF DAYS DOWN
	MOVE D,NOKTIM
	PUSHJ P,6TMCNV
	MOVE C,A
	MOVE D,OKTIM
	PUSHJ P,6TMCNV
	SUB A,C			;A:=NUMBER OF SECONDS*30.

	IMULI B,24.*60.*60.*30.

	ADD A,B	
	MOVEM A,OKTMUP		;STORE LENGTH OF TIME DOWN
	MOVEI TT,[ASCIZ/UP   /]
	MOVEI D,OKDAT
	PUSHJ P,UPTRWW
	JRST STILUP

UPTRWW:	SETZM COLUMN
	PUSHJ P,ASZW		;WRITE LINE TO UPTIME REPORT
	MOVE A,OKDAT-OKDAT(D)
	PUSHJ P,DATW
	WCHI 40
	MOVE A,OKTIM-OKDAT(D)
	PUSHJ P,TIMW
	WCHI 40
	MOVE A,OKTMUP-OKDAT(D)
ifn $$MCP,pushj p,hwrit2	;write out uptime as HOURS.xx
ife $$MCP,pushj p,dhmsw		;write out uptime as D!HH:MM:SS
	MOVEI T,40.		;ALIGN FOR REASON
	PUSHJ P,COLADJ
	JRST CRLF

ifn $$STAT,[
;;; update STATS line
;;; STATS line looks like:
;STATS fups crftys[crftym]coraos runt[nomax normax newfls newcnt oldfls oldcnt]
;;; where items in [...]'s exist only if $$MCP is on
;;; FUPS is # of file updates
;;; CRFTYS is # of dmnpsh logout's processed (# therefore pushing cruft)
;;; CRFTYM is # of MACSYMA's cruft is pushed for
;;; RUNTIME is total runtime consumed by PUFF
;;; CORAOS is total times core has been grown
;;; NOMAX  is total times no MACSYMA's seen
;;; NORMAX  is total times no MACSYMA's seen running
;;; NEWFLS is total times NEWFLS is called
;;; NEWCNT is total jobs invalidated by NEWFLS
;;; OLDFLS is total times OLDFLS is called
;;; OLDCNT is total jobs invalidated by OLDFLS

grvlst:	pushj p,sigch		;find significant char
	pushj p,untyi		;buffer back the char
	move t,column		;remember our column as well
	movem t,updtcp		;so we can write it back in the same place
	pushj p,numr		;get count of file updates
	aos a			;count this update
	movem a,updtct		;remember it
	movei b,crftys		;# of crufties pushed by logouts
	pushj p,addct0

ifn $$MCP,[
	movei b,crftym		;count number of crufties pushed by MACSYMA's
	pushj p,addct0
]; END ifn $$MCP,

	movei b,coraos		;update the count of core growings
	pushj p,addct0

	move a,lrunt		;back up
	movem a,orunt		;what we wrote out last time
	pushj p,sigch		;next significant char
	pushj p,untyi		;buffer it back
	move t,column		;remember where we got it
	movem t,lruntp
ifn $$MCP,pushj p,hread		;get run time from HOURS.xxx
ife $$MCP,pushj p,dhmsr		;get run time from D!HH:MM:SS
	movem a,lrunt
ifn $$MCP,[
	pushj p,sigch		;find next entry
	pushj p,untyi		;buffer back the char
	move t,column		;remember where we got it
	movem t,nomaxp
	pushj p,hread		;read the number
	move b,nomax		;get the count
	imuli b,60.*6.*3.	;convert to 1/100'ths of hours
	add a,b
	movem a,nomaxc		;add in the count
	setzm nomax		;restart count

	pushj p,sigch		;find next entry
	pushj p,untyi		;buffer back the char
	move t,column		;remember where on the line it was found
	movem t,normxp
	pushj p,hread		;read the number
	move b,normax		;get the count
	imuli b,60.*6.*3.	;convert to 1/100'ths of hours
	add a,b			;add in the count
	movem a,normxc		;remember the count for later
	setzm normax		;restart count

	movei b,flsnew		;update the count of NEWFLS's we've done
	pushj p,addct0
	movei b,cntnew		;update the count of MACSYMA's NEWFLS'ed
	pushj p,addct0
	movei b,flsold		;update the count of OLDFLS's we've done
	pushj p,addct0
	movei b,cntold		;update the count of MACSYMA's OLDFLS'ed
	pushj p,addct0
]; end $$IFN $$MCP

	setzm column		;start at the LHS
	movei tt,[asciz /STATS/]
	pushj p,aszw		;write it back out
	move t,updtcp		;get where we got the info from
	pushj p,coladj		;adjust our column to win
	move a,updtct		;get our count of updates
	pushj p,numw		;write out new count
	movei b,crftys		;# of crufties pushed by logouts
	pushj p,addct1

ifn $$MCP,[
	movei b,crftym		;count number of crufties pushed by MACSYMA's
	pushj p,addct1
];  END IFN $$MCP,

	movei b,coraos		;update the count of core growings
	pushj p,addct1

	.suset [.rrunt,,a]	;get our runtime
	sub a,orunt		;uncount the old
	addi a,4166.		;round,
	idivi a,8333.		;converting to seconds*30.
	add a,lrunt		;count it in with what was there
	move t,a		;get copy to round off
	addi t,30.*1800./1000.	;round it off
	idivi t,30.*3600./1000.	;just like printing and re-reading it will
	imul t,[<30.*3600./1000.>*8333.]  ;so that we don't accumulate error.
	movem t,orunt
	move t,lruntp		;get position to write this cruft in
	pushj p,coladj
ifn $$MCP,pushj p,hwrit3	;write with 3 decimal places precision
ife $$MCP,pushj p,dhmsw		;run time in D!HH:MM:SS

ifn $$MCP,[
	move t,nomaxp
	pushj p,coladj
	move a,nomaxc		;no-macsyma connect-time count
	pushj p,hwrit2		;record updated count

	move t,normxp
	pushj p,coladj
	move a,normxc		;no-macsyma run-time count
	pushj p,hwrit2		;record updated count

	movei b,flsnew		;update the count of NEWFLS's we've done
	pushj p,addct1
	movei b,cntnew		;update the count of MACSYMA's NEWFLS'ed
	pushj p,addct1
	movei b,flsold		;update the count of OLDFLS's we've done
	pushj p,addct1
	movei b,cntold		;update the count of MACSYMA's OLDFLS'ed
	pushj p,addct1
]; END IFN $$MCP,

	pushj p,crlf		;terpri
	jrst gvlfl1

addct0:	pushj p,sigch		;find next entry
	pushj p,untyi		;buffer back the char
	move t,column
	movem t,1(b)		;remember where to put it on the line
	pushj p,numr		;read the number
	addm a,(b)		;add in the count
	popj p,			;return

addct1:	move t,1(b)		;get column to hack
	pushj p,coladj		;adjust the output column now
	move a,(b)		;get the new count
	setzm (b)		;restart count
	jrst numw		;record updated count
]; END IFN $$STAT,

6DTCNV:	PUSHJ P,6XXMNG		;DAYS SINCE 1 JAN 1901
	SOS A,T
	IMULI A,365.
	IDIVI T,4
	ADD A,T
	PUSHJ P,6XXMNG
	ADD A,(T)6DTMNL-1
	PUSHJ P,6XXMNG
	ADDI A,-1(T)
	POPJ P,

6DTMNL:	ZZ==0
	IRPS L,,31 28 31 30 31 30 31 31 30 31 30 31
	 ZZ
	 ZZ==ZZ+.RADIX 10.,L
	 TERMIN

6TMCNV:	PUSHJ P,6XXMNG		;SECONDS SINCE MIDNIGHT
	MOVE A,T
	IMULI A,60.
	PUSHJ P,6XXMNG
	ADD A,T
	IMULI A,60.
	PUSHJ P,6XXMNG
	ADD A,T
	POPJ P,

6XXMNG:	LDB T,[360400,,D]
	LDB TT,[300400,,D]
	IMULI T,10.
	ADD T,TT
	LSH D,14
	POPJ P,

SUBTTL	Update user data

;Update user info from piece of cruft pointed to by Q.

UPDUSR:	SKIPN CF.VAL(Q)
	 POPJ P,		;already accounted for
	SKIPN JNAMEF
	 JRST UPDUS1
	MOVE A,JNAMEA		;verify that JNAME matches
	MOVE B,JNAMEB
	MOVE C,JNAMEC
	MOVE T,CF.JNM(Q)
	PUSHJ P,MATCH
	 POPJ P,		;no match
UPDUS1:	SKIPN ACCNTF
	 JRST UPDUS2
	MOVE A,ACCNTA		;verify that account matches
	MOVE B,ACCNTB
	MOVE C,ACCNTC
	MOVE T,CF.TNM(Q)
	PUSHJ P,MATCH
	 POPJ P,
UPDUS2:	SKIPN HOURSF
	 JRST UPDUS3
	MOVE T,CF.HOR(Q)	;verify that hour matches
	CAML T,FHOUR
	 CAMLE T,LHOUR
	  POPJ P,
UPDUS3:	MOVE T,CF.CTM(Q)	;update the usage
	ADDM T,CTIME
	ADDM T,STCTIM		;also update page subtotals
	MOVE T,CF.RTM(Q)
	ADDM T,RTIME
	ADDM T,STRTIM
	MOVE T,CF.SWI(Q)
	ADDM T,SWAPS
	ADDM T,STSWAP
ifn $$mcp,[
	move t,cf.mct(q)	;update the macsyma connect time
	addm t,mctime
	addm t,stmctm		;update the subtotal
	move t,cf.mrt(q)	;update the macsyma run time
	addm t,mrtime
	addm t,stmrtm		;update the subtotal
];mcp
	MOVE T,CF.DAT(Q)
	MOVEM T,LODAT
	MOVEM T,STLODT
	MOVE T,CF.TIM(Q)
	MOVEM T,LOTIM
	MOVEM T,STLOTM
	SETZM CF.VAL(Q)		;don't count this one again
	POPJ P,

;Skip if sixbit in T matches spec in A, B, C.  Clobbers A, B, C.

MATCH:	ANDCB B,C		;mask for literally specd chars
	XOR A,T			;xor the sixbits
	TDNE A,B
	 POPJ P,		;simply spec'd chars don't match
	JUMPE C,POPJ1		;match!
	AND C,T			;chars that gotta be digits or space
REPEAT 6,[
	SETZ B,
	LSHC B,6
	JUMPE B,.+4
	 CAIL B,'0
	  CAILE B,'9
	   POPJ P,		;no match
]
	JRST POPJ1		;matches

SUBTTL	Matching Routines

;Routine to match masked sixbit (in A, B, C) against cruft table.
;For each match, routine after call is called with Q -> piece of cruft.
;Clobbers A, B, Q, R, T, TT plus whatever that routine clobbers.

UCRUFT:	JUMPN B,UCRUF0		;has stars
	JUMPN C,UCRUF0		;has sharps
	JRST FUSER		;easy case

UCRUF0:	MOVEI Q,CRUFT		;Q -> pieces of cruft
	ANDCB B,C		;mask for chars literally specd
	MOVSI T,(@)
	IORM T,(P)
	INSIRP PUSH P,A B C Q
UCRUF1:	MOVE A,-3(P)
	XOR A,CF.UNM(Q)
	TDNE A,-2(P)
	 JRST UCRUF4		;simply spec'd chars don't match
	SKIPN B,-1(P)
	 JRST UCRUF3		;match!
	AND B,CF.UNM(Q)		;chars that gotta be digits or space
REPEAT 6,[
	SETZ A,
	LSHC A,6
	JUMPE A,.+4
	 CAIL A,'0
	  CAILE A,'9
	   JRST UCRUF4		;no match
]
UCRUF3:	PUSHJ P,@-4(P)		;matches, call spec'd routine
UCRUF4:	MOVEI Q,CF.LEN		;advance to next piece of cruft
	ADDB Q,(P)
	CAMGE Q,CRUFTF
	 JRST UCRUF1
	SUB P,[4,,4]		;done
	JRST POPJ1

;Routine to do binary search of cruft table for uname in A.
;Calls routine after call with Q -> each matching piece of cruft.
;Clobbers Q, R, T, TT, plus whatever that routine clobbers.

FUSER:	MOVEI T,CRUFT		;T lower limit
	MOVE TT,CRUFTF		;TT upper limit
FUSER0:	MOVE Q,TT		;Q pointer halfway between
	SUB Q,T
	JUMPE Q,POPJ1		;not found
	IDIVI Q,CF.LEN*2	;ensure proper alignment (clobber R)
	IMULI Q,CF.LEN
	ADD Q,T
	CAMN A,CF.UNM(Q)
	 JRST FUSER2		;got it.
	CAML A,CF.UNM(Q)
	 JRST FUSER1
	MOVE TT,Q		;go down
	JRST FUSER0

FUSER1:	MOVEI T,CF.LEN(Q)	;go up
	JRST FUSER0

FUSER2:	CAIE Q,CRUFT		;Found one cruft entry for this user.
	 CAME A,CF.UNM-CF.LEN(Q) ;Look back to see if there are more.
	  JRST FUSER3
	SUBI Q,CF.LEN
	JRST FUSER2

FUSER3:	MOVSI T,(@)		;Call spec'd routine for all entries for this user.
	IORM T,(P)
	PUSH P,A
	PUSH P,Q
FUSER4:	PUSHJ P,@-2(P)
	MOVEI Q,CF.LEN
	ADDB Q,(P)
	CAML Q,CRUFTF
	 JRST FUSER5
	MOVE A,-1(P)
	CAMN A,CF.UNM(Q)
	 JRST FUSER4
FUSER5:	SUB P,[2,,2]
POPJ1:	AOS (P)
CPOPJ:	POPJ P,

SUBTTL	I/O Routines

;Read character into T.  Returns -1 at EOF.  Keeps track of column position.

; UNTYI unreads the last character, keeping track of column position
unrch:
untyi:	movem t,bbc		;buffer back the character
	move t,ocolum		;get the old column number
	movem t,column		;it's current now.
	move t,bbc		;just in case anyone expects the char to remain
	popj p,

rch:	move t,column		;remember this position in case of UNTYI's
	movem t,ocolum
	skipl t,bbc		;check for buffered back character
	 jrst [	setom bbc ? jrst rch3]	;get it
	sosge inbc
	  jrst rch1		;refill input buffer
rch2:	ildb t,inbp
rch3:	aos column
	caie t,^M		;CR?
	 cain t,^J		;LF?
	  setzm column		;  End of line
	CAIE T,^I
	 POPJ P,
	MOVEI T,7	;tabs
	ADD T,COLUMN
	TRZ T,7
	MOVEM T,COLUMN
	MOVEI T,^I
	POPJ P,

RCH1:	MOVE T,[-INBFL,,INBUF]
	.IOT INCH,T
	MOVEI T,-INBUF(T)	;number of words read	
	IMULI T,5		;number of characters
	SOJL T,CPOPJ		;if read nothing, eof.
	MOVEM T,INBC		;otherwise, set up count
	MOVE T,[440700,,INBUF]	;and pointer
	MOVEM T,INBP
	JRST RCH2		;and return first character

;Write character from T

WCH:	SOSGE OUTBC
	 JRST WCH1		;no room in output buffer
	IDPB T,OUTBP
	AOS COLUMN
	caie t,^I		;tab?
	  popj p,		;  no, that's all.
	movei t,7		;yes, add another 7 positions
	add t,column		;to the position
	trz t,7			;and round down
	movem t,column		;to nearest tab stop
	movei t,^I		;in case someone really wants it.
	popj p,

WCH1:	PUSH P,T
	AOSL OUTBC		;skip if no previous buffer
	 PUSHJ P,WBUF		;dump previous buffer
	MOVEI T,5*OUTBFL
	MOVEM T,OUTBC
	MOVE T,[440700,,OUTBUF]
	MOVEM T,OUTBP
	POP P,T
	JRST WCH

;Write out the output buffer. Clobbers T.

WBUF:	MOVE T,OUTBP		;pad to end of word
	TLNN T,760000
	 JRST WBUF1
	MOVEI T,^C
	PUSHJ P,WCH
	JRST WBUF

WBUF1:	HRLOI T,-OUTBUF(T)
	EQVI T,OUTBUF
	.IOT OUCH,T
	POPJ P,


;Routine to read sixbit.
;A gets sixbit, B gets *-mask, C gets #-mask, T gets delimiter.
;Clobbers H, I, J.

SIXR:	SETZB B,C
	SETZ A,
	MOVE H,[440600,,A]
	MOVE I,[440600,,B]
	MOVE J,[440600,,C]	
SIXR0:	PUSHJ P,RCH
	CAIN T,"/
	 JRST SIXR1		;slash quotes
	CAIN T,"*
	 JRST SIXR2		;star matches any
	CAIN T,"#
	 JRST SIXR3		;sharp matches digits
	CAIE T,"_		;otherwise only letters, digits, underscore, dot allowed
	 CAIN T,".
	  JRST SIXR4
	CAIL T,"0
	 CAILE T,"z
	  JRST SIXR9
	CAIL T,"a
	 JRST SIXR5
	CAILE T,"Z
	 JRST SIXR9
	CAILE T,"9
	 CAIL T,"A
	  JRST SIXR4
SIXR9:	CAILE T,40
	 CAIN T,"=
	  CAIA
	   CLBFIL 1,Illegal character in sixbit name.
	JUMPN A,CPOPJ		;check for just a star
	JUMPN C,CPOPJ
	CAMN B,[770000,,000000]
	 SETO B,		;which should be equivalent to six stars
	POPJ P,			;done

SIXR1:	PUSHJ P,RCH
SIXR4:	SUBI T,40
SIXR5:	TLNN H,770000
	 CLBFIL 1,Name longer than six characters.
	IDPB T,H
	IBP I
	IBP J
	JRST SIXR0

SIXR2:	TLNN H,770000
	 CLBFIL 1,Name longer than six characters.
	IBP H
	SETO T,
	IDPB T,I
	IBP J
	JRST SIXR0

SIXR3:	TLNN H,770000
	 CLBFIL 1,Name longer than six characters.
	IBP H
	IBP I
	SETO T,
	IDPB T,J
	JRST SIXR0

;Advance to next significant char, return it in T.

SIGCH:	PUSHJ P,RCH
	CAIE T,40
	 CAIN T,^I
	  JRST SIGCH
	POPJ P,

;Write out from A, B, C.  (Opposite of SIXR.)
;Clobbers H, I, J, T, TT.

SIXW:	JUMPN A,SIXW0		;check for lone star
	JUMPN C,SIXW0
	CAME B,[-1]
	 JRST SIXW0
	WCHI "*
	POPJ P,

SIXW0:	MOVE H,[440600,,A]
	MOVE I,[440600,,B]
	MOVE J,[440600,,C]
SIXW1:	TLNN H,770000
	 POPJ P,
	ILDB T,I		;star?
	JUMPN T,SIXW2
	ILDB T,J		;sharp?
	JUMPN T,SIXW3
	ILDB T,H
	JUMPE T,SIXW4		;space, handle with char
	ADDI T,40		;ordinary character
	CAIN T,"_		;but maybe needs to be slashified
	 JRST SIXW6
	CAIE T,".
	 CAIL T,"0
	  CAILE T,"Z
	   JRST SIXW5
	CAILE T,"9
	 CAIL T,"A
	  JRST SIXW6
SIXW5:	WCHI "/
SIXW6:	PUSHJ P,WCH
	JRST SIXW1

SIXW2:	IBP H
	IBP J
	WCHI "*
	JRST SIXW1

SIXW3:	IBP H
	WCHI "#
	JRST SIXW1

SIXW4:	LDB TT,[360600,,H]	;see if space is embedded or terminal
	MOVEI T,1
	LSH T,(TT)
	SOS T			;mask for chars not yet processed
	TDNE A,T
	 JRST SIXW7		;embedded because of ordinary chars to right
	TDNN B,T
	 TDNE C,T
	  JRST SIXW7		;embedded because of magic chars to right
	POPJ P,			;terminal

SIXW7:	MOVEI T,40		;embedded space
	JRST SIXW5

FLNMW:	.CALL [	SETZ		;Write filename of input file
		'RFNAME		;Clobber A, B, C, H, I, J, T, TT
		MOVEI INCH
		MOVEM CLBDEV'
		MOVEM CLBFN1'
		MOVEM CLBFN2'
		SETZM CLBSNM' ]
	 .LOSE 1000
	SETZB B,C
	IRPS NM,CH,[CLBDEV:CLBSNM;CLBFN1 CLBFN2]
	 MOVE A,NM
	 PUSHJ P,SIXW
	 IFSN [CH], WCHI "CH
	 TERMIN
	POPJ P,

;Read decimal number into A.  Buffer-back the delimiter.  Clobber T.

NUMR:	SETZ A,
NUMR0:	PUSHJ P,RCH
	CAIL T,"0
	 CAILE T,"9
	  JRST UNRCH
	IMULI A,10.
	ADDI A,-"0(T)
	JRST NUMR0

;Write decimal number from A.  Clobber A, B, T.

NUMW:	MOVMS A			;paranoia
	IDIVI A,10.
	HRLM B,(P)
	SKIPE A
	 PUSHJ P,NUMW
	HLRZ B,(P)
	MOVEI T,"0(B)
	JRST WCH

;Adjust to column specified in T, but give at least 2 spaces.
;Clobber T.

COLADJ:	SUB T,COLUMN		;distance to go
	CAIGE T,2
	 MOVEI T,2
	ADD T,COLUMN		;where to go
	PUSH P,T
	TRO T,7
	SUB T,COLUMN
	LSH T,-3		;number of tabs needed
	JUMPE T,COLAD1
COLAD0:	WCHI ^I
	SOJG T,COLAD0
COLAD1:	POP P,T
	SUB T,COLUMN		;number of spaces needed
	JUMPLE T,CPOPJ
COLAD2:	WCHI 40
	SOJG T,COLAD2
	POPJ P,

;Write asciz from TT. Clobber T, TT.

ASZW:	HRLI TT,440700
ASZW0:	ILDB T,TT
	JUMPE T,CPOPJ
	PUSHJ P,WCH
	JRST ASZW0

ifn $$MCP,[
;;; Read hours.xxx into A as 30'ths of a second.  Clobbers T and A

hread:	pushj p,numr		;A gets a number
	imuli a,60.*60.*30.	;standard internal time units
	pushj p,rch		;T gets delimiter
	caig t,40		;delimiter?
	  jrst unrch		;  buffer it back, and return with no fraction
	caie t,".		;had better be "."
	  clbfil 1,Non "." where decimal point expected in hours.xxx field
	pushj p,getdig		;get a digit
	  popj p,		;  delimiter, read no more
	imuli t,60.*60.*3.	;first goes in 10'ths position
	add a,t			;accumulate in A
	pushj p,getdig		;get a digit
	  popj p,		;  delimiter, just return
	imuli t,60.*6.*3.	;goes in 100'th of hours position
	add a,t
	pushj p,getdig		;get a digit, this time
	  popj p,		;  delimiter, just return
	imuli t,3600.*30./1000.	;goes in 1000'ths of hours position
	add a,t			;accumulate in A
	pushj p,rch		;get a character
	caig t,40		;is it a delimiter?
	  popj p,		;yes, all OK.
	clbfil 1,Number too long or garbage in hours.xxx field

getdig:	pushj p,rch		;get another char
	caig t,40		;is it a delimiter?
	  popj p,
	caige t,"0		;  legal digit?
	  cail t,"9
	    caia		;  yes
	      clbfil 2,Garbage in hours.xxx field
	subi t,"0		;convert to number
	aos (p)			;skip
	popj p,			;return

;;; HWRIT2 writes time as n.xx    Clobbers T, A, B, and C
;;; HWRIT3 writes time as n.xxx   Clobbers T, A, B, and C
hwrit2:	MOVMS A			;paranoia
	addi a,180.*3.		;Round off correction.
	movei c,3600.*30./100.	;smallest printing value, for 0 suppression
;falls through
hwrita:	idivi a,60.*60.*30.	;convert 30'ths of seconds to hours
	push p,b		;save remainder, since NUMW clobbers B
	pushj p,numw		;write the number
	wchi ".			;decimal point
	pop p,a			;recover saved remainder
	idivi a,60.*60.*3.	;separate off the big part
	pushj p,putdig		;put it out
	camge b,c		;Is it aproximately 0??
	  popj p,		;  yes, don't print any more digits
	move a,b		;let's hack the rest
	idivi a,60.*6.*3.	;separate off the 100'ths hours
	pushj p,putdig		;put out this digit
	camge b,c		;is the rest effectively 0?
	  popj p,		;  yes, don't print any more digits
	move a,b		;B has last digit
	idivi a,3600.*30./1000.	;final convertion to 1000'th of hour from secs
	jrst putdig		;write out last digit

hwrit3:	movms a			;paranoia, inherited from MOON
	addi a,18.*3.		;round, don't truncate!
	movei c,3600.*30./1000.	;smallest printing val for zero supression
	jrst hwrita		;write out all but last digit

putdig:	movei t,"0(a)		;turn A into ascii digit
	jrst wch		;and write out the digit

] ;END IFN $$MCP,

IFE $$MCP,[
;Read DD!HH:MM:SS into A.  Buffer-back delimiter.  Clobber A, B, C, T.
DHMSR:	SETZB B,C		;B seconds from HH:MM:SS, C days
DHMSR0:	PUSHJ P,NUMR		;A gets a number
	PUSHJ P,RCH		;T gets its delimiter
	CAIN T,"!
	 JRST DHMSR1
	CAIN T,":
	 JRST DHMSR2
	PUSHJ P,UNRCH
	IMULI C,24.*60.*60.
	ADD A,C
	IMULI B,60.
	ADD A,B
	imuli a,30.		;convert to seconds*30.
	POPJ P,
DHMSR1:	MOVE C,A
	JRST DHMSR0
DHMSR2:	IMULI B,60.
	ADD B,A
	JRST DHMSR0

;Write DD!HH:MM:SS from A.  Clobber A, B, T.
DHMSW:	MOVMS A			;paranoia
	SETZM DHMSWF'		;frob-typed flag
	idivi a,30.		;convert to seconds
	IDIVI A,24.*60.*60.	;seperate days from HHMMSS
	JUMPE A,DHMSW0		;suppress days
	PUSH P,B
	PUSHJ P,NUMW
	WCHI "!
	POP P,B
	SETOM DHMSWF
DHMSW0:	MOVE A,B
	IDIVI A,60.*60.
	SKIPN DHMSWF
	 JUMPE A,DHMSW1
	PUSH P,B
	PUSHJ P,NUM2W
	WCHI ":
	POP P,B
	SETOM DHMSWF
DHMSW1:	MOVE A,B
	IDIVI A,60.
	SKIPN DHMSWF
	 JUMPE A,DHMSW2
	PUSH P,B
	PUSHJ P,NUM2W
	WCHI ":
	POP P,B
	SETOM DHMSWF
DHMSW2:	MOVE A,B
NUM2W:	SKIPN DHMSWF	;If a frob already typed, write number with two digits.
	 JRST NUMW
	IDIVI A,10.
	WCHI "0(A)
	WCHI "0(B)
	POPJ P,
]; END IFE $$MCP,


;Read YY.MM.DD into A as SIXBIT/YYMMDD/.  Clobber T, B, C.  Buffer back delimiter.

DATR:	SKIPA C,[".]

;Read HH:MM:SS into A as SIXBIT/HHMMSS/.  Clobber T, B, C.  Buffer back delimiter.

TIMR:	 MOVEI C,":
	SETZ A,
	MOVE B,[440600,,A]
TIMR0:	PUSHJ P,RCH
	CAMN T,C
	 JRST TIMR0
	CAIL T,"0
	 CAILE T,"9
	  JRST TIMR1
	SUBI T,40
	TLNN B,770000
	 CLBFIL 1,Date/Time too long.
	IDPB T,B
	JRST TIMR0

TIMR1:	MOVE B,A
	AND B,[606060606060]
	CAME B,[202020202020]
	 CLBFIL 1,Date/Time short or garbaged.
	JRST UNRCH

;Write SIXBIT/YYMMDD/ in A as YY.MM.DD.  Clobber T, B, C.

DATW:	SKIPA C,[".]

;Write SIXBIT/HHMMSS/ in A as HH:MM:SS.  Clobber T, B, C.

TIMW:	 MOVEI C,":
	MOVE B,[440600,,A]
TIMW0:	ILDB T,B
	ADDI T,40
	PUSHJ P,WCH
	ILDB T,B
	ADDI T,40
	PUSHJ P,WCH
	TLNN B,770000
	 POPJ P,
	MOVE T,C
	PUSHJ P,WCH
	JRST TIMW0

SUBTTL	System message logging

;This looks for SYSMSG messages starting with:
;  "DSK: "
;  "MEMORY: "
;and files them in the file DRAGON; SYSMSG LOG
;Clobbers all registers, uses OUCH

DSKLOG:	SETOM DSKLGF		;Flag file not open yet
	SETOM OUTBC		;Initialize output buffer routine
	.SUSET [.SIDF1,,[%PIRLT]]  ;Defer real-time ints
	MOVEI TT,8		;Compute number of words in SYSMBF
	LSH TT,@SYSMLNG
	MOVEM TT,SYSMBL
	SUBI TT,1
	MOVEM TT,SYSMBM
DSKLG0:	MOVE TT,@SYSMPT		;Last message generated by system
	CAMG TT,SYSMPU
	 JRST DSKLGX		;No new messages
	SUB TT,SYSMBL
	CAMLE TT,SYSMPU
	 MOVEM TT,SYSMPU	;Got behind, skip to catch up
	MOVEI A,8
	ADDB A,SYSMPU
	AND A,SYSMBM		;Index in SYSMBF of message to do
	ADD A,SYSMBF		;Address in system of message

	HRRZ B,(A)		; Get pointer to ascii message
	MOVE TT,SYCORE(B)	; Get first 5 characters
	CAMN TT,[ASCII/DSK: /]
	 JRST DSKLGA		; Go log DSK: message
	CAME TT,[ASCII/MEMOR/]
	 JRST DSKLG0		; Not relevant, ignore
	MOVE TT,SYCORE+1(B)	; Get next 5 characters
	TRZ TT,77777		; Reduce to next 3 characters
	CAME TT,[ASCII/Y: /]
	 JRST DSKLG0		; Not relevant, ignore
				; Go log MEMORY: message
DSKLGA:	AOSN DSKLGF		;Open file if necessary
	 PUSHJ P,DSKLGO
	WCHI 15			;crlf
	WCHI 12
	.RDATE TT,		;Time stamp
	PUSHJ P,6BTW
	WCHI 40
	.RTIME TT,
	PUSHJ P,6BTW
	WCHI 40
	WCHI 40
	ADD B,[440700,,SYCORE]	;bp to asciz string to type
	HLLZ D,(A)		;Get arg mode types
DSKLG1:	ILDB T,B
	JUMPE T,DSKLG2		;end
	CAIGE T,10
	 JRST DSKLG3		;insert that many arguments
	PUSHJ P,WCH
	JRST DSKLG1

DSKLG2:	WCHI 40
	PUSHJ P,DSKLG4
	 JRST DSKLG0
	JRST DSKLG2

DSKLG3:	PUSH P,T		;Number of args to print
DSKL3A:	PUSHJ P,DSKLG4
	 JFCL
	WCHI 40			;Space after each arg
	SOSLE (P)
	 JRST DSKL3A
	SUB P,[1,,1]
	JRST DSKLG1

;Display next arg, using A and D.
DSKLG4:	MOVEI C,0
	LSHC C,3	;Pull arg-type out of D
	JUMPE C,CPOPJ	;No more args, return with no skip
	AOS (P)		;Normally skips, i.e. more args exist
	AOS A
	MOVE TT,(A)	;Get arg
	XCT .(C)
	JRST DSKLG5	;1 Octal
	JRST DSKLG6	;2 Decimal
	JRST DSKLG6	;3 Decimal with commas
	POPJ P,		;4 crlf (ignore)
	POPJ P,		;5 not used
	JRST 6BTW	;6 Sixbit
	JRST DSKLG7	;7 Asciz

DSKLG7:	ADDI TT,SYCORE
	JRST ASZW

DSKLG5:	TLNN TT,-1
	 JRST DSKLG9
	PUSH P,TT
	HLRZS TT
	PUSHJ P,DSKLG9
	WCHI ",
	WCHI ",
	POP P,TT
	HRRZS TT
DSKLG9:	SKIPA C,[8]
DSKLG6:	 MOVEI C,10.
	MOVE T,TT
DSKLG8:	IDIV T,C
	HRLM TT,(P)
	SKIPE T
	 PUSHJ P,DSKLG8
	HLRZ T,(P)
	ADDI T,"0
	JRST WCH

;Routine to open the disk log file
DSKLGO:	.CALL [	SETZ
		SIXBIT/OPEN/
		JFFO TT		;error code
		[100000+.BAO,,OUCH]	;Write over
		[SIXBIT/DSK/]
		[SIXBIT/SYSMSG/]
		[SIXBIT/LOG/]
		SETZ SNAME ]
	 CAIA
	  JRST DKLGO1
	CAIE TT,%ENSFL
	 JRST [	SUB P,[1,,1]
		JRST DSKLGX ]	;Give it up
	.CALL [	SETZ
		SIXBIT/OPEN/
		[.BAO,,OUCH]
		[SIXBIT/DSK/]
		[SIXBIT/SYSMSG/]
		[SIXBIT/LOG/]
		SETZ SNAME ]
	 JRST [	SUB P,[1,,1]
		JRST DSKLGX ]	;Give it up
DKLGO1:	.CALL [	SETZ	;Seek to eof
		'FILLEN
		MOVEI OUCH
		SETZM TT ]
	 .LOSE %LSSYS
	.ACCESS OUCH,TT
	POPJ P,

;Print next arg
;Sixbit output from TT
6BTW:	MOVEI T,0
	LSHC T,6
	ADDI T,40
	PUSHJ P,WCH
	JUMPN TT,6BTW
	POPJ P,

;Exit from DSKLOG
DSKLGX:	SKIPL OUTBC		;If any buffered output, send it
	 PUSHJ P,WBUF
	.CLOSE OUCH,
	.SUSET [.SADF1,,[%PIRLT]]	;Reenable real-time ints
	POPJ P,

ifn $$MCP,[
;;; Access a shared page of MACSYMA

maxget:	syscal open,[%CLBIT,,.uii  ;access MACSYMA
	       %climm,,maxch	;on it's very own channel
	       [sixbit /SYS/]
	       [sixbit /TS/]
	       [sixbit /MACSYM/]]
	  .lose %lsfil
	syscal corblk,[%clbit,,%cbndr
	       %climm,,0
	       %climm,,%jself
	       %climm,,maxpag
	       %climm,,maxch
	       %climm,,maxshr]	;maxshr is page of file shared with MACSYMA
	  .lose %lsfil
	.close maxch,
	popj p,


;;; find all MACSYMA's and account for them
;;; This should only be called with interrupts defered
MAXSCN:	pushj p,maxmrk		   ;mark all the MACSYMA's
	jrst maxswp		   ;scan and push all old macsymas

maxlog:	movsi j,-ntrees
maxlg1:	skipn mx.xun(j)		;is there one there?
	  jrst maxlg9		;  nope, nothing further to do
	pushj p,mxpsh1		;push the cruft, but don't reset it
	setzm mx.ctm(j)		;don't count connect time twice
maxlg9:	aobjn j,maxlg1		;for all MACSYMA's
	popj p,			;don't do anything yet

;;;  This routine is called every 100'th of an hour and finds all jobs with
;;;	a page sharing with our MAXPAG, and if that job is already under
;;;	survelance, (as determined by the same XUNAME, and runtime >
;;;	than previous runtime) AOS's the connect time, and updates the run time
;;;	If the slot is filled with a different job, it is pushed onto cruft
;;;	table.  The flag half of MX.CTM is then set to -1 for new entries, and
;;;	1 for new entries, for the sake of detecting timing screws that leave
;;;	you in another page's circular list.  If this condition is detected,
;;;	all new entries are purged in the sweep phase.
;;;
;;;	The sweep routine, which is called after the mark phase, and clears
;;;	LH(MX.CTM) and pushes cruft for MACSYMA's that have disappeared, and
;;;	deletes entries not active.

MAXMRK:	move j,ruind		;in ourself
	syscal cortyp,[%climm,,%jsnum(j)  ;start in our very own
	       %climmm,,maxpag	;MACSYMA page
	       %clout,,t	   ;ignore type of page
	       %clout,,j	   ;job
	       %clout,,i	   ;page# in job
	       %clout,,t]	   ;determines non-shared page
	  .lose %lssys		   ;  FOOBAR
	hrrz t,t		   ;flush left-half bits!
	sosle t			   ;any sharers?
	  jrst markl1		   ;  yes, trace down that chain

ifn $$stat,aos nomax		   ;nope!  Count this event!

	popj p,			   ;but don't try to count MACSYMA's

marklp:	syscal cortyp,[%climm,,%jsnum(j)  ;job we are hacking
	       i		;page in job
	       %clout,,t	;Ignore type of page
	       %clout,,j	;job
	       %clout,,i	;page # in job
	       %clout,,t]	;determines non-shared page
	  jrst newfls		;  lost chain somewhere, restart
	hrrz t,t		;flush left-half bits!
	sojle t,newfls		;if T is 1 or zero, no sharers, lost chain
markl1:	camn j,ruind		;is it us?
	  popj p,		;  yes, end of circle
	jumpe j,newfls		;paranoia
	movei r,(j)		;move pointer to R so we can hack abs addrs
	imul r,lublk		;make absolute pointer

	hlre tt,mx.ctm(j)	;have we already looked at this job?
	jumpl tt,newfls		;  YES!  Invalidate new jobs!
	jumpn tt,oldfls		;  YES!	 Invalidate ALL jobs!!!!!!  Lose!!!

	pushj p,smjobp		;check to be sure it's the same job
	  jrst markn		;  not same! or gone (0 -=> b)

	movei tt,1		;note that this job was already there
	hrlm tt,mx.ctm(j)	;marked!
	jrst marklp		;loop til no more jobs in chain

markn:	jumpe b,newfls		;if b is 0, retry
	hrros mx.ctm(j)		;mark this job as new
	jrst marklp		;keep on marking

;; come here to flush all new entries and retry mark operation
newfls:	movsi j,-ntrees		;for all trees

ifn $$STATS,aos flsnew		;count the number of times we do this

newfl1:	hlre t,mx.ctm(j)	;get the flag
	skipge t		;if it's new
	  pushj p,maxrst	;  reset the entry

ifn $$STATS,[
	skipge t		;if it's new
	  aos cntnew		;  count this for our stats
]; END IFN $$STATS,

	aobjn j,newfl1		;for all trees
	jrst maxmrk		;restart

;; come here to flush all entries and retry mark operation
oldfls:	movsi j,-ntrees		;for all tress

ifn $$STATS,aos flsold		;count the number of times this gets done

oldfl1:
ifn $$STATS,[
	skipn mx.xun		;if there's anything there
	  aos cntold		;  count it as being flushed
]; END IFN $$STATS,

	pushj p,maxrst		;reset the entry (always, out of paranoia)
	aobjn j,oldfl1		;play it again.
	jrst maxmrk		;restart


;;;; the sweep phase.  Pushes dead jobs, unmarks.

maxswp:	movsi j,-ntrees		;for all MACSYMA's

ifn $$STAT,setzm rmaxfl		;flag to notice if there are no running maximas

maxsw0:	hlre t,mx.ctm(j)	;Get the marking
	hrrzs mx.ctm(j)		;unmark it
	skipn tt,mx.xun(j)	;is there a MACSYMA in this slot?
	  jumpe t,maxsw9	;  no macsyma, no mark, ignore the slot
	    jumpe tt,maxsw4	;    no macsyma, marked.  Initialize
	      jumpe t,maxsw6	;      MACSYMA, no mark, just push old MACSYMA.
	        jumpg t,maxsw4	;        MACSYMA, old MARK
		  pushj p,maxpsh  ;	   MACSYMA, new marked.  Push first.

maxsw4:	movei r,(j)		;get pointer into system for this job.
	imul r,lublk		;convert to system pointer
	move t,@ustp		;get info on run/stopped state
	pushj p,smjobp		;is this the same job?  Get info
	  jumpn tt,maxsw6	;  no, don't hack this entry
	jumpe b,maxsw5		;job gone, don't hack this entry, push it

	movem b,mx.xun(j)	;remember who you are

	tlnn t,busrc		;Is it running?
	  aos mx.ctm(j)		;  count this connect time
	tlnn t,busrc		;is it running?
	  setom rmaxfl		;  note that we found one
	movem a,mx.rtm(j)	;save the runtime
	
	movei tt,9.		;default hour is 9. am
	syscal rqdate,[%clout,,t]
	  jrst [movem tt,mx.hor(j)  ;system doesn't know time, assume 9
	        jrst maxsw9]
	hrrs t
	idivi t,2.*60.*60.	;convert to hour of day
	movei tt,(t)		;get in TT
	movem tt,mx.hor(j)

	jrst maxsw9		;already pushed entry, if any

maxsw5:	skipe mx.xun(j)		;if there is a job in this slot
maxsw6:	  pushj p,maxpsh	;  Push this entry
maxsw9:	aobjn j,maxsw0		;for all MACSYMA's

ifn $$stat,[
	skipn rmaxfl		;were there any running MACSYMA's?
	  aos normax		;  nope, count this event
]; end ifn $$stat,

	popj p,			;return


;;; SMJOBP skips if job pointed to by J is the same one recorded earlier
;;; assumes R is already set to LUBLK*<J>
;;; as a side effect, it returns runtime in A and XUNAME in B.
;;; if the job is gone, it returns 0 in B
;;; Does not clobber T or TT !!

smjobp:	move a,@utrntm		;get runtime for this user
	skipn b,@xuname		;get XUNAME for this user
	  move b,@uname		;  Or UNAME if no XUNAME
	skipn @uname		;paranoia, has the job disapeared?
	  setz b,		;  if UNAME is 0, XUNAME should be too!
	jumpe b,cpopj		;Paranoid of timing screws

	skipn mx.xun(j)		;Is there already a job in that slot?
	  popj p,
	came b,mx.xun(j)	;is it this same job?
	  popj p,		;  No, gotta push it first
	camge a,mx.rtm(j)	;is it the same job?
	  popj p,		;  No, gotta push it first
	jrst popj1		;same job!

;;; push the MACSYMA entry pointed to by J onto the cruft table and reset it.

maxpsh:	pushj p,mxpsh1		;push the cruft
maxrst:	setzm mx.xun(j)		;clear out the XUNAME
	setzm mx.ctm(j)		;and connect time
	setzm mx.rtm(j)		;and runtime
	setzm mx.ort(j)		;and old runtime
	setzm mx.hor(j)		;he didn't log in at a time yet!
	popj p,			;done, return


mxpsh1:

ifn $$STAT,aos crftym		;count number of times pushed these

	pushj p,getcor		;allocate a crufty
	  popj p,		;  no core
	move t,mx.xun(j)	;get the name
	movem t,cf.unm(h)	;and name the crufty
	move t,[sixbit /HACTRN/]  ;pretend it's a HACTRN
	movem t,cf.jnm(h)
	setzm cf.ctm(h)		;connect time is counted elsewhen
	setzm cf.rtm(h)		;similarly the
	setzm cf.swi(h)		;other tree-total resources.
	setzm cf.tnm(h)		;similarly, since TRMNAM isn't used now.
	hrrz t,mx.ctm(j)	;remember MACSYMA connect time
	imuli t,30.*3600./100.	;units of 1/100'th of an hour to seconds*30.
	movem t,cf.mct(h)
	move t,mx.rtm(j)	;remember MACSYMA run time
	sub t,mx.ort(j)		;this much has been counted already
	addi t,4166.		;round,
	idivi t,8333.		;converting to seconds*30.
	movem t,cf.mrt(h)	;and put it into the cruft
	addi t,30.*1800./1000.	;round it off
	idivi t,30.*3600./1000.	;just like printing and re-reading it will
	imul t,[<30.*3600./1000.>*8333.]  ;so that we don't accumulate error.
	addm t,mx.ort(j)	;remember we've counted this much
	pushj p,rdatim		;TT gets date, T gets time
	movem tt,cf.dat(h)	;remember date
	movem t,cf.tim(h)	;time
	setom cf.val(h)		;etc.
	popj p,			;return

]; END IFN $$MCP,


SUBTTL batch job launcher

;;;Call this routine with a first file name in A.  Checks for files on
;;;DRAGON with that first file name, which it then loads and launches.

if1, .insrt system;fsdefs

batdir:	sixbit /dragon/		;directory to check for batch jobs.

batch:	syscal open,[[.bii,,batdsk] ? [sixbit /dsk/]
		[sixbit /.file./] ? [sixbit /(dir)/] ? batdir]
	 .lose %lssys
	move tt,[-2000,,batufd]
	.iot batdsk,tt
	.close batdsk,
	move t,batufd+udnamp
batch1:	cail t,2000
	 jrst batch3
	came a,batufd+unfn1(t)
	 jrst batch2
	syscal open,[[.uii,,batdsk] ? [sixbit /dsk/]
		a ? batufd+unfn2(t) ? batdir]
	 jrst batch2
	syscal open,[[.bio,,batusr] ? [sixbit /usr/]
		movei 0 ? batufd+unfn2(t)]
	 jrst batch2
	syscal load,[movei batusr ? movei batdsk]
	 jrst batch2
	.iot batdsk,tt
	hrrz tt,tt
	.uset batusr,[.supc,,tt]
	syscal disown,[%clbit,,5 ? movei batusr]
	 jfcl
batch2:	addi t,lunblk
	jrst batch1

batch3:	.close batdsk,
	.close batusr,
	popj p,

SUBTTL	Variables, etc.

	VARIABLES

;Variables into which a line from accounting file is copied

LVRBEG::
UNAMEA:	0	;sixbit of uname
UNAMEB:	0	;* mask for uname
UNAMEC:	0	;# mask for uname
JNAMEA:	0	;sixbit of jname
JNAMEB:	0	;* mask for jname
JNAMEC:	0	;# mask for jname
JNAMEF:	0	;jname present flag
ACCNTA:	0	;sixbit of accnt
ACCNTB:	0	;* mask for accnt
ACCNTC:	0	;# mask for accnt
ACCNTF:	0	;account present flag
FHOUR:	0	;first hour
LHOUR:	0	;last hour
HOURSF:	0	;hours present flag
CTIME:	0	;connect time in seconds*30.
CTIMEP:	0	;column position of .. + 1
RTIME:	0	;run time in seconds*30.
RTIMEP:	0	;column position of .. + 1
SWAPS:	0	;number of swap ins
SWAPSP:	0	;column position of .. + 1

ifn $$MCP,[
mctime:	0	;macsyma connect time in seconds*30.
mctimp:	0	;column position of .. + 1
mrtime:	0	;macsyma run time in seconds*30.
mrtimp:	0	;column position of .. + 1
]; END IFN $$MCP,

LODAT:	0	;logout date
LODATP:	0	;column position of .. + 1
LOTIM:	0	;logout time
LOTIMP:	0	;column position of .. + 1
COLUMN:	0	;current column position
OCOLUM:	0	;previous column position (for sake of TAB's)
LVREND::

;I/O Routines' Variables

INBC:	0	;input bytes available
INBP:	0	;input byte pointer
OUTBC:	0	;output bytes left in buffer
OUTBP:	0	;output byte pointer
BBC:	-1	;.GE. 0 => buffered back character

INBFL==200	
INBUF:	BLOCK INBFL	;input buffer
OUTBFL==200
OUTBUF:	BLOCK OUTBFL	;output buffer

;Variables for error routine

NERRS:	0	;number of errors (index into ERRBFZ, ERRBFL)
LERRBF==40
ERRBFL:	BLOCK LERRBF	;line number
ERRBFZ:	BLOCK LERRBF	;asciz message
ERRBFA:	BLOCK LERRBF	;uname being hacked
ERRBFB:	BLOCK LERRBF	;..
ERRBFC:	BLOCK LERRBF	;..
LINENO:	0	;current line number
INTJPC:	0	;JPC at TSINT
BUGJPC:	0	;JPC at BUGCHK
BUGACS:	BLOCK 20 ;ACs at BUGCHK
BUGMSG:	BLOCK 10 ;DMNPOP copies each message into here for benefit of BUGCHK

;Variables for batch job launcher

batufd: block 2000	;binary MFD of directory to search for batch jobs.

;Variables for file updater etc.

CHKALL:	1,,	;non-zero => check all lines for garbage, not just ones being updated.
		;LH is modified by program; RH is a debugging switch.
		;assembled non-zero so will check whole file when first starting up.
UPDATR:	0	;name of person who last updated the file.
UPDATJ:	0	;jname
CLIFLG:	0	;0 normal, -1 do initial file update, 1 waiting for CLI completion.
DWNTIM:	0	;time system went down.

DRANDF:	0	;non-zero => delete useless randoms
RANDF:	0	;non-zero => currently in random users section.
TOTLF:	0	;non-zero => currently in totals section.
STOTLF:	0	;non-zero => next line is subtotals.
LASDAT:	0	;sixbit date file last updated
YEARF:	0	;nonzero => year has just ticked over
MONTHF:	0	;nonzero => month has just ticked over
DAYF:	0	;nonzero => day has just ticked over
MNTHLF:	0	;nonzero => doing monthly reset processing
STCTIM:	0	;subtotal connect time
STRTIM:	0	;subtotal run time
STSWAP:	0	;subtotal swaps
IFN $$MCP,[
stmctm:	0	;subtotal MACSYMA connect time
stmrtm:	0	;subtotal MACSYMA run time
]; END IFN $$MCP,

STLODT:	0	;subtotal logout date
STLOTM:	0	;subtotal logout time
GTCTIM:	0	;grand total connect time
GTRTIM:	0	;grand total run time
GTSWAP:	0	;grand total swaps

IFN $$MCP,[
gtmctm:	0	;grand total MACSYMA connect time
gtmrtm:	0	;grand total MACSYMA run time
]; END IFN $$MCP,
		;don't change order of next 6 items
OKDAT:	-1	;sixbit date last seen this execution of dragon
OKTIM:	-1	;sixbit time ditto
OKTMUP:	-1	;seconds*30. of up-time
NOKDAT:	0	;last date from file
NOKTIM:	0	;..
NOKTMU:	0	;..

LOTOLD:	BLOCK 5			;read from file into here
	0
LOTNEW:	ASCII\UNAME MM/DD/YY HH:MM:SS
\				;build new in here
IFN .-LOTNEW-5, .ERR LOTNEW loses

;dmn-push message from system for login

LI.==,,-1
LI.TIM==0	;FIRST WORD IS TIME IN 30THS
LI.IDX==1	;RH USER INDEX
 LI%COD==10000	;LH IS THIS CODE
LI.UNM==2	;UNAME
LI.JNM==3	;JNAME
LI.TNM==4	;TRMNAM

;dmn-push message from system for logout

LO.==,,-1
LO.TIM==0	;FIRST WORD IS TIME IN 30THS
LO.IDX==1	;RH USER INDEX
 LO%COD==20000	;LH IS THIS CODE
LO.UNM==2	;UNAME
LO.JNM==3	;JNAME
LO.RTM==4	;RUN TIME IN 4.069 MICROSECOND UNITS
LO.SWI==5	;SWAP-IN REQUESTS

;Fake logout message used at shutdown

FAKMSG:
	0	;LO.TIM
	LO%COD,, ;LO.IDX
	0	;LO.UNM
	0	;LO.JNM
	0	;LO.RTM
	0	;LO.SWI

;All stuff referenced frequently by DMNPOP etc. should be after this point.

CONST.:	CONSTANTS

;Cruft for each logged in tree

NTREES==171		;SHOULD BE ENOUGH (BETTER BE MORE THAN MAXJ)
TR.UNM:	BLOCK NTREES	;UNAME, 0 IF SLOT EMPTY
TR.JNM:	BLOCK NTREES	;TOP JNAME
TR.TNM:	BLOCK NTREES	;TRMNAM AT LOGIN
TR.XUN:	BLOCK NTREES	;XUNAME
TR.TIM:	BLOCK NTREES	;TIME AT LOGIN (FOR COMPUTING CONNECT)

;Unless we miss a dmn-push, nothing can go away or change
;its name without the system saying so.  Trees can appear
;without the system saying so if they are created by disowning;
;in that case we just won't charge connect time.

IFN $$MCP,[
mx.xun:	block ntrees  ;UNAME, 0 if slot empty
mx.ctm:	block ntrees  ;<flag>,,<interval count> (flag is used to mark MACSYMAs
		      ;  found on given scan;  the count -=> connect time)
mx.rtm:	block ntrees  ;runtime of this MACSYMA
mx.ort:	block ntrees  ;runtime of this MACSYMA which has already been recorded
mx.hor:	block ntrees  ;hour this MACSYMA was started, for HOURS=
]; END IFN $$MCP,

;Cruft pushed for logged out trees (at end of core)

CF.==,,-1

CF.UNM==0	      ;XUNAME    (0 MARKS END OF CRUFT)
CF.JNM==1	      ;JNAME
CF.TNM==2	      ;TRMNAM (ACCOUNT)
CF.HOR==3	      ;HOUR OF THE DAY LOGGED IN (0-23.)
CF.CTM==4	      ;CONNECT TIME IN SECONDS*30.
CF.RTM==5	      ;RUN TIME IN SECONDS*30.
CF.SWI==6	      ;# SWAPINS

ifn $$MCP,[
CF.MCT==7	      ;# of times this MACSYMA was runable
CF.MRT==10	      ;run time this MACSYMA observed to use
CF.DAT==11	      ;SIXBIT DATE OF LOGOUT
CF.TIM==12	      ;SIXBIT TIME OF LOGOUT
CF.VAL==13	      ;VALID FLAG (0 => HAS BEEN PUT INTO ACCT FILE)
CF.LEN==14	      ;# WDS/CRUFTY
]; END IFN $MCP,

ife $$MCP,[
CF.DAT==7	      ;SIXBIT DATE OF LOGOUT
CF.TIM==10	      ;SIXBIT TIME OF LOGOUT
CF.VAL==11	      ;VALID FLAG (0 => HAS BEEN PUT INTO ACCT FILE)
CF.LEN==12	      ;# WDS/CRUFTY
]; END IFE $MCP,

;data
ruind:	0	      ;OUR user index

;timers
dmpcnt:	-31	;count till time to dump
dmpinv:	-31	;# of clock cycles between file updates (25. = 15 minutes)
maxcnt:	-1	;count till time to do acounting update on MACSYMAs
maxinv:	-1	;count of ticks per 36. second period
rltinv:	60.*36.	;36. seconds between ints, normally.

CRUFTP:	CRUFT	;points at first unused entry in cruft table.
CRUFTF:	CRUFT	;fence.  stuff below this is getting updated into file.
CRUFTE:	CRUFTZ	;points at highest loc+1 allocated for cruft table.
		; which had better be a page boundary.


ifn $$STAT,[
;; internal statistics

updtct:	0			;count of file updates
updtcp:	0			;position of above on line
crftys:	0			;# of pieces of cruft pushed by logout dmnpush
crftsp:	0			;position of above on line
lrunt:	0			;last recorded runtime
lruntp:	0			;position of above on line
orunt:	0			;runtime consumed so far in this job
coraos:	0			;# of times we've grown core.
coraop:	0			;position of above on line

ifn $$MCP,[
rmaxfl:	0			;flag to notice no running MACSYMA's seen

crftym: 0			;# of pieces of cruft pushed for MACSYMA's
crftmp:	0			;position of above on line
nomax:	0			;# of times no MACSYMA's seen
nomaxc:	0
nomaxp:	0			;position of above on line
normax:	0			;# of times no running MACSYMA's seen
normxc:	0
normxp:	0			;position of above on line
flsnew:	0			;# of times we've NEWFLS'd
flsnwp:	0			;position of above on line
cntnew:	0			;# of MACSYMA's we've NEWFLS'd
cntnwp:	0			;position of above on line
flsold:	0			;# of times we've OLDFLS'd
flsolp:	0			;position of above on line
cntold:	0			;# of MACSYMA's we've OLDFLS'd
cntolp:	0			;position of above on line
];  END IFN $$MCP,
];  END IFN $$STAT,


;Pointers to relevant system symbols

DEDTIM:	0	;<0 system down.  0 Up.  Otherwise time till death.
TIME:	0	;30ths since system up.
USRHI:	0	;index of highest user +.
LUBLK:	0	;length of user variables block.
UNAME:	0	;user name.
XUNAME:	0	;user's intended uname
JNAME:	0	;job name.
SUPPRO:	0	;superior pointer.
USTP:	0	;run/stop flag
UTRNTM:	0	;user run time.
USIPRQ:	0	;user swapin requests.
TRUNTM:	0	;run time of inferiors minus previously-logged.
TSIPRQ:	0	;swapin requests of inferiors minus previously-logged.

DMNBC:	0	;AOS'ed each time something is put in the buffer, before
DMNBD:	0	;AOS'ed each time something is put in the buffer, after
DMNBF:	0	;the buffer
DMNBFE:	0	;the end of the buffer
DMNBEL:	0	;# words per message

LDMNBD:	0	;last value of DMNBD seen
DMNBSZ:	0	;number of frobs that fit in the buffer

SYSMPT:	0	;Pointer to index of message most recently inserted in SYSMBF
SYSMLNG:0	;log(2) of number of 4-word entries in SYSMBF
SYSMBF:	0	;Pointer to SYSMSG table in the system
SYSMBL:	0	;Number of words in SYSMBF
SYSMBM:	0	;That minus one (mask)
SYSMPU:	0	;Index of last message processed
DSKLGF:	0	;-1 means file not opened yet in DSKLOG

;Cruft Table Starts Here

CRUFT:	-1
CRUFTZ==<CRUFT+1777>&<776000>	;address of first additional cruft page.
crfmrg==20.			;ask for update this many crufties from full
crfsiz==10.			;ten pages of cruft before panic dump

maxpag==176			;page for sharing with MACSYMA
maxshr==60			;page of SYS:TS MACSYM to share with

CMPAGE=376000	;Communication page, see CLIINT.
SYCORE=400000	;Upper Core is mapped into absolute.

END GO
