Google
 

Trailing-Edge - PDP-10 Archives - cuspmar86binsrc_1of2_bb-x128b-sb - 10,7/acct/newact.mac
There are 2 other files named newact.mac in the archive. Click here to see a list.
	TITLE	NEWACT--CONVERT A VERSION 4 ACCT.SYS TO A VERSION 6 ACTDAE.SYS
	SUBTTL	Tarl/TARL/LWS/TL/RCB	3-Dec-85

	SALL			;CLEAN LISTINGS
	.DIRECT	FLBLST		;EVEN CLEANER

	SEARCH	ACTPRM
	MODULE	(NEWACT)

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

COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1986.
ALL RIGHTS RESERVED.
\;END COPYRIGHT MACRO


	NEAVER==1	;VERSION NUMBER
	NEAEDT==7	;EDIT NUMBER
	NEAWHO==0	;WHO EDITED LAST
	NEAMIN==0	;MINOR VERSION NUMBER

NEAACV==<VRSN.	(NEA)>

	.ORG	137
	EXP	NEAACV	;VERSION
	.ORG
	SUBTTL	Table of contents

;               TABLE OF CONTENTS FOR NEWACT
;
;
;                        SECTION                                   PAGE
;    1. Table of contents.........................................   2
;    2. Revision History..........................................   3
;    3. General information.......................................   4
;    4. Assembly parameters.......................................   5
;    5. Definitions...............................................   6
;    6. Basic conversion table....................................   7
;    7. Main program..............................................   8
;    8. Initialization............................................   9
;    9. Lookup the input files....................................  10
;   10. Initialize the USERS.TXT index............................  11
;   11. Insert USERS.TXT index entries............................  12
;   12. Main conversion loop......................................  13
;   13. Final cleanup.............................................  14
;   14. Read an entry from ACCT.SYS...............................  15
;   15. Read an entry from AUXACC.SYS.............................  16
;   16. Convert an entry..........................................  17
;   17. Convert a username........................................  18
;   18. Convert a password........................................  19
;   19. Convert core limits.......................................  20
;   20. Convert program-to-run....................................  21
;   21. Convert expiration date...................................  22
;   22. Convert AUXACC data.......................................  23
;   23. Format a PPN for display..................................  24
;   24. Find the length of a SIXBIT string........................  25
;   25. Random hiseg data.........................................  26
;   26. Impure (lowseg) data......................................  27
SUBTTL	Revision History

;  1)	Create NEWACT. /TARL
;
;  2)	Add defaults when converting "program-to-run" so they'll
;	be displayed the same as when created by new REACT.
;	QAR #868019 5-Apr-85 /LWS
;
;  3)	Converts ACCT.SYS and AUXACC.SYS to RMS ACCT.ACT /TL
;
;  4)	New accounting file name is now SYS:ACTDAE.SYS, managed
;	by RMS.	/LWS
;
;  5)	Get rid of VERSET. ACTRMS must be loaded first. /LWS
;
;  6)   DO COPYRIGHTS. 15-AUG-85 /LEO
;
;  7)	Upgrade from version 5 (already obsolete) to version 6.  /RCB
;
;
;End of revision history
	SUBTTL	General information

COMMENT	`

The following information describes how to customize NEWACT for any possible
changes to the formats of ACCT.SYS and AUXACC.SYS for your site, or for use
of .ACCUS or the old 'charge number'.

First, make sure that ACTSYM is up-to-date with respect to your changes.  In
particular, be sure that .ACLEN accurately reflects the length of a profile
entry.  Then, be sure that you update the AEPROF macro in ACTSYM to define
words for your entries in the new profile format.

If you have used some form of password encryption in the past, you will want to
set NCRYPT accordingly.  You may also need to modify ACTCUS to include your
encryption algorithm, if you wrote your own.

Next, add your profile entries to CNDTAB, so that the fields from ACCT.SYS will
be preserved in ACTDAE.SYS.  Then, for any of your entries which require no
translation, add them to the table of fixed byte pointers (XFRBPT).  For any
entries which do require translation, add calls your conversion routines to
CNVSPC.

Also, if you want the users' names to be something a bit more readable than
the old 12-character restriction, you can define a USERS.TXT file.  This is
entirely optional, and NEWACT will be quite happy if no USERS.TXT exists.
If you should desire this, the official format of USERS.TXT is as follows:
	<structure>:[ppn],<user-name>
on each line.  In fact, NEWACT considers the structure name to be optional,
and will allow comments and blank lines in the file.  The user-name can
be any ASCII text without control-characters, up to 39 characters in length.
No spaces are allowed between the ppn and the comma separating it from the
username.  Any spaces after that comma are considered part of the username.
It is not necessary that every PPN in ACCT.SYS have an entry in USERS.TXT.

(As a historical footnote, the reason that USERS.TXT has a structure in it
is due to a mail system which was used in-house at DEC, but which was never
made suitable for general distribution.  This structure was where the user's
mail file resided.)

Even if you use no site-specific fields in ACCT.SYS, you may want to examine
the available assembly parameters.  The security features made availble with
this release of the accounting system are a considerable improvement over what
has been previously available.

Finally, note that NEWACT looks for all files on DSK:.  If your PPN has read
privileges to the system files, a simple manipulation of your search list with
SETSRC or PATH will suffice to include the system area in your DSK: definition,
should that be desired.

`
	SUBTTL	Assembly parameters

ND	FTISWS,0
IFN FTISWS,<
	.ACLEN==20	;JUNKY OLD WORDS (OBSOLETE)
	ND	NCRYPT,2
>
ND	NCRYPT,0	;0 - OLD ACCT.SYS IS NOT ENCRYPTED
			;1 - OLD ACCT.SYS IS ENCRYPTED BY CUSTOMER ALG 1 (#5)
			;2 - OLD ACCT.SYS IS ENCRYPTED BY CUSTOMER ALG 2 (#6)

;DEFINITIONS FOR NEW VALUES IN PROFILE

ND	CTXMXC,4	;MAXIMUM NUMBER OF CONTEXTS
ND	CTXMXP,^D1000	;MAXIMUM NUMBER OF IDLE CONTEXT PAGES
ND	PIDMAX,2	;MAXIMUM NUMBER OF PIDS
ND	PSWLEN,0	;MINIMUM ALLOWABLE PASSWORD LENGTH
ND	PSWCHG,0	;DEMAND PASSWORD CHANGE ON NEXT LOGIN IF NON-ZERO
ND	PSWINT,0	;NON-ZERO IS MAXIMUM NUMBER OF DAYS BETWEEN PSW CHANGES
ND	SETPCP,0	;DISALLOW USER-ORIGINATED PASSWORD CHANGES IF NON-ZERO
	SUBTTL	Definitions

;PDL length
	PDLLEN==200	;MIGHT BE LONG ENOUGH

;Dummy for ACTRMS
	UGAUX%==:.RETF	;SATISFY GLOBAL REQUIREMENTS

;Macro to define byte pointer pairs: old location:new location

DEFINE FROMTO(F.OFF,F.MASK,T.OFF,T.MASK),<
IFN F.MASK,<POINTR(ACCT+F.OFF,F.MASK)>
IFE F.MASK,<POINT 36,ACCT+F.OFF,35>
XLIST
IFN T.MASK,<POINTR(PROFIL+T.OFF,T.MASK)>
IFE T.MASK,<POINT 36,PROFIL+T.OFF,35>
LIST
>;END DEFINE FROMTO

DEFINE NEWDEF(F.DEF,T.OFF,T.MASK),<
IFN F.DEF,<
	POINT 36,[F.DEF],35
XLIST
IFN T.MASK,<POINTR(PROFIL+T.OFF,T.MASK)>
IFE T.MASK,<POINT 36,PROFIL+T.OFF,35>
LIST
>>;END DEFINE NEWDEF
	SUBTTL	Basic conversion table

;Byte pointers for quantities which can be simply transferred.
XFRBPT:
FROMTO(	.ACPPN,	,	.AEPPN,	,)	;PPN
FROMTO(	.ACPRV,	,	.AEPRV,	,)	;PRIMARY PRIV WORD
FROMTO(	.ACLIT,	,	.AELGT,	,)	;LOGIN TIMES
FROMTO(	.ACCIP,	AC.SND,	.AEIPC,	AE.SND)	;IPCF SEND QUOTA
FROMTO(	.ACCIP,	AC.RCV,	.AEIPC,	AE.RCV)	;IPCF RECEIVE QUOTA
NEWDEF(	PIDMAX,		.AEIPC,	AE.PID)	;IPCF MAX NUMBER OF PIDS
FROMTO(	.ACPRO,	AC.WDT,	.AEWCH,	JW.WDY)	;WATCH	DAYTIME
FROMTO(	.ACPRO,	AC.WRT,	.AEWCH,	JW.WRN)	;	RUNTIME
FROMTO(	.ACPRO,	AC.WWA,	.AEWCH,	JW.WWT)	;	WAIT
FROMTO(	.ACPRO,	AC.RED,	.AEWCH,	JW.WDR)	;	READ
FROMTO(	.ACPRO,	AC.WRI,	.AEWCH,	JW.WDW)	;	WRITES
FROMTO(	.ACPRO,	AC.WVR,	.AEWCH,	JW.WVR)	;	VERSION
FROMTO(	.ACPRO,	AC.WMT,	.AEWCH,	JW.WMT)	;	MAGTAPE
FROMTO(	.ACPRO,	AC.WFL,	.AEWCH,	JW.WFI)	;	FILE
FROMTO(	.ACPRO,	AC.CDR,	.AESPL,	JS.PCR)	;SPOOL	CDR
FROMTO(	.ACPRO,	AC.CDP,	.AESPL,	JS.PCP)	;	CDP
FROMTO(	.ACPRO,	AC.PTP,	.AESPL,	JS.PPT)	;	PTP
FROMTO(	.ACPRO,	AC.PLT,	.AESPL,	JS.PPL)	;	PLOT
FROMTO(	.ACPRO,	AC.LPT,	.AESPL,	JS.PLP)	;	LPT
FROMTO(	.ACPRO,	AC.OPR,	.AEPRX,	JP.OPR)	;OPR PRIVS
FROMTO(	.ACPRO,	AC.RMK,	.AEREQ,	AE.RMK)	;REQUIRE REMARK
FROMTO(	.ACPRO,	AC.ACT,	.AEREQ,	AE.ACT)	;	ACCOUNT
FROMTO(	.ACPRO,	AC.NRT,	.AEREQ,	AE.NRT)	;	NAME UNDER TIMESHARING
FROMTO(	.ACPRO,	AC.NRB,	.AEREQ,	AE.NRB)	;	NAME UNDER BATCH
FROMTO(	.ACPRO,	AC.PRT,	.AEREQ,	AE.PRT)	;	PASSWORD UNDER TIMESHARING
FROMTO(	.ACPRO,	AC.PRB,	.AEREQ,	AE.PRB)	;	PASSWORD UNDER BATCH
NEWDEF(	SETPCP,		.AEREQ,	AE.PCP)	;	PROHIBIT PASSWORD CHANGING
NEWDEF(	PSWLEN,		.AEREQ,	AE.PWL)	;	MINIMUM PASSWORD LENGTH
NEWDEF(	PSWINT,		.AEREQ,	AE.PCI)	;	PASSWORD CHANGE INTERVAL
FROMTO(	.ACPRO,	AC.LOC,	.AEACC,	AE.LOC)	;LOGIN	LOCAL
FROMTO(	.ACPRO,	AC.ROP,	.AEACC,	AE.ROP)	;	REMOTE OPERATOR
FROMTO(	.ACPRO,	AC.DST,	.AEACC,	AE.DST)	;	DATA SET
FROMTO(	.ACPRO,	AC.RMT,	.AEACC,	AE.RMT)	;	REMOTE
FROMTO(	.ACPRO,	AC.BAT,	.AEACC,	AE.BAT)	;	BATCH
FROMTO(	.ACPRO,	AC.SBJ,	.AEACC,	AE.SBJ)	;	BATCH SUB JOB
FROMTO(	.ACPRO,	AC.RMT,	.AEACC,	AE.FAL)	;	FILE ACCESS
FROMTO(	.ACPRO,	AC.RMT,	.AEACC,	AE.CDR)	;	PHYSICAL CARD READER
FROMTO(	.ACESE,	AC.SCD,	.AESCD,	AE.SCD)	;SCHEDULAR TYPE
FROMTO(	.ACESE,	AC.EDQ,	.AEENQ,	,)	;ENQ/DEQ QUOTA
NEWDEF( CTXMXC,         .AECTX, AE.CNQ) ;MAXIMUM NUMBER OF CONTEXTS
NEWDEF( CTXMXP,         .AECTX, AE.CPQ) ;MAX NUMBER OF IDLE CONTEXT PAGES
;Customer stuff and obsolete stuff
FROMTO(	.ACCUS,	,	.AECUS,	,)	;CUSTOMER PROFILE WORD.
;FROMTO(.ACCNO,	,	.AECU2,	,)	;CHARGE NUMBER. OBSOLETE.
EXP	0				;END OF TABLE.
	SUBTTL	Main program

NEWACT:	JFCL			        ;IGNORE CCL ENTRY
	RESET			        ;CLEAR THE WORLD'S STATUS
	MOVE	P,PDLPTR	        ;GET POINTER TO PUSH DOWN LIST
	MOVEI	S1,IB.SZ	        ;SIZE OF IB
	MOVEI	S2,IB		        ;POINTER TO INITIALIZATION BLOCK
	$CALL	I%INIT		        ;INITIALIZE
	PUSHJ	P,INITIA		;SETUP INITIAL DATA AND OVERHEAD
	PUSHJ	P,CONVRT		;CONVERT THE VARIOUS PROFILES
	PUSHJ	P,FINISH		;CLEAN UP

DIEBAD:	EXIT				;DONE
	SUBTTL	Initialization

INITIA:	MOVEI	S1,'NEA'		;MODULE PREFIX
	SETZ	S2,			;NO ERROR PROCESSOR
	PUSHJ	P,A$ERRI##		;SETUP THE ERROR MODULE
	PUSHJ	P,INITIO##		;WARM UP ACTRMS
	SETZM	ZERMEM			;CLEAR A WORD
	MOVE	S1,[ZERMEM,,ZERMEM+1]	;XFER VECTOR
	BLT	S1,ZEREND		;FINISH CLEARING VOLATILE MEMORY
	PUSHJ	P,FNDIFL		;FIND AND CHECK THE INPUT FILES
	PUSHJ	P,ACCYNK		;YANK IN AN ACCT.SYS ENTRY
	SKIPT				;THERE'D BETTER BE ONE
	FATAL	(NEA,<No entries found in ACCT.SYS>,,DIEBAD)
	PUSHJ	P,AUXYNK		;YANK IN AN AUXACC.SYS ENTRY
	SKIPT				;NEED AT LEAST ONE
	FATAL	(NEX,<No entries found in AUXACC.SYS>,,DIEBAD)
	PUSHJ	P,USRINI		;DEAL WITH USERS.TXT
	$TEXT	(<-1,,OUTFIL>,<DSK:^W/[ACTFIL]/.SYS^0>) ;MAKE OUTPUT FILE NAME
	MOVEI	S1,OUTFIL		;POINT TO FILE SPEC
	MOVEI	S2,1			;WRITE ACCESS
	PUSHJ	P,OPNA##		;OPEN AS THE PRIMARY FILE
	JUMPF	INIT.F			;REPORT FAILURES BELOW
	MOVEI	S1,1			;OPTION TYPE ONE
	MOVEI	S2,1			;SET THE LOAD FLAG
	PUSHJ	P,OPTA##		;THIS IS THE INITIAL LOAD
	MOVEI	S1,4			;OPTION NUMBER
	PUSHJ	P,OPTA##		;GET RETURNED FILESPEC BLOCK
	MOVSI	S1,FDXSIZ		;BLOCK LENGTH
	MOVEM	S1,(S2)			;SET FOR GLXFIL
	INFO	(WTF,<Writing to file ^F/(S2)/>) ;SAY WHERE THIS IS GOING
	SETOM	PRODEF+.AEMAP		;START OUT TO DEFAULT
	MOVE	S1,[PRODEF+.AEMAP,,PRODEF+.AEMAP+1] ;BLT XFER WORD
	BLT	S1,PRODEF+.AEMAP+.AMPLW-1 ;SETUP TO DEFAULT EVERYTHING
	MOVSI	P1,-.AEMIN		;AOBJN POINTER TO CHGTAB
	MOVEI	T1,PRODEF		;CURRENT PROFILE
	SETZ	T3,			;GOING TO CLEAR THE BITS
INIT.1:	HRRZ	T2,P1			;GET JUST THE OFFSET
	MOVX	T4,PD.CND!PD.UNP	;CAN-NOT-DEFAULT OR USER-CHANGEABLE
	TDNE	T4,CHGTAB##(P1)		;IS IT ON FOR THIS OFFSET?
	PUSHJ	P,A$BMAP##		;YES, CLEAR THE 'DEFAULTED' BIT
	AOBJN	P1,INIT.1		;LOOP OVER ALL PROFILE ENTRIES
	MOVSI	P1,-CNDLEN		;TABLE INDEXER FOR ACCT.SYS ENTRIES
INIT.2:	MOVE	T2,CNDTAB(P1)		;GET NEXT NON-DEFAULTED ENTRY
	PUSHJ	P,A$BMAP##		;CLEAR ITS DEFAULT BIT
	AOBJN	P1,INIT.2		;DEAL WITH EXPLICIT VALUES IN OLD FILE
	GETPPN	S1,			;GET OUR PPN
	  JFCL				;WHO WOULD PUT THIS THING IN PRVTAB?
	MOVEM	S1,PRODEF+.AEPAP	;PROFILE ALTERATION PPN
	$CALL	I%NOW			;GET THE CURRENT UDT
	MOVEM	S1,PRODEF+.AETIM	;SET AS PROFILE CHANGE TIME
	MOVE	S1,[6,,.AEMIN]		;VERSION AND INITIAL LENGTH
	MOVEM	S1,PRODEF+.AEVRS	;SETUP OVERHEAD WORD
	POPJ	P,			;DONE SETTING UP

INIT.F:	MOVEI	S1,2			;LAST FAB ERROR
	PUSHJ	P,OPTA##		;GET THE RMS ERROR CODE
	FATAL	(RIE,<RMS initialization error; code is ^O/S1/>,,DIEBAD)

CNDTAB:	EXP	.AEPRV,.AEPRX,.AECOR,.AEIPC,.AESPL,.AEACC,.AEREQ,.AEPGR
	EXP	.AESCD,.AEENQ,.AECTX,.AEEXP
;	EXP	.AECUS
CNDLEN==.-CNDTAB
	SUBTTL	Lookup the input files

FNDIFL:	MOVEI	S1,FOB.MZ		;FOB SIZE
	MOVEI	S2,ACCFOB		;FOB FOR ACCT.SYS
	$CALL	F%IOPN			;LOOKUP THE FILE
	SKIPT				;DID IT WIN?
	FATAL	(CRA,<Can't read ACCT.SYS; error is ^E/S1/>,,DIEBAD)
	MOVEM	S1,ACCIFN		;SAVE IFN FOR READING
	MOVEI	S1,FOB.MZ		;FOB SIZE
	MOVEI	S2,AUXFOB		;FOB FOR AUXACC.SYS
	$CALL	F%IOPN			;LOOKUP THE FILE
	SKIPT				;MAKE SURE IT WINS
	FATAL	(CRX,<Can't read AUXACC.SYS; error is ^E/S1/>,,DIEBAD)
	MOVEM	S1,AUXIFN		;SAVE IFN FOR READING
	MOVE	S1,ACCIFN		;READ ACCT.SYS
	$CALL	F%IBYT			;ONE (FIRST) WORD
	SKIPT				;BETTER BE THERE
	FATAL	(ERA,<Error reading ACCT.SYS; error is ^E/S1/>,,DIEBAD)
IFN FTISWS,<
	TXZN	S2,1B0			;CHECK THE ENCRYPTION FLAG
	TDZA	S1,S1			;OFF
	SETO	S1,			;OR ON
	MOVEM	S1,CPTFLG		;REMEMBER IT FOR LATER
> ;END OF FTISWS
	CAMN	S2,ACCVWD		;QUICK CHECK AGAINST EXPECTED VALUE
	JRST	FNDI.1			;GOT IT, DON'T CHECK FIELDS
	LOAD	S1,S2,AC.VRS		;NO, GET VERSION FIELD
	CAIE	S1,.ACCVN		;IS IT WHAT WE EXPECTED?
	FATAL	(WVA,<Wrong version of ACCT.SYS; expected ^O/[.ACCVN]/ but ^O/S1/ was found>,,DIEBAD)
	LOAD	S1,S2,AC.LEN		;YES, GET LENGTH FIELD
	CAIE	S1,.ACLEN		;MAKE SURE IT'S WHAT WE EXPECTED
	FATAL	(BSA,<Bad size entry in ACCT.SYS; expected ^O/[.ACLEN]/ but ^O/S1/ was found>,,DIEBAD)

FNDI.1:	MOVE	S1,ACCIFN		;HANDLE FOR ACCT.SYS
	MOVEI	S2,FI.SIZ		;FILE-INFO SIZE REQUEST
	$CALL	F%INFO			;ASK GLXFIL FOR RIBSIZ
	SKIPT				;I HOPE IT WORKED
	FATAL	(UGE,<Unexpected GLXLIB error:  ^E/S1/>,,DIEBAD)
	IDIVI	S1,.ACLEN		;CONVERT TO NUMBER OF ENTRIES
	CAIE	S2,1			;IS THE REMAINDER WHAT WE EXPECT?
	WARN	(ILA,<Incorrect length of ACCT.SYS; proceeding>)
	MOVEM	S1,ACCNUM		;STORE AS COUNT OF ACCT.SYS ENTRIES
	MOVE	S1,AUXIFN		;HANDLE FOR AUXACC.SYS
	$CALL	F%IBYT			;READ FORMAT WORD
	SKIPT				;I HOPE IT WORKED
	FATAL	(ERX,<Error reading AUXACC.SYS; error is ^E/S1/>,,DIEBAD)
	SKIPE	S2			;CHECK FORMAT/VERSION WORD
	FATAL	(UVX,<Unknown version of AUXACC.SYS; expected 0 but ^O/S2/ was found>,,DIEBAD)
	MOVE	S1,ACCIFN		;GET HANDLE FOR ACCT.SYS
	SETO	S2,			;WANT REAL FILESPEC
	$CALL	F%FD			;GET AN FD FOR IT
	INFO	(RAF,<Reading ACCT information from ^F/(S1)/>)
	MOVE	S1,AUXIFN		;GET HANDLE FOR AUXACC.SYS
	SETO	S2,			;WANT REAL FILESPEC
	$CALL	F%FD			;GET AN FD FOR IT
	INFO	(RXF,<Reading AUXACC information from ^F/(S1)/>)
	POPJ	P,			;DONE
	SUBTTL	Initialize the USERS.TXT index

USRINI:	MOVEI	S1,FOB.MZ		;SIZE OF FOB
	MOVEI	S2,USRFOB		;FOB FOR USERS.TXT
	$CALL	F%IOPN			;TRY TO READ THE FILE
	JUMPT	USRI.0			;CONTINUE IF LOOKUP SUCCEEDS
	CAXN	S1,ERFNF$		;NO SUCH FILE?
	$RETT				;PROCEED WITHOUT IT QUIETLY
	WARN	(CRU,<Can't read USERS.TXT; error is ^E/S1/; proceeding>,,.RETF)

USRI.0:	MOVEM	S1,USRIFN		;SAVE FILE HANDLE
	SETO	S2,			;WANT REAL FILESPEC
	$CALL	F%FD			;GET IT
	INFO	(BUI,<Building USERS.TXT index from ^F/(S1)/>)

USRI.1:	MOVE	S1,USRIFN		;RECOVER IFN
	$CALL	F%IBYT			;GET FIRST BYTE FROM THE LINE
	JUMPF	USRI.7			;EOF?
	CAIE	S2,";"			;IS IT A COMMENT INTRODUCER?
	CAIN	S2,"!"			;OF EITHER KIND?
	JRST	USRI.5			;YES, SKIP THIS LINE
	CAIGE	S2," "			;OR A CONTROL CHARACTER?
	JRST	USRI.1			;YES, WAIT FOR A REAL CHARACTER
	CAIA				;NO, START LOOKING FOR A PPN

USRI.2:	$CALL	F%IBYT			;GET ANOTHER CHARACTER ON THE LINE
	SKIPT				;GET IT OK?
	FATAL	(UUE,<Unexpected USERS.TXT error:  ^E/S1/>,,USRI.7)
	CAIGE	S2," "			;CONTROL CHARACTER?
	JRST	USRI.1			;YES, ASSUME END OF LINE
	CAIE	S2,"["			;START OF PPN?
	JRST	USRI.2			;NO, KEEP LOOKING
	PUSHJ	P,USRI.4		;YES, GET PROJECT NUMBER
	JUMPF	USRI.3			;ERROR ALREADY GIVEN
	CAIE	S2,","			;TERMINATOR OK?
	FATAL	(SEU,<Syntax error in USERS.TXT>,,USRI.7)
	MOVSM	T1,USRPPN		;YES, SAVE PRJNUM
	PUSHJ	P,USRI.4		;GET PRGNUM
	JUMPF	USRI.3			;ERROR ALREADY GIVEN
	CAIE	S2,"]"			;TERMINATOR OK?
	FATAL	(SEU,<Syntax error in USERS.TXT>,,USRI.7)
	HRRM	T1,USRPPN		;YES, SAVE PRGNUM
	MOVE	S1,USRPPN		;GET PPN JUST PARSED
	PUSHJ	P,A$CKPP##		;TEST IF IT'S RESERVED
	JUMPF	USRI.3			;YES, DON'T BOTHER WITH IT
	MOVE	S1,USRIFN		;GET FILE HANDLE AGAIN
	$CALL	F%IBYT			;GET NEXT CHARACTER
	SKIPF				;IT BETTER SUCCEED
	CAIE	S2,","			;AND LOOK RIGHT
	FATAL	(SEU,<Syntax error in USERS.TXT>,,USRI.7)
	SETZM	FIXNAM			;CLEAR OUT OUR BLOCK
	MOVE	S2,[FIXNAM,,FIXNAM+1]	;XFER WORD
	BLT	S2,FIXNAM+.AANLW-1	;CLEAR ALL OF IT
	MOVE	T1,[POINT 8,FIXNAM]	;STORAGE POINTER
	MOVEI	T2,.AANLC		;CHARACTER LIMIT
	PUSHJ	P,USRI.8		;GET THE NAME
	SKIPN	FIXNAM			;ANYTHING THERE?
	JRST	USRI.1			;NO, FORGET IT
	MOVEI	S1,FIXNAM		;YES, POINT TO IT
	PUSHJ	P,A$CKNM##		;SEE IF NAME IS RESERVED
	JUMPF	USRI.1			;DON'T BOTHER WITH IT IF SO
	IBP	T1			;OK, SKIP PAST A NULL
	HRRZI	S2,1-FIXNAM(T1)		;GET TOTAL WORD LENGTH OF STRING
	PUSHJ	P,TREINS		;STICK INTO THE TREE
	JRST	USRI.1			;GO UNTIL EOF

USRI.3:	MOVE	S1,USRIFN		;RECOVER FILE HANDLE

USRI.5:	$CALL	F%IBYT			;EAT ANOTHER CHARACTER
	SKIPT				;EOF NOT ALLOWED HERE
	FATAL	(SEU,<Syntax error in USERS.TXT>,,USRI.7)
	CAIL	S2," "			;CONTROL CHARACTER?
	JRST	USRI.5			;NO, KEEP LOOKING
	JRST	USRI.1			;YES, START ANOTHER LINE

USRI.4:	SETZ	T1,			;PLACE TO GET OCTAL NUMBER

USRI.6:	$CALL	F%IBYT			;GET ANOTHER CHARACTER
	SKIPT				;BETTER WORK
	WARN	(BPU,<Bad PPN in USERS.TXT ignored>,,.RETF)
	CAIL	S2,"0"			;IS IT AN OCTAL DIGIT?
	CAILE	S2,"7"			;CHECK
	$RETT				;NO, WE'RE DONE HERE
	LSH	T1,3			;YES, SHIFT NUMBER OVER
	TRO	T1,-"0"(S2)		;INCLUDE THE DIGIT
	TLNN	T1,-1			;RANGE TEST
	JRST	USRI.6			;STILL OK SO LOOP
	WARN	(BPU,<Bad PPN in USERS.TXT ignored>,,.RETF)

USRI.8:	$CALL	F%IBYT			;GET NEXT CHARACTER OF NAME
	$RETIF				;QUIT AT EOF
	CAIGE	S2," "			;STILL A PRINTING CHARACTER?
	$RETT				;NO, WE'RE DONE
	CAIL	S2,"a"			;YES, IS IT LOWER CASE
	CAILE	S2,"z"			; ?
	CAIA				;NO
	TRZ	S2,40			;YES, RAISE CAISE
	IDPB	S2,T1			;STUFF INTO BLOCK
	SOJG	T2,USRI.8		;LOOP AS LONG AS WE HAVE ROOM

USRI.7:	SKIPN	S1,USRLIS		;DID WE BUILD AN INDEX?
	POPJ	P,			;NO, IGNORE THINGS HERE
	$CALL	L%FIRST			;YES, GET ITS FIRST ENTRY
	SKIPF				;IF NO FIRST,
	CAME	S2,USRMNA		;OR INDEX IS INCONSISTENT,
	FATAL	(CBI,<Can't build index for USERS.TXT, proceeding without it after all>,,USRI.9)
	INFO	(UIB,<USERS.TXT index has been built>)
	$RETT				;WE WIN

USRI.9:	MOVE	S1,USRLIS		;GET LIST HANDLE
	SETZM	USRLIS			;INVALIDATE IT FOR SEARCHERS
	PJRST	L%DLST			;DELETE THE ENTIRE LIST
	SUBTTL	Insert USERS.TXT index entries

TREINS:	ADDI	S2,2			;ADJUST BY OUR OVERHEAD
	MOVEM	S2,USRLEN		;SAVE TEXT BLOCK LENGTH
	SKIPN	USRLIS			;IS THIS THE FIRST ENTRY?
	JRST	TREI.1			;YES, GO CREATE THE LIST
	MOVE	S1,USRPPN		;GET INCOMING PPN
	CAMGE	S1,USRMIN		;IS THIS A NEW MINIMUM?
	JRST	TREI.2			;YES, INSERT AT THE BEGINNING
	CAMLE	S1,USRMAX		;IS THIS A NEW MAXIMUM?
	JRST	TREI.3			;YES, INSERT AT THE TAIL
	MOVE	T1,USRHED		;NO, START AT THE ROOT AND TRUDGE ALONG

TREI.4:	MOVE	S1,USRPPN		;GET INCOMING PPN AGAIN
	CAMN	S1,0(T1)		;IS THIS A DUPLICATE?
	WARN	(DED,<Duplicate entry for ^U/USRPPN/ discarded>,,.RETF)
	CAML	S1,0(T1)		;PREDECESSOR?
	JRST	TREI.5			;NO, SUCCESSOR
	HLRZ	S2,1(T1)		;YES, GET PREDECESSOR LINK
	JUMPE	S2,TREI.7		;TIME TO INSERT IF NO LEFT SON
	MOVE	T1,S2			;ADJUST CURRENT NODE
	JRST	TREI.4			;KEEP SEARCHING & SORTING

TREI.7:	MOVEI	T2,L%CBFR		;CREATE BEFORE CURRENT ENTRY
	PUSHJ	P,TREI.6		;COPY DATA TO THIS NEW BLOCK
	HRLM	S2,1(T1)		;ADJUST LEFT-SON POINTER
	$RETT				;BINGO

TREI.6:	MOVE	S1,USRLIS		;GET LIST HANDLE
	MOVE	S2,T1			;COPY NODE POINTER
	$CALL	L%APOS			;POSITION BY ADDRESS
	SKIPT				;BETTER BE OK
	$STOP	(ICE,<Index consistency error>)

TREI.9:	MOVE	S2,USRLEN		;GET DESIRED BLOCK LENGTH
	PUSHJ	P,(T2)			;LINK IN REQUIRED DIRECTION
	MOVE	S1,USRPPN		;GET INCOMING PPN
	MOVEM	S1,0(S2)		;SET IN BLOCK
	MOVEI	S1,2(S2)		;WHERE THE TEXT WILL GO
	HRLI	S1,FIXNAM		;WHENCE IT COMES
	MOVE	T2,USRLEN		;TEXT LENGTH
	ADDI	T2,-1(S2)		;END OF BLT
	BLT	S1,(T2)			;COPY THE TEXT
	POPJ	P,			;RETURN TO LINK INTO TREE

TREI.5:	HRRZ	S2,1(T1)		;GET RIGHT SON POINTER
	JUMPE	S2,TREI.8		;TIME TO INSERT IF NO RIGHT SON
	MOVE	T1,S2			;ADJUST CURRENT NODE
	JRST	TREI.4			;AND KEEP LOOKING FOR A SPOT

TREI.8:	MOVEI	T2,L%CENT		;LINK AFTER CURRENT ENTRY
	PUSHJ	P,TREI.6		;COPY DATA TO THIS NEW BLOCK
	HRRM	S2,1(T1)		;UPDATE RIGHT-SON POINTER
	$RETT				;BINGO

TREI.1:	$CALL	L%CLST			;CREATE A NEW LIST
	MOVEM	S1,USRLIS		;SAVE HANDLE
	MOVEI	T2,L%CENT		;CREATE AFTER NIL
	PUSHJ	P,TREI.9		;MAKE AN ENTRY AND COPY DATA TO IT
	MOVEM	S2,USRMNA		;MIN ADDRESS
	MOVEM	S2,USRMXA		;MAX ADDRESS
	MOVEM	S2,USRHED		;ROOT OF TREE
	MOVE	S1,USRPPN		;PPN JUST INSERTED
	MOVEM	S1,USRMIN		;IS LEAST
	MOVEM	S1,USRMAX		;AND LARGEST
	$RETT				;BINGO

TREI.2:	MOVE	T1,USRMNA		;ADDRESS OF OLD MINIMUM
	PUSHJ	P,TREI.7		;LINK IN A PREDECESSOR
	MOVE	S1,USRPPN		;INCOMING PPN
	MOVEM	S1,USRMIN		;IS NEW MIN PPN
	MOVEM	S2,USRMNA		;NEW MINIMUM'S ADDRESS
	POPJ	P,			;AND RETURN

TREI.3:	MOVE	T1,USRMXA		;ADDRESS OF OLD MAXIMUM
	PUSHJ	P,TREI.8		;LINK IN A SUCCESSOR
	MOVE	S1,USRPPN		;INCOMING PPN
	MOVEM	S1,USRMAX		;IS NEW MAX PPN
	MOVEM	S2,USRMXA		;NEW MAXIMUM'S ADDRESS
	POPJ	P,			;AND RETURN
	SUBTTL	Main conversion loop

CONVRT:	MOVE	S1,[PRODEF,,PROFIL]	;XFER WORD
	BLT	S1,PROFIL+.AEMAX-1	;INITIALIZE FROM THE DEFAULT PROFILE
	SKIPN	S1,AUXPPN		;GET PPN OF AUXACC ENTRY
	JRST	CONV.1			;THERE IS NONE
	SKIPN	S2,ACCT+.ACPPN		;IS THERE AN ACCT.SYS ENTRY?
	JRST	CONV.0			;NO, SO WE WANT TO DO AUXACC
	CAML	S1,S2			;IS IT TIME TO SPLICE IN AUXACC?
	JRST	CONV.1			;NO, DON'T BOTHER ME

CONV.0:	MOVEM	S1,PPN			;YES, SAVE WHAT WE'RE DOING
	MOVEM	S1,PROFIL+.AEPPN	;SET IN MAIN PROFILE
	MOVX	S2,AE.FIO		;POSSIBLE FILES-ONLY PPN
	TRC	S1,-1			;CHANGE ITS FORM
	TRCE	S1,-1			;IS IT OF THE FORM [*,%]?
	IORM	S2,PROFIL+.AEACC	;NO, DON'T ALLOW IT TO LOG IN
	SETOM	AUXFLG			;THIS IS FOR AUXACC
	PUSHJ	P,CNVSPC		;ONLY CONVERT THE SPECIAL ENTRY FORMS
	JRST	CONV.2			;GO INSERT THE NEW ENTRY

CONV.1:	SETZM	AUXFLG			;FLAG A REAL USER ACCOUNT
	MOVE	S1,ACCT+.ACPPN		;GET PROFILE'S PPN
	MOVEM	S1,PPN			;SAVE FOR THOSE WHO CARE
	PUSHJ	P,CNVDAT		;CONVERT THE PROFILE

CONV.2:	MOVEI	S1,PROFIL		;POINT TO THE ENTRY WE WANT TO ADD
	PUSHJ	P,PUTA##		;INSERT IT INTO THE FILE
	JUMPT	CONV.3			;CONTINUE ON SUCCESS
	MOVEI	S1,3			;LAST RAB ERROR
	PUSHJ	P,OPTA##		;GET RMS CODE
	CAIE	S1,ER$DUP##		;IS IT A DUPLICATE KEY?
	JRST	CONV.4			;NO, DON'T TRY TO MAKE THINGS WORSE
	SETZM	FIXNAM			;CLEAR NAME WORD
	MOVE	S1,[FIXNAM,,FIXNAM+1]	;XFER WORD
	BLT	S1,FIXNAM+.AANLW+1	;CLEAR ENTIRE BLOCK
	SKIPN	PROFIL+.AENAM		;HAVE A PREVIOUS NAME?
	JRST	CONV.6			;NO, DO SHORT NAME
	$TEXT	(<POINT 8,FIXNAM,-1>,<^Q/NAMPTR/-^O/PPN,LHMASK/,^O/PPN,RHMASK/^0>)			;YES, CREATE A FIXED-UP NAME
	JRST	CONV.7			;SKIP SHORT NAME

CONV.6:	MOVE	S1,PPN			;GET THE PPN
	PUSHJ	P,A$CKPP##		;GENERATE SOME TEXT
	$TEXT	(<POINT 8,FIXNAM,-1>,<^T/(S1)/^0>) ;GENERATE DEFAULT NAME

CONV.7:	MOVEI	S1,377_4		;MASK FOR LOW-ORDER BYTE
	TDNE	S1,FIXNAM+.AANLW-1	;DID IT FIT?
	JRST	CONV.4			;NO, GIVE UP
	MOVE	S1,[FIXNAM,,PROFIL+.AENAM] ;XFER WORD
	BLT	S1,PROFIL+.AENAM+.AANLW-1 ;MOVE THE NEW NAME
	MOVEI	S1,PROFIL		;POINT TO OUR PROPOSED RECORD
	PUSHJ	P,PUTA##		;TRY IT AGAIN
	JUMPT	CONV.3			;FINE IF IT WORKED

CONV.4:	MOVEI	S1,3			;GIVE UP
	PUSHJ	P,OPTA##		;GET LAST RMS ERROR CODE
	AOS	FAICNT			;COUNT OUR FAILURES
	FATAL	(EWF,<Error writing output file for ^U/PPN/; RMS error is ^O/S1/>,,CONV.5)

CONV.3:	PUSHJ	P,CVTPPF		;FORMAT THE PPN
	$TEXT	(,< ^T/(S1)/ ^Q/NAMPTR/>) ;DISPLAY SOMETHING
	AOS	COUNT			;NOTE WE CONVERTED ANOTHER SUCCESSFULLY

CONV.5:	MOVE	S1,PPN			;GET THE NEW PPN
	CAML	S1,AUXPPN		;DID WE USE UP OUR AUXACC ENTRY?
	PUSHJ	P,AUXYNK		;YES, GET THE NEXT ONE
	MOVE	S1,PPN			;GET THE PPN WE'RE INSERTING AGAIN
	CAML	S1,ACCT+.ACPPN		;DID WE USE UP OUR ACCT.SYS ENTRY?
	PUSHJ	P,ACCYNK		;YES, FETCH THE NEXT
	SKIPN	AUXPPN			;IF NO MORE AUXACC,
	SKIPE	ACCT+.ACPPN		;AND NO MORE ACCT.SYS
	JRST	CONVRT			;(NO, KEEP CONVERTING)
	POPJ	P,			;THEN WE'VE FINISHED CONVERTING
	SUBTTL	Final cleanup

FINISH:	PUSHJ	P,CLSA##		;CLOSE THE OUTPUT FILE
	JUMPF	FINI.1			;LAST CHANCE TO CARE ABOUT ERRORS
	MOVE	S1,ACCIFN		;POINT TO ACCT.SYS
	$CALL	F%REL			;RELEASE THE CHANNEL
	MOVE	S1,AUXIFN		;POINT TO AUXACC.SYS
	$CALL	F%REL			;RELEASE THE CHANNEL
	SKIPE	S1,USRIFN		;IF WE HAD A USERS.TXT,
	$CALL	F%REL			;GET RID OF ITS CHANNEL
	SETZM	ACCIFN			;CLEAR OUT THE IFNS
	SETZM	AUXIFN			;FOR NEATNESS'S SAKE
	SETZM	USRIFN			;BECAUSE IT LOOKS BETTER
	SKIPE	S1,USRLIS		;IF WE MADE A LINKED LIST,
	$CALL	L%DLST			;DESTROY IT
	SETZM	USRLIS			;FORGET THE LIST HANDLE
	MOVE	S1,COUNT		;GET OUR SUCCESS COUNT
	MOVEI	T1,[ITEXT (<^D/COUNT/ entries converted>)] ;ASSUME SEVERAL
	CAIN	S1,1			;CORRECT?
	MOVEI	T1,[ITEXT (<One entry converted>)] ;ONLY ONE
	SKIPN	S1			;FINAL CHECK
	MOVEI	T1,[ITEXT (<No entries converted>)] ;HOW CAN THIS HAPPEN?
	MOVE	S1,FAICNT		;GET OUR FAILURE COUNT
	MOVEI	T2,[ITEXT (<; there were ^D/FAICNT/ failures>)] ;ASSUME SOME
	CAIN	S1,1			;CORRECT?
	MOVEI	T2,[ITEXT (<; there was one failure>)] ;A BETTER SITUATION
	SKIPN	S1			;FINAL CHECK
	MOVEI	T2,[ITEXT (<>)]		;BEST OF ALL
	INFO	(SUM,<^I/(T1)/^I/(T2)/>) ;GIVE A SUMMARY
	PJRST	M%CLNC			;RETURN AFTER CLEANING CORE

FINI.1:	MOVEI	S1,2			;LAST FAB ERROR
	PUSHJ	P,OPTA##		;ASK RMS
	FATAL	(ECO,<Error closing output file, RMS status is ^O/S1/>,,DIEBAD)
	SUBTTL	Read an entry from ACCT.SYS

ACCYNK:	MOVE	S1,ACCIFN		;GET OUR FILE HANDLE
	MOVSI	T1,-.ACLEN		;POINTER TO FILL UP A BLOCK

ACCY.1:	$CALL	F%IBYT			;GET A WORD FROM THE FILE
	JUMPF	ACCY.2			;CHECK IF EOF OR OTHER ERROR
	MOVEM	S2,ACCT(T1)		;STORE PROFILE WORD
	AOBJN	T1,ACCY.1		;GET AN ENTIRE PROFILE INTO THE BLOCK
	SKIPN	ACCT+.ACPPN		;IS THIS A VALID ENTRY?
	JRST	ACCYNK			;NO, TRY AGAIN
	$RETT				;YES, RETURN IT

ACCY.2:	CAIN	S1,EREOF$		;IF EOF RETURNED,
	TRNE	T1,-1			;AT THE START OF A PROFILE,
	FATAL	(AFE,<ACCT.SYS file error:  ^E/S1/>,,.+1) ;NO, COMPLAIN
	SETZM	ACCT+.ACPPN		;NOTE THE EOF
	$RETF				;AND RETURN
	SUBTTL	Read an entry from AUXACC.SYS

AUXYNK:	MOVE	S1,AUXIFN		;GET FILE HANDLE FOR AUXACC.SYS
	$CALL	F%IBYT			;READ START OF NEXT BLOCK
	JUMPF	AUXY.F			;CHECK FAILURE FOR EOF
	AOSE	S2			;IS IT THE EXPECTED FLAG?
	FATAL	(JIF,<Junk in AUXACC.SYS file>,,AUXY.Z) ;NO, GIVE UP
	$CALL	F%IBYT			;YES, GET 'LENGTH' WORD
	JUMPF	AUXY.X			;REQUIRED
	MOVEI	T1,(S2)			;COPY IT
	IDIVI	T1,5			;SEE HOW MANY SUB-ENTRIES
	CAIE	T2,1			;MAKE SURE THE FORMAT IS VALID
	FATAL	(FEX,<Format error in AUXACC.SYS>,,AUXY.Z) ;NO, GIVE UP
	MOVNI	S2,-1(S2)		;GET MINUS LENGTH OF REAL AUXACC STUFF
	HRLZM	S2,AUXLEN		;STORE FOR LATER A$EBLK CALL
	SETZM	AUXBLK			;CLEAR THE BUFFER
	MOVE	T2,[AUXBLK,,AUXBLK+1]	;XFER WORD
	BLT	T2,AUXBLK+.AUMAX-1	;SPREAD THE ZEROS AROUND A LITTLE
	$CALL	F%IBYT			;GET THE PPN OF THIS ENTRY
	MOVEM	S2,AUXPPN		;SAVE FOR LATER
	JUMPF	AUXY.X			;REQUIRED
	MOVEI	T2,AUXBLK		;POINT TO THE BLOCK WE'RE GOING TO FILL

AUXY.1:	SOJL	T1,AUXY.2		;CHECK USEFULNESS AT END OF ENTRY
	$CALL	F%IBYT			;GET NEXT STRUCTURE
	JUMPF	AUXY.X			;REQUIRED
	MOVEM	S2,.AUSTR(T2)		;SAVE AWAY
	$CALL	F%IBYT			;GET ITS RESERVED QUOTA
	JUMPF	AUXY.X			;REQUIRED
	MOVEM	S2,.AURES(T2)		;SAVE IT
	$CALL	F%IBYT			;GET ITS FCFS QUOTA
	JUMPF	AUXY.X			;REQUIRED
	MOVEM	S2,.AULIN(T2)		;SAVE THAT
	$CALL	F%IBYT			;GET LOGGED-OUT QUOTA
	JUMPF	AUXY.X			;REQUIRED
	MOVEM	S2,.AUOUT(T2)		;SAVE AWAY
	$CALL	F%IBYT			;GET THE STATUS BITS
	JUMPF	AUXY.X			;REQUIRED
	MOVEM	S2,.AUBIT(T2)		;SAVE IT
	ADDI	T2,.AULEN		;UPDATE SUB-BLOCK POINTER
	JRST	AUXY.1			;LOOP OVER ALL SUB-BLOCKS FOR THIS ENTRY

AUXY.2:	SKIPN	AUXPPN			;DID WE CARE AFTER ALL?
	JRST	AUXYNK			;NO, TRY AGAIN
	$RETT				;YES, RETURN THE BLOCK

AUXY.F:	CAIN	S1,EREOF$		;WAS IT THE EXPECTED EOF?
	JRST	AUXY.Z			;YES, JUST GIVE UP

AUXY.X:	CAIE	S1,EREOF$		;PREMATURE EOF?
	FATAL	(UXE,<Unexpected AUXACC.SYS error:  ^E/S1/>,,AUXY.Z)
	FATAL	(PEX,<Premature end of AUXACC.SYS>,,AUXY.Z)

AUXY.Z:	SETZM	AUXPPN			;NO MORE ENTRIES
	$RETF				;RETURN
	SUBTTL	Convert an entry

CNVDAT:	MOVEI	T4,XFRBPT		;GET POINTER TO TRANSFER BPT'S

CNVD.1:	SKIPN	0(T4) 			;CHECK FOR END OF LIST
	JRST	CNVSPC			;DONE, NOW DO NON-SIMPLE TASKS.
	LDB	T1,0(T4)		;GET OLD VALUE
	DPB	T1,1(T4)		;STORE IN NEW PLACE
	ADDI	T4,2			;POINT TO NEXT SERIES OF BPT'S
	JRST	CNVD.1

;HERE TO DO THE NON-TRIVIAL CONVERSIONS

CNVSPC:	PUSHJ	P,CNVNAM		;CONVERT THE NAME
	PUSHJ	P,CNVPSW		;CONVERT THE PASSWORD
	PUSHJ	P,CNVCOR		;CONVERT THE CORE LIMITS
	PUSHJ	P,CNVPGR		;CONVERT THE PROGRAM-TO-RUN
	PUSHJ	P,CNVEXP		;CONVERT THE EXPIRATION DATE
	PUSHJ	P,CNVAUX		;CONVERT THE AUXACC DATA (IF ANY)
;	PUSHJ	P,CNVCUS		;MAYBE CONVERT CUSTOMER DATA
	POPJ	P,			;DONE
	SUBTTL	Convert a username

CNVNAM:	MOVE	S1,PPN			;GET THE PPN WE'RE CONVERTING
	PUSHJ	P,A$CKPP##		;CHECK IF RESERVED AND GET A NAME
	JUMPF	CNVNMR			;RESERVED NAMES NEED LOOK NO FURTHER
	MOVEM	S1,NAMTXT		;NOT RESERVED, SAVE THE TEXT ADDRESS

CNVNM1:	SKIPN	S1,USRLIS		;IS THERE AN INDEX FOR USERS.TXT?
	JRST	CNVNM4			;NO, DON'T TRY TO USE IT
	MOVE	S2,PPN			;YES, GET CURRENT PPN AGAIN
	CAMN	S2,USRMIN		;IS THIS THE NEXT ENTRY TO USE?
	JRST	CNVNMF			;YES, GO GET A NAME FROM THE FILE
	CAML	S2,USRMIN		;DID WE SKIP ONE BY?
	JRST	CNVNMG			;YES, GO FLUSH IT

CNVNM4:	SKIPE	AUXFLG			;NO USERS.TXT, SHOULD WE CHECK ACCT.SYS?
	JRST	CNVNM2			;NO, SKIP THIS
	MOVE	S2,[POINT 6,ACCT+.ACNM1] ;SOURCE POINTER
	PUSHJ	P,SIXLEN		;SETUP FOR SIXBIT TO ASCII CONVERSION
	MOVE	T3,[POINT 8,PROFIL+.AENAM] ;DESTINATION POINTER
	JUMPN	S1,CNVNM5		;GO DO IT IF NON-NULL NAME
	HRRZ	T2,PPN			;GET THE PRGNUM
	CAIE	T2,-2			;ONLY ALLOW BLANK NAME FOR [*,#]
	JRST	CNVNM2			;NO, USE DEFAULT STRING
	SETZ	T2,			;YES, RESET TO USE NO BYTES
CNVNM5:	EXTEND	S1,[EXP <MOVSO 40>,0]	;STRING TRANSLATION
CNVNM2:	$TEXT	(<POINT 8,PROFIL+.AENAM,-1>,<^T/@NAMTXT/^0>) ;OR STRING MOVING

CNVNM3:	MOVEI	S1,PROFIL+.AENAM	;POINT TO NAME IN QUESTION
	PUSHJ	P,A$CKNM##		;TEST IF IT'S RESERVED
	$RETIT				;WIN IF IT ISN'T
	SETZM	PROFIL+.AENAM		;CLEAR START OF NAME
	MOVE	S1,[PROFIL+.AENAM,,PROFIL+.AENAM+1] ;XFER WORD
	BLT	S1,PROFIL+.AENAM+.AANLW-1 ;CLEAR REST OF NAME IN PROFILE
	JRST	CNVNM2			;USE THE DEFAULT TEXT

CNVNMR:	$TEXT	(<POINT 8,PROFIL+.AENAM,-1>,<^T/(S1)/^0>) ;JUST USE THIS NAME
	POPJ	P,			;IT'S GOOD IF RESERVED TO US

CNVNMG:	WARN	(UES,<USERS.TXT entry for ^U/USRMIN/ wasn't referenced>)
	PUSHJ	P,CNVNMD		;DELETE THE USELESS NAME
	JRST	CNVNM1			;TRY AGAIN TO GET A NAME

CNVNMD:	MOVE	S1,USRLIS		;WHICH LIST ARE WE TALKING ABOUT
	$CALL	L%DENT			;DELETE THE CURRENT ENTRY
	MOVE	S1,USRLIS		;GET LIST HANDLE AGAIN (JUST IN CASE)
	$CALL	L%FIRST			;MAKE SURE WE HAVE A 'CURRENT' ENTRY
	JUMPF	CNVNME			;END OF LINKED LIST
	MOVE	S1,0(S2)		;GET THE PPN
	MOVEM	S1,USRMIN		;SAVE FOR COMPARISONS
	MOVEM	S2,USRMNA		;DATA ADDRESS (FOR THOSE WHO CARE)
	$RETT				;RETURN WITH A NEW LIST FRONT

CNVNME:	MOVE	S1,USRLIS		;GET HANDLE AGAIN
	$CALL	L%DLST			;DELETE THE LIST ITSELF
	SETZM	USRLIS			;HANDLE NO LONGER VALID
	$RETF				;NO MORE NAMES TO BE FOUND HERE

CNVNMF:	$CALL	L%SIZE			;GET ENTRY'S DATA LENGTH IN WORDS
	MOVEI	S1,PROFIL+.AENAM-1-2(S2) ;FORM END ADDRESS OF BLT
	MOVE	S2,USRMNA		;GET ADDRESS OF ENTRY
	MOVSI	S2,2(S2)		;SOURCE FROM NAME PORTION THEREOF
	HRRI	S2,PROFIL+.AENAM	;DESTINATION IS THE PROFILE
	BLT	S2,(S1)			;MOVE THE NAME
	PJRST	CNVNMD			;DELETE THIS USED ENTRY AND RETURN
	SUBTTL	Convert a password

CNVPSW:	SETZM	PSWBLK			;MAKE SURE THE BLOCK STARTS OFF NULL
	MOVE	S1,[PSWBLK,,PSWBLK+1]	;XFER WORD
	BLT	S1,PSWBLK+.APWLW-1	;CLEAR OUT THE ENTIRE BLOCK
	SKIPE	AUXFLG			;IF THIS IS FOR AUXACC,
	JRST	CNVPS1			;DON'T FETCH FROM PROFILE
	MOVE	S1,ACCT+.ACPSW		;GET THE PASSWORD
IFN NCRYPT!FTISWS,<
 IFN FTISWS,<
	SKIPE	CPTFLG			;ARE WE ENCRYPTING?
 >
	JRST	CNVPS2			;YES, GO DEAL WITH ENCRYPTION
> ;END IF IFN NCRYPT
	MOVEM	S1,PSWSIX		;STORE SIXBIT TEXT FOR CONVERSION
	MOVE	S2,[POINT 6,PSWSIX]	;POINT TO PASSWORD SOURCE
	PUSHJ	P,SIXLEN		;SETUP FOR MOVSO
	MOVE	T3,[POINT 8,PSWBLK]	;WHERE TO STORE THE EXPANDED TEXT
	EXTEND	S1,[EXP <MOVSO 40>,0]	;CONVERT SIXBIT TO 8-BIT ASCII
	  JFCL				;SHOULD ALWAYS SKIP

CNVPS1:	MOVEI	S1,PSWBLK		;PASSWORD TEXT
	MOVEI	S2,PROFIL		;THE PROFILE WE'RE CREATING
	PUSHJ	P,SETPSW##		;STORE AN ENCRYPTED PASSWORD
	JUMPT	CNVPS3			;CONTINUE IF WON
	MOVE	S1,CURALG##		;GET FAILING ALGORITHM
	WARN	(EAF,<Encryption algorithm ^O/S1/ failed for ^Q/NAMPTR/ ^U/PPN/>)			;COMPLAIN OF FAILURE
	SKIPN	PSWBLK			;WAS THERE ANYTHING TO ENCRYPT?
	JRST	CNVPS3			;YES, GIVE UP
	SETZM	PSWBLK			;CLEAR WORD OF BLOCK
	MOVE	S1,[PSWBLK,,PSWBLK+1]	;XFER VECTOR
	BLT	S1,PSWBLK+.APWLW-1	;CLEAR WHOLE BLOCK
	JRST	CNVPS1			;TRY AGAIN

IFN NCRYPT!FTISWS,<
CNVPS2:	MOVEM	S1,PSWBLK		;SAVE ENCRYPTED WORD IN BLOCK
	MOVEI	T1,PROFIL		;PROFILE WE'RE CREATING
	MOVE	T2,[-4,,.AEPSW]		;LENGTH AND OFFSET OF EXTENSIBLE BLOCK
	MOVEI	T3,PSWBLK		;WHERE WE STORED IT
	SETO	T4,			;WE ALREADY CLEARED THE BIT
	PUSHJ	P,A$EBLK##		;STUFF THE ALREADY ENCRYPTED PASSWORD IN
	MOVEI	S1,NCRYPT+4		;CONVERT 'CUSTOMER' INDEX TO 'DEC' INDEX
	STORE	S1,PROFIL+.AEFLG,AE.PWE	;SAVE WHICH ALGORITHM WAS USED
> ;END OF IFN NCRYPT

CNVPS3:
IFN PSWCHG,<
	SKIPN	AUXFLG			;IF FOR A REAL PPN,
	SETOM	PROFIL+.AEPCT		;REQUIRE A PASSWORD CHANGE AT NEXT LOGIN
>
	POPJ	P,			;DONE HERE
	SUBTTL	Convert core limits

CNVCOR:	SKIPE	AUXFLG			;IF FOR AUXACC,
	POPJ	P,			;CAN'T DO IT
	LOAD	S1,ACCT+.ACCIP,AC.NPP	;GET PHYSICAL LIMIT
	SKIPE	S1			;IF RESTRICTED,
	AOS	S1			;CONVERT TO REAL PAGE LIMIT
	STORE	S1,PROFIL+.AECOR,AE.NPP	;SET PHYSICAL LIMIT
	LOAD	S1,ACCT+.ACCIP,AC.NVP	;GET VIRTUAL LIMIT
	SKIPE	S1			;IF RESTRICTED,
	AOS	S1			;CONVERT TO REAL PAGE LIMIT
	STORE	S1,PROFIL+.AECOR,AE.NVP	;SET VIRTUAL LIMIT
	POPJ	P,			;DONE
	SUBTTL	Convert program-to-run

CNVPGR:	SKIPN	AUXFLG			;MUST BE A REAL PROFILE,
	SKIPN	S1,ACCT+.ACPGM		;WITH A REAL PROGRAM-TO-RUN
	POPJ	P,			;ELSE CAN DO NOTHING
	MOVEM	S1,PGRBLK+.FDNAM-.FDFIL	;SET NAME IN BLOCK
	SKIPN	S1,ACCT+.ACDEV		;GET DEVICE FOR PROGRAM IF GIVEN
	MOVSI	S1,'SYS'		;USE SYS: OTHERWISE
	MOVEM	S1,PGRBLK+.FDSTR-.FDFIL	;SET DEVICE IN BLOCK
	MOVE	S1,ACCT+.ACDIR		;GET PPN FOR PROGRAM
	MOVEM	S1,PGRBLK+.FDPPN-.FDFIL	;SET PPN IN BLOCK
	MOVEI	T1,PROFIL		;THE PROFILE TO AFFECT
	MOVE	T2,[.FDFIL-.FDPPN-1,,.AEPGR] ;LENGTH AND OFFSET TO INSERT
	MOVEI	T3,PGRBLK		;WHERE WE STORED IT
	SETO	T4,			;WE ALREADY CLEARED THE BIT
	PUSHJ	P,A$EBLK##		;INSERT THE PROGGIE INTO THE PROFILE
	POPJ	P,			;DONE HERE
	SUBTTL	Convert expiration date

CNVEXP:	SKIPE	S2,AUXFLG		;MUST BE FOR A REAL PROFILE
	POPJ	P,			;CAN DO NOTHING FOR AUXACC
	LOAD	S1,ACCT+.ACESE,AC.EXP	;GET OLD EXPIRATION DATE
	CAIN	S1,-1			;IF 'NEVER'
	SOJA	S2,CNVEX1		;PROPAGATE THE 'NEVER'
	MOVEI	T1,(S1)			;ELSE, COPY 15-BIT DATE FOR HACKING
	IDIVI	T1,^D<31*12>		;SEPARATE YEAR
	IDIVI	T2,^D31			;THEN MONTHS AND DAYS
	MOVEI	S1,1(T3)		;GET ACTUAL DAYS AS A BASE
	MOVEI	S2,^D1964(T1)		;GET REAL YEAR FOR CALCULATIONS
	CAIGE	T2,2			;JAN OR FEB?
	 SOSA	S2			;YES, DECREMENT YEAR
	CAIA				;NO, DON'T HIT MONTHS YET
	ADDI	T2,^D12			;YES, OFFSET FOR LEAP YEAR HACKERY
	ADDI	T2,2			;MAKE +1 OFFSET FROM -1
	IMULI	T2,^D153		;INTEGER MULTIPLY MONTH BY (153/5)
	IDIVI	T2,5			; TO GET CUMULATIVE MONTH TOTAL
	ADDI	S1,(T2)			;GET DAYS THIS YEAR
	MOVEI	T1,(S2)			;COPY YEAR FOR CONVERSION
	IMULI	S2,^D365		;GET BASIC COUNT OF DAYS FOR YEAR
	IDIVI	T1,4			;GET COUNT OF POSSIBLE LEAP YEARS
	ADDI	S2,(T1)			;THESE MADE EXTRA DAYS
	IDIVI	T1,^D25			;CONVERT TO CENTURIES
	SUBI	S2,(T1)			;THESE DIDN'T HAVE LEAP DAYS
	IDIVI	T1,4			;GET QUADRACENTURIES
	ADDI	S2,(T1)			;BUT THESE WERE LEAP YEARS AFTER ALL
	ADDI	S2,(S1)			;ADD COUNT FOR DAYS THIS YEAR
	SUB	S2,[^D679004]		;MAKE DAY 0=17-NOV-1858
	TLNE	S2,-1			;OVERFLOW?
	SETO	S2,			;YES, MAKE IT 'NEVER' AFTER ALL
	HRLOS	S2			;GO TO END OF SPECIFIED DAY

CNVEX1:	MOVEM	S2,PROFIL+.AEEXP	;SAVE EXPIRATION DATE AWAY
	POPJ	P,			;DONE HERE
	SUBTTL	Convert AUXACC data

CNVAUX:	MOVE	S1,PPN			;GET PPN WE'RE CONVERTING
	CAME	S1,AUXPPN		;DOES IT HAVE AN AUXACC ENTRY?
	POPJ	P,			;NOTHING WE CAN DO IF NOT
	MOVEI	T1,PROFIL		;WHICH PROFILE WE'RE DOING
	MOVE	T2,AUXLEN		;MINUS BLOCK LENGTH IN LH
	HRRI	T2,.AEAUX		;PROFILE OFFSET IN RH
	MOVEI	T3,AUXBLK		;WHERE WE STORED THE DATA
	SETZ	T4,			;CLEAR THE 'DEFAULTED' BIT
	PJRST	A$EBLK##		;STUFF INTO THE PROFILE AND RETURN
	SUBTTL	Format a PPN for display

CVTPPF:	PUSHJ	P,.SAVE2	;SAVE P1 AND P2
	MOVEI	P1,[ITEXT (<^O6R /S1/>)]
	MOVEI	P2,[ITEXT (<^O6L /S2/>)]
	HLRZ	S1,PPN		;GET PROJECT NUMBER
	HRRZ	S2,PPN		;AND PROGRAMMER NUMBER
	CAIN	S1,-1		;DEFAULT PROJECT?
	MOVEI	P1,[ITEXT (<     %>)] ;YES
	CAIN	S2,-1		;DEFAULT PROGRAMMER?
	MOVEI	P2,[ITEXT (<%     >)] ;YES
	CAIN	S2,-2		;WILD PROGRAMMER?
	MOVEI	P2,[ITEXT (<#     >)] ;YES
	$TEXT	(<-1,,PPNTXT>,<^I/(P1)/,^I/(P2)/^0>)
	MOVEI	S1,PPNTXT	;POINT TO TEXT
	POPJ	P,		;AND RETURN
	SUBTTL	Find the length of a SIXBIT string

SIXLEN:	SKIPN	0(S2)			;MAKE SURE
	SKIPE	S1,1(S2)		;THAT SOMETHING'S THERE
	CAIA				;IT IS
	JRST	SIXL.1			;NO, JUST RETURN THE ZERO
	SKIPN	T1,1(S2)		;CHECK LENGTH OF SECOND WORD
	MOVE	T1,0(S2)		;UNLESS ONLY ONE WORD IN LENGTH
	SKIPE	S1,1(S2)		;OFFSET LENGTH BY FIRST WORD
	MOVEI	S1,6			;IF PRESENT
	MOVN	T2,T1			;SHUFFLE SOME BITS
	AND	T1,T2			;FIND LAST BIT IN TRAILING WORD
	JFFO	T1,.+1			;GET ITS BIT NUMBER
	IDIVI	T2,6			;THEN ITS SIXBIT CHARACTER OFFSET
	ADDI	S1,1(T2)		;TOTAL, CORRECTING FOR 0 VS 1 ORIGIN

SIXL.1:	SETZB	T1,T4			;DON'T CONFUSE THE MICROCODE
	MOVEI	T2,(S1)			;TARGET IS AT LEAST AS LARGE AS SOURCE
	POPJ	P,			;ALL BUT T3 NOW SETUP
	SUBTTL	Random hiseg data

PDLPTR:	IOWD	PDLLEN,PDL		;STACK BASE

NAMPTR:	POINT	8,PROFIL+.AENAM		;POINTER TO CONFUSE ITEXT MACRO

ACCVWD:	4,,.ACLEN			;OUR EXPECTED FORMAT WORD OF ACCT.SYS

IB:	$BUILD	(IB.SZ)			;BUILD A LIBRARY INITIALIZATION BLOCK
	  $SET	(IB.PRG,,%%.MOD)	;PROGRAM NAME
	  $SET	(IB.FLG,IB.NPF,1)	;WE WANT TO USE UP MEMORY
	$EOB				;DEFAULT THE REST

ACCFDB:	$BUILD	(FDXSIZ)		;FD FOR ACCT.SYS
	  $SET	(.FDLEN,FD.LEN,FDXSIZ)	;REMEMBER SIZE OF BLOCK
	  $SET	(.FDSTR,,'DSK   ')	;DEVICE DSK:
	  $SET	(.FDNAM,,'ACCT  ')	;NAME ACCT
	  $SET	(.FDEXT,,'SYS   ')	;EXTENSION .SYS
	$EOB

ACCFOB:	$BUILD	(FOB.MZ)		;SMALL FOB
	  $SET	(FOB.FD,,ACCFDB)	;POINT TO FD
	  $SET	(FOB.CW,FB.BSZ,^D36)	;FULLWORD BYTE SIZE
	$EOB

AUXFDB:	$BUILD	(FDXSIZ)		;AUXACC.SYS FD
	  $SET	(.FDLEN,FD.LEN,FDXSIZ)	;REMEMBER SIZE OF BLOCK
	  $SET	(.FDSTR,,'DSK   ')	;DEVICE DSK:
	  $SET	(.FDNAM,,'AUXACC')	;NAME AUXACC
	  $SET	(.FDEXT,,'SYS   ')	;EXTENSION .SYS
	$EOB

AUXFOB:	$BUILD	(FOB.MZ)		;SMALL FOB
	  $SET	(FOB.FD,,AUXFDB)	;POINT TO FD
	  $SET	(FOB.CW,FB.BSZ,^D36)	;FULLWORD BYTE SIZE
	$EOB

USRFDB:	$BUILD	(FDXSIZ)		;USERS.TXT FD
	  $SET	(.FDLEN,FD.LEN,FDXSIZ)	;LENGTH OF FD
	  $SET	(.FDSTR,,'DSK   ')	;DEVICE DSK:
	  $SET	(.FDNAM,,'USERS ')	;NAME USERS
	  $SET	(.FDEXT,,'TXT   ')	;EXTENSION .TXT
	$EOB

USRFOB:	$BUILD	(FOB.MZ)		;SMALL FOB
	  $SET	(FOB.FD,,USRFDB)	;POINT TO FD
	  $SET	(FOB.CW,FB.LSN,1)	;NOTICE (& IGNORE) LSN'S
	  $SET	(FOB.CW,FB.BSZ,^D7)	;BYTE SIZE (TEXT, OF COURSE)
	$EOB
	SUBTTL	Impure (lowseg) data

	RELOC	0

ZERMEM:!				;START OF ZEROABLE DATA

PRODEF:	BLOCK	.AEMAX			;DEFAULT (BASE) PROFILE
PROFIL:	BLOCK	.AEMAX			;WORKING PROFILE
ACCT:	BLOCK	.ACLEN			;VERSION 4 PROFILE TO CONVERT
COUNT:	BLOCK	1			;COUNT OF SUCCESSFUL CONVERSIONS
FAICNT:	BLOCK	1			;COUNT OF UNRECOVERED RMS FAILURES
ACCNUM:	BLOCK	1			;NUMBER OF ENTRIES PRESUMED IN ACCT.SYS
AUXLEN:	BLOCK	1			;AOBJN LH FOR .AEAUX
AUXBLK:	BLOCK	.AUMAX			;SPACE FOR AN AUXACC ENTRY
AUXPPN:	BLOCK	1			;THE PPN FROM THE AUXACC ENTRY
PPN:	BLOCK	1			;OUR CURRENT WORKING PPN
AUXFLG:	BLOCK	1			;FLAG FOR WHETHER CONVERTEE IS A USER
PSWSIX:	BLOCK	2			;SPACE FOR A SIXBIT PASSWORD SOURCE
PSWBLK:	BLOCK	.APWLW			;SPACE FOR AN 8-BIT PASSWORD
FIXNAM:	BLOCK	.AANLW+2		;SPACE TO FIX UP A USERNAME
PGRBLK:	BLOCK	.FDPPN-.FDFIL+1		;SPACE FOR A PROGRAM-TO-RUN BLOCK
ACCIFN:	BLOCK	1			;IFN FOR ACCT.SYS
AUXIFN:	BLOCK	1			;IFN FOR AUXACC.SYS
USRIFN:	BLOCK	1			;IFN FOR USERS.TXT
USRLIS:	BLOCK	1			;LIST HANDLE FOR USERS.TXT DATA
USRMIN:	BLOCK	1			;LEAST PPN IN USERS.TXT
USRMNA:	BLOCK	1			;ADDRESS OF ENTRY DESCRIBING USRMIN
USRMAX:	BLOCK	1			;LARGEST PPN IN USERS.TXT
USRMXA:	BLOCK	1			;ADDRESS OF ENTRY FOR USRMAX
USRHED:	BLOCK	1			;ROOT OF TREE WHILE BUILDING USRLIS
USRPPN:	BLOCK	1			;THE CURRENT PPN FROM USERS.TXT
USRLEN:	BLOCK	1			;LENGTH OF ENTRY TO CREATE IN TREINS
NAMTXT:	BLOCK	1			;TEMP. WHILE ASSIGNING USERNAMES
OUTFIL:	BLOCK	^D<7*8+3+7*2+1>/5+1	;SPACE FOR ASCIZ FILESPEC
PPNTXT:	BLOCK	4			;FORMATTED PPN FOR DISPLAY

ZEREND==.-1				;LAST ZEROABLE WORD WAS ABOVE

PDL:	BLOCK	PDLLEN			;OUR STACK

	RELOC				;BACK TO HISEG FOR LITERALS


	END	NEWACT