!TITLE User Indexes
!<DBITMAP2
! %externalintegerfn DBITMAP2(%integername LO, HI, %integer FSYS)
!>
!<DDELUSER
! %externalintegerfn DDELUSER(%string(18)FILE INDEX, %integer FSYS)
!>
!
CONSTINTEGER  ERCC = 1
CONSTINTEGER  KENT = 0
CONSTINTEGER  SITE = KENT
CONSTINTEGER  BITKEY = 0
CONSTINTEGER  DIRLOG KB = 128
CONSTINTEGER  DLOG = 8; ! route PRINTSTRING to DIRLOG
CONSTINTEGER  ENDLIST = 255
CONSTINTEGER  LOGKB=64; ! Kbytes for logfiles
CONSTINTEGER  NNTKEY = 1; ! SYSAD
CONSTINTEGER  NO = 0
CONSTINTEGER  UNAVA = 1; ! CODES
CONSTINTEGER  VIOLAT = 64; ! CODES
CONSTINTEGER  WRSH = 11
CONSTINTEGER  YES = 1
CONSTINTEGER  BADKEY = 6; ! SYSAD
CONSTINTEGER  CHERSH = 16; ! CODES
CONSTINTEGER  DATKEY = 4; ! SYSAD
CONSTINTEGER  LEAVE = 8
CONSTINTEGER  LOSTFLEN = 48
CONSTINTEGER  OLDGE = 4; !CODES2
CONSTINTEGER  PON AND CONTINUE = 6
CONSTINTEGER  SYNC1 TYPE = 1
CONSTINTEGER  TEMPFS = 12
CONSTINTEGER  TOPLOST = 80
CONSTINTEGER  W = B'00000010';          !Write permission
CONSTINTEGER  COM36=X'00100004'
CONSTINTEGER  WRTOF=4, LOG=2, DT=1
CONSTINTEGER  SIG STACK SEG=6
CONSTINTEGER  CLEAR=255,NULL=0,SCREEN2=2,SCREEN SWITCH=1
CONSTINTEGER  LINES PER PAGE=24
CONSTINTEGER  GROUPFULLFLAG=5,SYSFULLFLAG=101,ADDEDFLAG=100
CONSTINTEGER  AMBIFLAG=31,NOFREEFLAG=70,NOTINLISTFLAG=50
CONSTINTEGER  TOPUG=7,ENDL=-1
CONSTINTEGER  PROCRESET=1,ADDTO=2,INCRE=4,RESETN=8,OLDSCREEN=16
CONSTINTEGER  REMOVE=32,DISPLAY=64,CHECKN=128,SIDECHAIN=512
CONSTSTRINGNAME  DATE = X'80C0003F'
CONSTINTEGER  NEXECPROCS = 4
CONSTSTRING (6)ARRAY  EXECPROCS(1:NEXECPROCS) = "FTRANS", "SPOOLR", "VOLUMS",
               "MAILER"
!
!
!
CONSTSTRINGNAME  TIME = X'80C0004B'
CONSTSTRING (5)ARRAY  LM(0:2)="FREE", "READY", "MAIN"
!
!
!
      RECORDFORMAT  C 
KYFF(STRING (11)NAME, INTEGER  A,B,C,D,E)
      RECORDFORMAT  C 
FHDRF(INTEGER  NEXTFREEBYTE,TXTRELST,MAXBYTES,THREE, C 
      SEMA,DATE,NEXTCYCLIC,READ TO)
!
INCLUDE  "PD22S_C03FORMATS"
!
!
      EXTERNALROUTINESPEC  C 
ATTU(STRINGNAME  S)
      EXTERNALINTEGERFNSPEC  C 
AV(INTEGER  FSYS, TYPE)
      EXTERNALINTEGERFNSPEC  C 
BAD PAGE(INTEGER  TYPE, FSYS, BITNO)
      EXTERNALINTEGERFNSPEC  C 
HINDA(STRING (6) USER,INTEGERNAME  FSYS,INDAD, INTEGER  TYPE)
      EXTERNALINTEGERFNSPEC  C 
COUNT PROCS IN(STRING (6)USERGROUP, INTEGERNAME  IPROCS)
      EXTERNALINTEGERFNSPEC  C 
CREATE AND CONNECT(STRING (31)FILE,
      INTEGER  FSYS, NKB, ALLOC, MODE, APF,
      INTEGERNAME  SEG, GAP)
      EXTERNALROUTINESPEC  C 
CYCINIT(INTEGER  ADR, MAXBYTES)
      EXTERNALROUTINESPEC  C 
DAPINTERFACE(INTEGER  ACT)
      EXTERNALINTEGERFNSPEC  C 
DCHSIZE(STRING (31)USER, FILE, INTEGER  FSYS, NKB)
      EXTERNALINTEGERFNSPEC  C 
DCONNECTI(STRING (31)FILE,INTEGER  FSYS,MODE,APF,
      INTEGERNAME  SEG,GAP)
      EXTERNALINTEGERFNSPEC  C 
DCREATEF(STRING (31)FILE,INTEGER  FSYS,NKB,ALLOC,LEAVE,
      INTEGERNAME  DA)
      EXTERNALINTEGERFNSPEC  C 
DDAYNUMBER
      EXTERNALINTEGERFNSPEC  C 
DDESTROYF(STRING (31)FILE, INTEGER  FSYS, TYPE)
      EXTERNALINTEGERFNSPEC  C 
DDISCONNECTI(STRING (31) FILE,INTEGER  FSYS,LO)
      ROUTINESPEC  C 
DDUMP(INTEGER  A,B,C,D)
      EXTERNALSTRINGFNSPEC  C 
DERRS(INTEGER  N)
      EXTERNALINTEGERFNSPEC  C 
DFILENAMES(STRING (18)INDEX, RECORD (KYFF)ARRAYNAME  F,
      INTEGERNAME  JUNK, MAX, N, INTEGER  FSYS, TYPE)
      EXTERNALINTEGERFNSPEC  C 
DFSTATUS(STRING (31)USER, FILE, INTEGER  FSYS, ACT, VALUE)
      EXTERNALROUTINESPEC  C 
DOUTI(RECORD (PARMF)NAME  P)
      EXTERNALINTEGERFNSPEC  C 
DPERMISSIONI(STRING (18) OWNER,USER,DATE,FILE,
      INTEGER  FSYS,TYPE,ADRPRM)
      EXTERNALROUTINESPEC  C 
DPONI(RECORD (PARMF)NAME  P)
      EXTERNALINTEGERFNSPEC  C 
DPON3I(STRING (6)USER, RECORD (PARMF)NAME  P, INTEGER  INVOC, TYPE, OUT)
      INTEGERFNSPEC  C 
DPRGP(STRING (18)INDEX, STRING (11)FNAME, STRING (6)LABEL,
      INTEGER  FSYS, SITE, DIRECTION)
      EXTERNALINTEGERFNSPEC  C 
DRENAME(STRING (18)USER, OLD, NEW, INTEGER  FSYS)
      EXTERNALINTEGERFNSPEC  C 
DSETPASSWORD(STRING (6)USER, INTEGER  FSYS, WHICH, STRING (63)OLD, NEW)
      EXTERNALROUTINESPEC  C 
EMPTY DVM
      EXTERNALINTEGERFNSPEC  C 
FBASE2(INTEGER  FSYS, ADR)
      EXTERNALROUTINESPEC  C 
FILL(INTEGER  LENGTH, FROM, FILLER)
      EXTERNALINTEGERFNSPEC  C 
FINDA(STRING (31)INDEX, INTEGERNAME  FSYS, FINDAD, INTEGER  TYPE)
      EXTERNALINTEGERFNSPEC  C 
FIND NNT ENTRY(STRING (18)INDEX, INTEGERNAME  FSYS, NNAD,INTEGER  TYPE)
      EXTERNALROUTINESPEC  C 
GETAVFSYS2(INTEGER  TYPE, INTEGERNAME  N, INTEGERARRAYNAME  A)
      EXTERNALINTEGERFNSPEC  C 
HASH(STRING (6)USER, INTEGER  NNTHASH)
      EXTERNALSTRINGFNSPEC  C 
HTOS(INTEGER  I, PL)
      EXTERNALINTEGERFNSPEC  C 
IN2(INTEGER  FN)
      EXTERNALSTRINGFNSPEC  C 
ITOS(INTEGER  I)
      EXTERNALINTEGERFNSPEC  C 
MAP FILE INDEX(STRINGNAME  INDEX, INTEGERNAME  FSYS, FINDAD, STRING (31)TXT)
      EXTERNALINTEGERFNSPEC  C 
MOVESECTION(INTEGER  FSYS1, STARTP1, FSYS2, STARTP2, EPGS)
      EXTERNALROUTINESPEC  C 
MOVE(INTEGER  LENGTH, FROM, TO)
      EXTERNALINTEGERFNSPEC  C 
NEWAINDA(STRING (18)INDEX, INTEGER  FSYS, INTEGERNAME  AFINDAD)
      EXTERNALINTEGERFNSPEC  C 
NEWFIND(INTEGER  FINDAD, DA, STRINGNAME  FILE)
      EXTERNALINTEGERFNSPEC  C 
NINDA(INTEGER  FSYS, INDNO, INTEGERNAME  INDAD)
      ROUTINESPEC  C 
OPER(INTEGER  CONSOLE, STRING (255)S)
      EXTERNALINTEGERFNSPEC  C 
OUT(INTEGER  FLAG, STRING (63)TEMPLATE)
      EXTERNALINTEGERFNSPEC  C 
PACKDT
      EXTERNALINTEGERFNSPEC  C 
PP(INTEGER  SEMADDR,SEMANO,STRING (63)S)
      ROUTINESPEC  C 
PREC(STRING (255)S, RECORD (PARMF)NAME  P, INTEGER  N)
      ROUTINESPEC  C 
PRHEX(INTEGER  I)
      EXTERNALINTEGERFNSPEC  C 
PRIME CONTINGENCY(ROUTINE  R)
      EXTERNALROUTINESPEC  C 
PROCESS1(INTEGER  A, B)
      EXTERNALINTEGERFNSPEC  C 
S11OK(STRINGNAME  S11)
      EXTERNALINTEGERFNSPEC  C 
STARTP(STRING (6)USER, STRINGNAME  FILE, STRING (63)ITADDR,
      INTEGERNAME  INVOC, INTEGER  FSYS, STARTCNSL, REASON, STREAM ID,
      DIRVSN, PROTOCOL)
      EXTERNALINTEGERFNSPEC  C 
STOI2(STRING (255)S, INTEGERNAME  I2)
      EXTERNALROUTINESPEC  C 
STOP ONE(INTEGER  A, B)
      EXTERNALINTEGERFNSPEC  C 
SYSAD(INTEGER  KEY, FSYS)
      EXTERNALINTEGERFNSPEC  C 
SYSBASE(INTEGERNAME  SYS START, INTEGER  FSYS)
      EXTERNALINTEGERFNSPEC  C 
TXTMESS(STRING (6) USER,RECORD (PARMF)NAME  RP,
      INTEGER  SYNC,INVOC,TXTLEN,TXTAD,FSYS,SACT)
      EXTERNALINTEGERFNSPEC  C 
UFO(STRING (31)USER, FILE, STRINGNAME  UNA, INA, FNA, INDEX, FULL)
      EXTERNALINTEGERFNSPEC  C 
UIO(STRING (31)USER, STRINGNAME  UNA, INA, INDEX)
      EXTERNALINTEGERFNSPEC  C 
UNOK(STRINGNAME  USER)
      EXTERNALINTEGERFNSPEC  C 
VAL(INTEGER  ADR, LEN, RW, PSR)
      EXTERNALROUTINESPEC  C 
VV(INTEGER  SEMADDR, SEMANO)
      EXTERNALROUTINESPEC  C 
WRS(STRING (255)S)
      EXTERNALROUTINESPEC  C 
WRSS(STRING (255)S1, S2)
      EXTERNALROUTINESPEC  C 
WRSN(STRING (255)S, INTEGER  N)
      EXTERNALROUTINESPEC  C 
WRSNT(STRING (255)S, INTEGER  N, T)
      EXTERNALROUTINESPEC  C 
WRS3N(STRING (255)S1, S2, S3, INTEGER  N)
!
!
!-----------------------------------------------------------------------
EXTRINSICINTEGER  D CALLERS PSR
EXTRINSICINTEGER  DIRLOGAD 
EXTRINSICINTEGER  DIRMON
EXTRINSICINTEGER  D TRYING 
EXTRINSICINTEGER  FILE1AD
EXTRINSICINTEGER  GOT SEMA
EXTRINSICINTEGER  LOG ACTION 
EXTRINSICINTEGER  OUTPAD
EXTRINSICINTEGER  PROCESS
EXTRINSICINTEGER  SELECTED FSYS
EXTRINSICSTRING (6)PROCUSER
EXTRINSICSTRING (18)SELECTED INDEX
EXTRINSICSTRING (127)SELECTED NODE
EXTRINSICSTRING (15)VSN
EXTERNALINTEGER  MONITORAD
!
!
!
!
OWNSTRING (31) DELIV="Machine Room"
OWNINTEGER  GMON=0
! This variable below is set non-zero when JOURNL requests the current logfile
! be spooled. The SPOOLR reply is passed on to the DEST specified.
OWNINTEGER  JOURNL DEST=0
OWNINTEGER  MAIN LP=1
OWNINTEGER  PRINT ON=0
OWNINTEGER  READY FILES=0
OWNRECORD (LF)ARRAYNAME  LOGS
!
!
!
!
OWNINTEGER  LOSTARAD
RECORDFORMAT  LOSTF(STRING (8) DATE,TIME,  C 
   STRING (6) USER,STRING (11) FILE,  C 
   BYTEINTEGER  CODES2,CODES,CHERISHED)
!
!
!
!-----------------------------------------------------------------------
!
!
!
!
!
!
!
CONSTSTRING  (1) SNL = "
"
OWNINTEGER  HEAD=ENDL,ASL=-2,CURIUSERS=0,MAXIUSERS=10
!
!
!
!
!
!
      RECORDFORMAT  C 
POPERF(INTEGER  DEST,SRCE,BYTEINTEGER  LINE,POS,ZERO,STRING (20)TEXT)
      RECORDFORMAT  C 
UGF(STRING (6)U, INTEGER  SUBLINK, LINK, MAX, N)
!
!
!
OWNRECORD (UGF)ARRAY  UG(0:TOPUG)
OWNINTEGER  LINE NO = 0
!
!
!
!-----------------------------------------------------------------------
!
EXTERNALSTRINGFN  FROMSTRING(STRING (255) S,INTEGER  I,J)
      UNLESS  0<I<=J AND  J<=LENGTH(S) THEN  RESULT =""
      I=I-1
      CHARNO(S, I) =J-I
      RESULT =STRING(ADDR(S)+I)
END ; ! FROMSTRING
!
!-----------------------------------------------------------------------
!
INTEGERFN  DBITS(INTEGER  N)
      *LB_0; ! BIT COUNT
      *LSS_N
      *JAT_4,<OUT>; ! J IF ZERO
LOOP:
      *ST_TOS 
      *USB_1
      *AND_TOS ; ! N = N & (N-1)
      *ADB_1
      *JAF_4,<LOOP>; ! J IF NOT ZERO
OUT:
      *LSS_B ; ! RESULT TO ACC
      *EXIT_-64; ! %RETURN
END ; ! DBITS
!
!-----------------------------------------------------------------------
!
INTEGERFN  SIZEOF(NAME  X)
INTEGER  I
      *LSS_(LNB +5)
      *ST_I
      RESULT  = (I<<8)>>8 UNLESS  I & X'C2000000' = 0
      I = (I >> 27) & 7
      RESULT  = 1 + ((X'F0') << I) >> 11
END ; ! SIZEOF
!
!-----------------------------------------------------------------------
!
INCLUDE  "PD22S_B13GENERAL"
INCLUDE  "PD22S_B11OPER"
externalintegerfn  SET FILE INDEX(string (6)USER, string (11)NAME,
      integer  FSYS, SIZE, NPD, NFD, FINDAD, INDNO)
!
! SIZE given in half K's
!
integer  FLAG, N, SDBYTES, J
record (FF)name  F
constinteger  PDSTART = 128
RECORD (FDF)ARRAYNAME  FDS
constinteger  TOPN = 19
constbyteintegerarray  V(0:TOPN) = 0, 1, 3, 4, 7, 8, c 
      15, 16, 23, 24, 31, 32, 39,40,47,48,55,56,63, 64
constbyteintegerarray  PDV(1:TOPN) = 8, 12, 32, 32, 48, 48, 48, 48, 48(11)
                          { SDs     29      48     180    1204     628  }
constintegerarray  FDV(1:TOPN) =     8, 37, 50, 93,100,200,200,200,200, 400(6),
            800(4)
      FLAG = 8
      cycle  J = TOPN, -1, 0
         N = J
         exit  if  V(N) = SIZE
      repeat 
      -> OUT if  N = 0; ! an invalid size
!
      NPD = PDV(N) if  NPD = 0
      NFD = FDV(N) if  NFD = 0
!
      -> OUT unless  8 <= NPD <= 128
      -> OUT unless  8 <= NFD <= 1024
      NPD = (NPD + 3) & (-4); ! make NPD a multiple of 4
      SDBYTES = SIZE << 9 - PDSTART - PDSIZE*NPD - FDSIZE*NFD
      -> OUT unless  0 <= SDBYTES <= 32768
!
      FLAG = 0
      F == RECORD(FINDAD)
      FILL(SIZE<<9, FINDAD, 0); ! clear the whole record
      F_OWNER = USER
      F_NAME = NAME
      F_FSYS = FSYS
      F_SEMA = -1
      F_SEMANO = FSYS << 16 ! INDNO
      F_ASEMA = -1
      F_PDSTART = PDSTART
      F_SDSTART = PDSTART + PDSIZE*NPD
      F_FDSTART = F_SDSTART + SDBYTES
      F_DAY42 = DDAYNUMBER & 255
!
      IF  NAME = "#ARCH" START 
         F_MAXFILE = SIZE << 9
      FINISH  ELSE  START 
         F_SIZE = SIZE
         FDS == ARRAY(FINDAD + F_FDSTART, FDSF)
         CYCLE  J = 1, 1, 7
            FDS(J)_NAME = ".NULL"
         REPEAT 
      FINISH 
OUT:
      result  = FLAG
end ; ! OF SET FILE INDEX
!
!-----------------------------------------------------------------------
!
integerfn  SET USER RECORD(string (6)USER, integer  FSYS,
      SIZE, NPD, NFD, INDAD, INDNO)
!
! SIZE in half K's
!
integer  FLAG, N, J
LONGINTEGER  L
string (4)U4
record (FF)name  F
record (HF)name  H
!
!
!
constinteger  TOPU = 8
conststring (4)array  U(0:TOPU) = c 
      "DUMM", "MANA", "VOLU", "JOBR", "ENGI", "SPOO", "JOUR", "MAIL", "FTRA"
constintegerarray  MAXFILE(0:TOPU) = c 
      0,      50000(8)
constbyteintegerarray  ACR(0:TOPU) = c 
      0,      4,      9,      9,      2,      9,      9,      9,    9
constbyteintegerarray  STKKB(0:TOPU) = c 
      0,      0,      252,    0,      0,      0,      0,      0,    0
constintegerarray  TRYING(0:TOPU) = c 
      0,      X'F7F7F7F7'(6),                            X'7141710', X'F7F7F7F7'
!
!
!
      U4 <- USER; ! look for special users
      cycle  J = TOPU, -1, 0
         N = J
         exit  if  U(N) = U4
      repeat 
!
      H == RECORD(INDAD)
      FILL(512, INDAD, 0); ! clear first 512, file part cleared separately
      H_OWNER = USER
      H_MARK = 1
      H_MSGSEMA = -1
      H_ACR = ACR(N)
      H_DIRVSN = 255
      H_IMAX = 255
      H_BMAX = 255
      H_TMAX = 255
      H_STKKB = STKKB(N)
      H_FSYS = FSYS
      H_TOP = SIZE << 9
      L = 0
      J = M'....' !! (-1)
      *LSS_J
      *ST_L+4
      H_DWSP = L
      H_BWSP = L
!
      J = DSETPASSWORD(USER, FSYS, 1, "....", "....")
      J = DSETPASSWORD(USER, FSYS, 0, "....", "....")
!
      IF  SITE = ERCC AND  N = 0 { unprivileged } START 
         H_TRYING = 1 << 16 { allow use of real money devices } C 
                            UNLESS  CHARNO(USER, 4) = 'U'
      FINISH  ELSE  H_TRYING = TRYING(N)
!
      H_LAST LOG ON = PACKDT
      INDAD = INDAD + 512
      FLAG = SET FILE INDEX(USER, "", FSYS, SIZE-1, NPD, NFD, INDAD, INDNO)
      F == RECORD(INDAD)
      F_MAXFILE = MAXFILE(N)
      result  = FLAG
end ; ! SET USER RECORD
!
!-----------------------------------------------------------------------
!
INTEGERFN  NEW NNT ENTRY(STRING (18)INDEX, INTEGER  FSYS, INTEGERNAME  NNAD)
INTEGER  J, STOP, N, FINDAD, K
STRING (18)UNA, INA
RECORD (NNF)ARRAYFORMAT  NNTF(0 : 16384)
RECORD (NNF)ARRAYNAME  NNT
RECORD (NNF)NAME  NN
RECORD (FF)NAME  F
RECORD (DISCDATAF)DATA
      RESULT  = 23 IF  AV(FSYS, 0) = 0; ! FSYS N/A
      J = FBASE2(FSYS, ADDR(DATA))
      RESULT  = J UNLESS  J = 0
!
      NNT == ARRAY(SYSAD(NNTKEY, FSYS), NNTF)
      UNA = INDEX AND  INA = "" UNLESS  INDEX -> UNA . (ISEP) . INA
      STOP = HASH(UNA, DATA_NNTHASH)
      K = -1; ! to remember first empty entry
      N = STOP
!
      UNTIL  N = STOP CYCLE ; ! cycle through, starting at optimum place
                              ! checking if already in and getting a free entry
         NN == NNT(N)
         IF  NN_NAME = UNA START 
            IF  INA = "" START ; ! main index entry reqd
               RESULT  = 14 IF  NN_TAG = 0; ! already has a main index
            FINISH  ELSE  START 
               IF  NN_TAG > 0 START ; ! this entry is for a file index
                  J = NINDA(FSYS, NN_INDNO, FINDAD)
                  RESULT  = J UNLESS  J = 0; ! should not occur
                  F == RECORD(FINDAD)
                  RESULT  = 87 UNLESS  F_OWNER = UNA
                  RESULT  = 14 IF  F_NAME = INA
               FINISH 
            FINISH 
         FINISH  ELSE  START 
            K = N IF  K < 0 AND  LENGTH(NN_NAME) < 6; ! first free
         FINISH 
         IF  N = DATA_NNTTOP THEN  N = 0 ELSE  N = N + 1
      REPEAT 
!
      RESULT  = 13 IF  K < 0; ! no free entries found
      NNAD = ADDR(NNT(K))
      RESULT  = 0
END ; ! NEW NNT ENTRY
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  DINDNO(STRING (18)NAME, INTEGER  FSYS,
      INTEGERNAME  INDNO)
INTEGER  J, NNAD
STRING (31)UNA, INA, INDEX
RECORD (NNF)NAME  NN
      J = IN2(33)
      -> OUT UNLESS  J = 0
!
      J = 8
      -> OUT IF  FSYS < 0
!
      J = UIO(NAME, UNA, INA, INDEX)
      -> OUT UNLESS  J = 0
!
      J = FIND NNT ENTRY(INDEX, FSYS, NNAD, 0)
      -> OUT UNLESS  J = 0
!
      NN == RECORD(NNAD)
      INDNO = NN_INDNO
OUT:
      RESULT  = OUT(J, "SI")
END 
!
!-----------------------------------------------------------------------
!
INCLUDE  "PD22S_B08DPRG"
externalintegerfn  STRING TO FILE(integer  TXTL,TXTA,FA)
integer  I0, MAX, FREE
record (FHDRF)name  H
      H == RECORD(FA)
      CYCINIT(FA, 4096) if  H_MAXBYTES = 0
      I0 = H_NEXTCYCLIC
      MAX = H_MAXBYTES
      FREE = MAX - I0; ! >= 1
!
      if  TXTL <= FREE start ; ! no wrapround
         MOVE(TXTL, TXTA, FA + I0)
         if  H_NEXT FREE BYTE < MAX c 
         then  H_NEXT FREE BYTE = I0 + TXTL
!
         if  I0 + TXTL < MAX start 
            H_NEXT CYCLIC = I0 + TXTL
            if  I0 < H_READ TO <= I0 + TXTL start 
               if  I0 + TXTL = MAX + 1 c 
               then  H_READ TO = H_TXT REL ST c 
               else  H_READ TO = I0 + TXTL + 1
            finish 
         finish  else  start 
            H_NEXT CYCLIC = H_TXT REL ST
            if  I0 < H_READ TO c 
            then  H_READ TO = H_TXT REL ST + 1
         finish 
      finish  else  start 
         MOVE(FREE, TXTA, FA + I0); ! it does wrap round, move two bits
         MOVE(TXTL - FREE, TXTA + FREE, FA + H_TXT REL ST)
         H_NEXT FREE BYTE = MAX
         H_NEXT CYCLIC = H_TXT REL ST + TXTL - FREE
         if  H_READ TO > I0 or  H_READ TO <= H_NEXT CYCLIC c 
         then  H_READ TO = H_NEXT CYCLIC + 1
      finish 
      result =(I0<<16) ! H_NEXT CYCLIC
end ; ! STRING TO FILE
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  COPY TO FILE(INTEGER  FA1, FA2)
INTEGER  J, RT, NC, TRS
RECORD (FHDRF)NAME  H
      H == RECORD(FA1)
      RT = H_READTO
      NC = H_NEXTCYCLIC
      TRS= H_TXT REL ST
      RETURN  IF  RT = NC; ! nothing to copy
!
      IF  RT < NC START ; ! simple chunk to dispose of
         J = STRING TO FILE(NC-RT, FA1+RT, FA2)
      FINISH  ELSE  START ; ! two chunks, although second may be null !!
         J = STRING TO FILE(H_MAXBYTES-RT, FA1+RT, FA2)
         IF  NC > TRS START ; ! there is another bit
            J = STRING TO FILE(NC-TRS, FA1+TRS, FA2)
         FINISH 
      FINISH 
      H_READTO = NC
END ; ! COPY TO FILE
!
!-----------------------------------------------------------------------
!
INTEGERFN  SPOOL(STRING (31)FULL, DEST, DELIV,
      INTEGER  FSYS, START, END, COPIES, PROC1 DACT)
INTEGER  LEN
STRING (255)S
STRING (31)USER, FILE
RECORD (PARMF)P
      FULL -> USER . (".") . FILE
      S = "DOCUMENT DEST=" . DEST . C 
          ",SRCE=" . FILE . C 
          ",LENGTH=" . ITOS(END - START) . C 
          ",PRTY=VHIGH" . C 
          ",COPIES=" . ITOS(COPIES) . C 
          ",USER=" . USER . C 
          ",START=" . ITOS(START) . C 
          ",FSYS=" . ITOS(FSYS)
      S = S . ",DELIV=" . DELIV UNLESS  DELIV = ""
      ATTU(S)
      LEN = LENGTH(S)
      P_DEST = X'FFFF0016'
      RESULT  = TXTMESS("SPOOLR", P, 1, 0, LEN, ADDR(S)+1, -1, PROC1 DACT)
END ; ! SPOOL
!
!-----------------------------------------------------------------------
!
!
!
!              Process1  DACTs for replies:
!                 38 reply from SPOOLR for JOURNAL file
!                 39                        LP
!
!
!---------------------------------------------------------------------------------
STRINGFN  NEWNAME
OWNINTEGER  INC=0
STRING (10) S,HH,MM,SS
      S=TIME
      S->HH.(".").MM.(".").SS
      INC=INC+1
      INC=0 IF  INC>9999; ! restricting final name to 10 characters
      RESULT ="M".ITOS(INC)."#".HH.MM
END ; ! NEWNAME
!
!-----------------------------------------------------------------------
!
INTEGERFN  MAKE LOGFILE(RECORD (LF)NAME  LOGRECORD)
! This routine creates a uniquely-named file of LOGKB kbytes(currently 16 epages)
! in VOLUMS index on the SLOAD file system, fills it with EM characters
! and puts an Edinburgh Subsystem standard header on it (character file)
! In addition the file is permitted to the DIRECT process, is made 
! temporary and is connected into the caller's VM (to prevent it's being
! accidentally destroyed (e.g. by erroneous software!).
INTEGER  J, GAP, FAD, BYTES, SEG, FN, DA
STRING (11)VOLSFILE
STRING (31)FULL
RECORD (FHDRF)NAME  FH
CONSTSTRING (12)PROC = "MAKE LOGFILE"
      VOLSFILE = LOGRECORD_NAME
      FULL = "VOLUMS." . VOLSFILE
      LOGRECORD_FSYS = COM_SUPLVN
!
      FN = 1; ! CREATE
      J = DCREATEF(FULL, -1, LOGKB, 1, LEAVE, DA); ! ALLOCATE
      -> ERR UNLESS  J = 0
!
      GAP = 0
      SEG = 0
       FN = 2; ! CONNECT
      J = DCONNECTI(FULL, -1, 2, 0, SEG, GAP)
      -> ERR UNLESS  J = 0
!
      FAD = SEG << 18
      FH == RECORD(FAD)
      BYTES = 64 << 10
      FILL(BYTES, FAD, X'19')
      FH_NEXTFREEBYTE = 32
      FH_TXTRELST = 32
      FH_MAXBYTES = BYTES
      FH_THREE = 3; ! CHARACTER FILE
    ! We now want to remove the PRIVacy VIOLated state. We used to do this
    ! by creating the file with the "zero" option, but this was too slow
    ! (psychologically, at IPL). So we'll just disconnect and the transfers
    ! occur "on the fly", i.e. we don't go to sleep awaiting
    ! completion of the transfers.
      J = DDISCONNECTI(FULL, -1, 0)
!
      GAP = 0
      SEG = 0
      J = DCONNECTI(FULL, -1, 11, 0, SEG, GAP)
      DOPERR(PROC, 2, J) UNLESS  J = 0
    ! Now make it a temporary file, so that unused files are destroyed at
    ! consistency check (temporary attribute disappears on RENAME, 
    ! TRANSFER or PERMIT, and that's why we can't create it "temporary").
      J = DFSTATUS("VOLUMS", VOLSFILE, -1, 5, 0)
      DOPERR(PROC, 8, J) UNLESS  J = 0
!
      LOGRECORD_DISC ADDR = DA
      LOGRECORD_STATE = 1; ! READY
      READY FILES = READY FILES + 1
      RESULT  = 0
ERR:
      DOPERR(PROC, FN, J)
      RESULT  = J
END ; ! MAKE LOGFILE
!
!-----------------------------------------------------------------------
!
ROUTINE  LOG DISCONNECT(RECORD (LF)NAME  LOG)
!
! The point of this routine is to get the LOG file "disconnected" even if it
! isn't connected in this VM (becase a previous invocation of DIRECT died in
! an unfortunate manner. This will be done as follows. If the DDISCONNECT
! fails 39, we get a new (ready) file to copy the contents into, and replace
! the data in the LOG parameter record accordingly.
!
INTEGER  J, INSEG, OUTSEG, N, GAP, ONCE, INAD, OUTAD
RECORD (FHDRF)NAME  H
CONSTSTRING (15)PROC = "LOG DSCN"
      ONCE = 0
REDISC:
      J = DDISCONNECTI("VOLUMS." . LOG_NAME, LOG_FSYS, 0)
      RETURN  IF  J = 0 OR  ONCE > 0
!
      ONCE = 1
      IF  J = 39 START 
         ! not connected, we presume because previous invocation of DIRECT had
         ! it connected.
         INSEG = 0
         GAP = 0
         J = DCONNECTI("VOLUMS." . LOG_NAME, LOG_FSYS, 11, 0, INSEG, GAP)
         DOPERR(PROC." ".LOG_NAME, 2, J) UNLESS  J = 0
         INAD = INSEG << 18
         !
         ! Find a ready file to copy the data into
         CYCLE  N = 0, 1, TOP LOG
            IF  LOGS(N)_STATE = 1 START ; ! ready
               OUTSEG = 0
               GAP = 0
               ! (we actually expect it to be connected, but this way we get the
               !  connect address)
               J = DCONNECTI("VOLUMS." . LOGS(N)_NAME, LOGS(N)_FSYS,
                  11, 0, OUTSEG, GAP)
               DOPERR(PROC." ".LOGS(N)_NAME,2,J) AND  RETURN  IF  0 # J # 34
               OUTAD = OUTSEG << 18
               !
               ! Move the necessary amout of data
               H == RECORD(INAD)
               J = H_TXTRELST
               J = J + 1 WHILE  J < H_MAXBYTES AND  BYTEINTEGER(INAD+J) # X'19'
               H_NEXTFREEBYTE = J
               MOVE(J, INAD, OUTAD)
               !
               ! The OUTSEG file now replaces the original
               LOG = LOGS(N)
               LOGS(N)_STATE = 0; ! just forget about the original
               -> REDISC
            FINISH 
         REPEAT 
         DOPER2(PROC." no free files")
      FINISH 
      DOPERR(PROC,5,J)
END ; ! LOG DISCONNECT
!
!-------------------------------------------------------------------------------
!
ROUTINE  SPOOL LOGFILE(RECORD (LF)NAME  LOG TO BE SPOOLED,
      INTEGER  BYTES, CHAR1)
    ! This routine scans the file to be spooled backwards to get the last
    ! non-EM character. But if BYTES is >0 then this is the
    ! required data length.
    ! If PRINT ON = 0, then to JOURNAL queue
    !               >0, then to LP queue in addition.
RECORD (FHDRF)NAME  H
STRING (31)COPFILE, UP TO TIME FILE, FULL
INTEGER  J, A, SEG, GAP, LOGFILE SEG, CURF, DA, FN, PROC1 DACT
INTEGER  START, END
STRING (11)CURLOG
CONSTSTRING (13)PROC = "SPLOGFILE"
      PROC1 DACT = 39
      LOG DISCONNECT(LOG TO BE SPOOLED)
      CURLOG = LOG TO BE SPOOLED_NAME
      CURF = LOG TO BE SPOOLED_FSYS
      DA = LOG TO BE SPOOLED_DISC ADDR
! Rename the file to make it's identifier reflect the current time-
! of-day.
      UP TO TIME FILE = NEWNAME
      CHARNO(UP TO TIME FILE, 1) = CHAR1; ! make first char M for mainlog, D for Director log
      J = DRENAME("VOLUMS", CURLOG, UP TO TIME FILE, CURF)
!
      IF  J = 0 C 
      THEN  CURLOG = UP TO TIME FILE C 
      ELSE  DOPERR(PROC." ".CURLOG, 9, J)
!
      LOG TO BE SPOOLED = 0
      RETURN  IF  CURLOG = ""
!
      FULL = "VOLUMS." . CURLOG
      LOGFILE SEG = 0
      GAP = 0
      FN = 2
      J = DCONNECTI(FULL, CURF, 11, 0, LOGFILE SEG, GAP)
      -> ERR UNLESS  J = 0
!
      A = LOGFILE SEG <<18
          H==RECORD(A)
          UNLESS  0<H_TXTRELST<=64 AND  0<H_MAXBYTES<=X'10000' START 
             DOPER2("HDR ? .".CURLOG." ".HTOS(DA,4))
             H_NEXTFREEBYTE=X'10000'
             H_TXTRELST=32
             H_MAXBYTES=X'10000'
             H_THREE=3
             FINISH 
          IF  H_TXTRELST<BYTES<=H_MAXBYTES THEN  H_NEXTFREEBYTE=BYTES  C 
          ELSE  START 
             J=A + H_TXTRELST
             J=J+1 WHILE  J<A + H_MAXBYTES AND  BYTEINTEGER(J)#X'19'
             H_NEXTFREEBYTE=J - A
          FINISH 
!
      START = H_TXTRELST
      END = H_NEXTFREEBYTE
!
          IF  PRINT ON>0 START 
             PRINT ON=PRINT ON - 1
             ! make a copy of the file in VOLUMS to send to LP.   
             COPFILE=FULL
             BYTEINTEGER(ADDR(COPFILE)+8)='L'
             FN = 1
             J=DCREATEF(COPFILE,CURF,  C 
                (END+X'3FF')>>10, 16 + 1,LEAVE,DA)
            IF  J = 0 START 
               SEG = 0
               GAP = 0
               FN = 2
               J=DCONNECTI(COPFILE,CURF,3,0,SEG,GAP)
               IF  J=0 START 
                  MOVE(END, A, SEG << 18)
                  J=DDISCONNECTI(COPFILE,CURF,0)
                  FN = 10
                  J=SPOOL(COPFILE, "LP", DELIV,CURF,START, END,1,PROC1 DACT)
                  FINISH 
             FINISH 
               DOPERR(PROC."/C", FN, J) UNLESS  J = 0
             FINISH ; ! PRINT ON > 0
             FN = 5
          J=DDISCONNECTI(FULL,CURF,1)
            DOPERR(PROC, FN, J) UNLESS  J = 0
          FN = 10
          IF  CHAR1='M' THEN  PROC1 DACT=38
          J=SPOOL(FULL, "JOURNAL", "",CURF,START, END,1,PROC1 DACT)
          !
      RETURN  IF  J = 0
ERR:
      DOPERR(PROC, FN, J)
END ; ! SPOOL LOGFILE
!
!-----------------------------------------------------------------------
!
ROUTINE  PRINT LOGSPACE
INTEGER  J
      CYCLE  J=0,1,TOPLOG
         DOPER2(LOGS(J)_NAME." ".HTOS(LOGS(J)_DISC ADDR,8)." ".LM(LOGS(J)_STATE))
      REPEAT 
END ; ! PRINT LOGSPACE
!
!-----------------------------------------------------------------------
!
ROUTINE  FILL LOGSPACE(INTEGER  RESTART)
! If RESTART is non-zero (restart of DIRECT) then the logfiles are possibly
! marked connected (in the previous DIRECT's VM). This is the case only if
! DIRECT has failed on the signal stack.
! Anyway, what we are going to do on restart is to forget the "ready" files,
! keep the files which PRINTER has got and re-fill the logspace with new files.
! Then when PRINTER gives up its existing files they will get copied into fresh
! files in rt SPOOL LOGFILE.
!
INTEGER  J,K
      K=0
      CYCLE  J=0,1,TOPLOG
         ! (If restart and state=ready then state=free)
         IF  RESTART#0 AND  LOGS(J)_STATE=1 THEN  LOGS(J)_STATE=0
         IF  LOGS(J)_STATE=0 START ; ! FREE
            LOGS(J)_NAME=NEWNAME
            K=K!MAKE LOGFILE(LOGS(J))
         FINISH 
      REPEAT 
!
      IF  K#0 START 
         DOPERR("FILL LOGSPACE", 0, K)
         PRINT LOGSPACE
      FINISH 
END ; ! FILL LOGSPACE
!
!-----------------------------------------------------------------------
!
ROUTINE  GIVE NEW SECTION(INTEGER  DEST)
! This routine serves the following two functions:
!     1.    selects a "ready" file and gives its disc address to the
!           resident PRINTER routine (DEST#-1), and
!     2.    selects a "ready" file to copy the Director logfile into,
!           and spools it (DEST=-1)
INTEGER  AD,J,LX,FSYS,SEG,GAP
INTEGER  SEMADR, SEMANO
STRING (11) NAME
RECORD  (PARMF)P
RECORD (FHDRF)NAME  H, HH
RECORD (DIRCOMF)NAME  DIRCOM
CONSTSTRING (13)PROC = "GIVE NEW SCTN"
      RETURN  IF  DEST=0
      CYCLE  LX=0,1,TOPLOG
         IF  LOGS(LX)_STATE=1 START ; ! READY
            UNLESS  DEST = -1 START 
               ! SEND IT OFF TO PRINTER
               P=0
               P_DEST=DEST
               P_P1=16; ! EPGS
               P_P2=LOGS(LX)_DISC ADDR
               IF  GMON#0 THEN  DOPER2("NEWSECT ".HTOS(P_P2,8))
               DPONI(P)
            FINISH 
            NAME=LOGS(LX)_NAME
            FSYS=LOGS(LX)_FSYS
! Also RENAME it, to add a "#" to the RH end of the name (which is max 10
! characters at this point) to indicate that this is one of the files
! This also removes TEMPORARY status, so that this log will survive IPL.
            J=DDISCONNECTI("VOLUMS." . NAME,FSYS,0)
            DOPERR(PROC, 5, J) UNLESS  J = 0
            J=DRENAME("VOLUMS",NAME,NAME."#",FSYS)
            DOPERR(PROC, 9, J) UNLESS  J = 0
            NAME=NAME."#"
            LOGS(LX)_NAME=NAME
               SEG=0; GAP=0
               J=DCONNECTI("VOLUMS." . NAME,FSYS,11,0,SEG,GAP)
               DOPERR(PROC, 2, J) UNLESS  J = 0
            IF  DEST=-1 START 
               IF  J=0 START 
                  ! Copy and spool the Director logfile
                  AD=SEG<<18
                  H==RECORD(AD)
                  ! Set up the header first for the copy of the circular file
                  H_NEXTFREEBYTE=32
                  H_TXTRELST=32
                  H_MAXBYTES=LOGKB<<10
                  H_NEXTCYCLIC=32
                  DIRCOM == RECORD(SYSAD(DIRCOM KEY, -1))
                  SEMADR = ADDR(DIRCOM_DIRLOGSEMA)
                  SEMANO = (1<<31)!1
                  J = PP(SEMADR, SEMANO,PROC); ! DIRLOG sema
                  IF  J = 0 START 
                      COPY TO FILE(DIRLOGAD,AD)
                      ! Clear word4 "SPARE" in #DIRLOG header to indicate to
                      ! other processes that data has been spooled.
                      HH==RECORD(DIRLOGAD)
                      HH_SEMA=0
                      VV(SEMADR, SEMANO)
                  FINISH 
                  ! Now fix the header into the format required by JOURNAL
                  ! (Word numbers counting from zero)
                  H_THREE=3; ! Word 3
                  H_DATE=PACKDT; ! Word 5
                  H_NEXTCYCLIC=X'FFFFFF04';    ! Word 6, DIRLOG code
                  ! Spool if not empty else destroy
                  IF  H_NEXTFREEBYTE>H_TXTRELST THEN   C 
                     SPOOL LOGFILE(LOGS(LX),H_NEXTFREEBYTE,'D')  C 
                     ELSE  J=DDISCONNECTI("VOLUMS." . NAME,FSYS,2); ! and destroy
                  LOGS(LX)_STATE=0; ! FREE
               FINISH 
            FINISH  ELSE  START 
               ! Giving (having given) a file section to PRINTER
               LOGS(LX)_STATE=2; ! MAIN
               READY FILES=READY FILES - 1
            FINISH 
            !
            RETURN 
         FINISH 
      REPEAT 
      PRINT LOGSPACE
END ; ! GIVE NEW SECTION
!
!-----------------------------------------------------------------------
!
ROUTINE  LSPOOL(INTEGER  DA,BYTES)
INTEGER  J
! DISC ADDRESS IS ALLOWED TO REPRESENT ANY PAGE IN THE 16 EPAGE
! SECTION, AND SECTIONS ARE 16 EPAGE-ALIGNED. SO DROP THE BOOTTOM
! 4 BITS
      DA=DA&(¬15)
      CYCLE  J=0,1,TOPLOG
         IF  LOGS(J)_DISC ADDR=DA AND  LOGS(J)_STATE=2 START ; ! MAIN
            UNLESS  32<=BYTES<=X'10000' START 
               DOPER2("DA       BYTES ")
               DOPER2(HTOS(DA,8)." ".HTOS(BYTES,8))
            FINISH 
            ! OK TO SPOOL
            IF  GMON#0 START 
               DOPER2("LSPOOL ".LOGS(J)_NAME)
               DOPER2(HTOS(DA,8)." ".HTOS(BYTES,8))
            FINISH 
            SPOOL LOGFILE(LOGS(J),BYTES,'M')
            LOGS(J)_STATE=0; ! FREE
            !
            RETURN 
         FINISH 
      REPEAT 
      DOPERR("LSPOOL", 0, DA)
      PRINT LOGSPACE
END ; ! LSPOOL
!
!-----------------------------------------------------------------------
!
ROUTINE  PROCEED TO NEW FILE
RECORD  (PARMF)P
      P=0
      P_DEST=X'00360007'
      P_SRCE=21; ! TO BE USED FOR FUTURE NEW FILE REQUESTS FROM MAIN
      DPONI(P)
      MAIN LP=0
END ; ! PROCEED TO NEW FILE
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  LOGLINK(RECORD (PARMF)NAME  P, INTEGER  ACT)
OWNINTEGER  DPRINT NEWSECT DEST=0
OWNINTEGER  LINIT=0
OWNINTEGER  MAPAD
INTEGER  N1,SEG,SEGAD,GAP,FSYS,I,J,DISCAD,SIZE, RES, PROTOCOL
INTEGER  NKB, ALLOC, MODE, APF, RESTART, INOFF, OUTOFF, DA
RECORD (FHDRF)NAME  HH
RECORD (LOGF HDF)NAME  H
RECORD (FEPF)ARRAYNAME  FEPS
RECORD (FEP DETAILF)NAME  FEP
RECORD (DIRCOMF)NAME  DIRCOM
CONSTINTEGER  TOPLA=12
SWITCH  NEWLA(0:TOPLA)
CONSTSTRING (7)PROC = "LOGLINK"
STRINGNAME  SITEF
      RES = 8
      IF  LINIT = 0 START ; ! need to initialise
         FSYS = -1
         ! NKB = (ADDR(TESTH_LEND) - ADDR(TESTH) + X'FFF')>>10
         NKB = 128; ! one block. The FEP buffers go in the last 16 pages.
                    ! The amount required for the LOGF HDR record is only
                    ! four pages.
         ALLOC = X'03000055'; ! EEP=3, ZERO, TEMPFI
         MODE = WRSH
         APF = 0
         SEG = 0
         GAP = 0
         J = DCREATEF("VOLUMS.#LOGMAP", FSYS, NKB, ALLOC, LEAVE, DA)
         DOPERR(PROC, 1, J) UNLESS  J=0 OR  J=23 OR  J=37 OR  J=16
         -> OUT UNLESS  J = 0 OR  J = 16
!
         J = DCONNECTI("VOLUMS.#LOGMAP", FSYS, MODE, APF, SEG, GAP)
         DOPERR(PROC, 2, J) AND  -> OUT UNLESS  J = 0 OR  J = 34
!
         SEGAD = SEG <<18
         MAPAD = SEGAD + X'10000'
         H == RECORD(MAPAD)
!
         IF  H_LOGMAPST = 0 START ; ! just done an IPL
            DIRCOM == RECORD(SYSAD(DIRCOM KEY, -1)); ! preserve DEFAULT SUBSYS and STUDENT
            DIRCOM_DIRLOGSEMA = -1
            DIRCOM_FEPSEMA = -1
            DAP INTERFACE(1); ! Set DAP fields
            DIRCOM_SUBSYS SITE COUNT = 0
            DIRCOM_STUDENT SITE COUNT = 0
!
            CYCLE  J = 0, 1, 253
               H_PROCLIST(J) = 0;
               H_PROCLIST(J)_LINK = J + 1
            REPEAT 
            H_PROCLIST(254)_LINK = ENDLIST
            H_LOGMAPST = ADDR(H_PROCLIST(0)) - MAPAD
            H_FREEHD = 0
            H_LIVEHD = ENDLIST
            H_BACKHD = ENDLIST
!
            FEPS == H_FEPS
            INOFF = - 2*FEP IO BUFF SIZE
            CYCLE  I = 0, 1, TOP FE NO
               FEPS(I)_AVAILABLE = NO
               CYCLE  PROTOCOL = ITP, 1, X29
                  FEP == FEPS(I)_FEP DETAILS(PROTOCOL)
                  FEP_INPUT STREAM = 0;!STREAM TYPE
                  FEP_OUTPUT STREAM = 1;    !DITTO
                  FEP_IN BUFF DISC ADDR = DA
                  FEP_OUT BUFF DISC ADDR = DA
                  FEP_IN BUFF DISC BLK LIM = 31
                  FEP_OUT BUFF DISC BLK LIM = 31
                  INOFF = INOFF + 2*FEP IO BUFF SIZE
                  OUTOFF = INOFF + FEP IO BUFF SIZE
                  FEP_IN BUFF CON ADDR = SEGAD + INOFF
                  FEP_OUT BUFF CON ADDR = SEGAD + OUTOFF
                  FEP_IN BUFF OFFSET = INOFF
                  FEP_OUT BUFF OFFSET = OUTOFF
                  FEP_IN BUFF LENGTH = FEP IO BUFF SIZE
                  FEP_OUT BUFF LENGTH = FEP IO BUFF SIZE
               REPEAT 
            REPEAT 
            RESTART = 0
         FINISH  ELSE  RESTART = 1
!
         LOGS == H_LOGS
         FILL LOGSPACE(RESTART)
!
!
      ALLOC = X'03000051'; ! Create a 'monitor' file, zeroed, EEP = 3
      MODE = WRSH
      APF = 0
      SEG = 15
      GAP = 0
      J = CREATE AND CONNECT("VOLUMS.#MONITOR", FSYS, 8 {kb},
         ALLOC, MODE, APF, SEG, GAP)
      IF  J = 0 C 
      THEN  MONITORAD = SEG << 18 C 
      ELSE  DOPERR(PROC, 1, J)
!
         ! Also create and connect a 1/2-segment file for a Director log
         ALLOC = X'03000051'; ! EEP=3, ZERO
         MODE = WRSH
         APF = 0
         SEG = 14
         GAP = 0
         J = CREATE AND CONNECT("VOLUMS.#DIRLOG", FSYS, C 
               DIRLOG KB, ALLOC, MODE, APF, SEG, GAP)
         IF  J = 0 START 
            DIRLOGAD = SEG << 18
            HH == RECORD(DIRLOGAD)
            HH_SEMA = 0; ! clear it at IPL (yes 0=clear!) in case left held
                         ! at end of last session
            CYCINIT(DIRLOGAD, DIRLOG KB<<10) IF  HH_MAXBYTES = 0
            LOG ACTION = DT ! DLOG
         FINISH  ELSE  DOPERR(PROC, 1, J)
!
         SITEF == DIRCOM_DEFAULT SUBSYS
         IF  DIRCOM_SUBSYS SITE COUNT = 0 AND  SITEF # "" START 
            DOPER2("IPLPRG " . SITEF)
            J = DPRG("", SITEF, -1, "DEV".ITOS(100+COM_SUPLVN), X'380')
            DOPER2("Flag=" . ITOS(J))
            SITEF = "" IF  J = 0
         FINISH 
!
         SITEF == DIRCOM_DEFAULT STUDENT
         IF  DIRCOM_STUDENT SITE COUNT = 0 AND  SITEF # "" START 
            DOPER2("IPLPRG " . SITEF)
            J = DPRG("", SITEF, -1, "DEV".ITOS(100+COM_SUPLVN), X'400')
            DOPER2("Flag=" . ITOS(J))
            SITEF = "" IF  J = 0
         FINISH 
!
         LINIT=1; ! LOGLINK INITIALISED
      FINISH 
      UNLESS  0<ACT<=TOPLA THEN  ACT=0
      -> NEWLA(ACT)
    !---------------------------- NEW     ------------------------------------
    NEWLA(0):    ! INVALID ACT
          -> OUT
    NEWLA(1):     ! FROM PR(21) IN RT PROCESS1
          ! P_P1 = DISC ADDRESS WITHIN FILE TO BE SPOOLED
          ! P_P2 = FINAL SIZE IN BYTES
          DPRINT NEWSECT DEST=P_SRCE
          DISCAD=P_P1
          SIZE=P_P2
          GIVE NEW SECTION(DPRINT NEWSECT DEST)
          IF  DISCAD#0#SIZE THEN  LSPOOL(DISCAD,SIZE)
          FILL LOGSPACE(0)
          -> OUT0
    !
    NEWLA(2):     ! AFTER IPL, BEFORE STARTING VOLUMS & SPOOLR
          PROCEED TO NEW FILE
    NEWLA(8):      ! give address in LOGMAP file for DIRECT's process list
          P_DEST=MAPAD
          -> OUT0
    !
    NEWLA(3):     ! D/PRINT(25)
          ! Param specifies number of files to be printed, where
          !     D/PRINT  (no param) means print current file
          !     D/PRINT 0         means go to new file without printing current file
          !     and otherwise 
          !     D/PRINT n  means print current and n-1 subsequent
          !     files.
          ! Note that the number PRINT ON is also set by  D/DIRPRINT
          N1=P_DEST
          IF  N1=1 THEN  GMON=1
          IF  N1=2 THEN  GMON=0
          IF  N1=-1 THEN  N1=1; ! default value
          PRINT ON=N1
          IF  READY FILES=0 START 
             FILL LOGSPACE(0)
             GIVE NEW SECTION(DPRINT NEWSECT DEST)
             FINISH 
          PROCEED TO NEW FILE
          -> OUT0
    !
    NEWLA(4):      ! from JOURNL, requesting current logfile be spooled
    ! This entry is reached by sending a DACT 27 message to DIRECT. A reply is
    ! (eventually) given to SRCE. P_P1 is a FLAG, 0 if operation successful, 
    ! otherwise -1,-2 or -3, or SPOOLR's flag as described below.
    ! Effects are as follows:
    !     1.    If MAIN has printer then FLAG -3, else
    !           PRINTER is poked to close current file.                             
    !     2.    SRCE of request is remembered, FLAG -2 given immediately if one
    !           already remembered.
    !     3.    Each time a file is spooled to JOURNAL, if a SRCE is remembered, a reply
    !           is given to the SRCE (and SRCE forgotten). If the spool request
    !           failed, FLAG is -1 if Director flag non-zero, else SPOOLR's flag.
    !           P_P2 of the reply to SRCE is SPOOLR's Id for the file spooled if spool successful, else -1.
          IF  JOURNL DEST#0 OR  MAIN LP#0 START 
             P_DEST=P_SRCE
             P_P1=-2
             IF  MAIN LP#0 THEN  P_P1=-3
             DPONI(P)
          FINISH  ELSE  JOURNL DEST=P_SRCE
          PROCEED TO NEW FILE
          -> NEWLA(11); ! GIVE DIRLOG TO JOURNL AS WELL
    !
    NEWLA(5):     ! D/MAINLP(32)
!
      RES = 81
      -> OUT UNLESS  MAINLP = 0
      P = 0
      P_DEST = X'00360008'
      DOUTI(P)
      RES = P_P1
      MAINLP = 1 IF  RES = 0
      -> OUT
    NEWLA(6):
          PRINT LOGSPACE
          -> OUT0
    NEWLA(7):      ! D/DELIVER
          DELIV<-STRING(ADDR(P))
          IF  DELIV="" THEN  DELIV="Machine Room"
          -> OUT0
    !
    NEWLA(9):      ! Dact 38 in process1. JOURNAL reply fROM SPOOLR
                   IF  JOURNL DEST#0 START 
                      ! only if spooled to JOURNAL queue
                      P_DEST=JOURNL DEST
                      DPONI(P)
                      JOURNL DEST=0
                      FINISH 
    !
    NEWLA(10):     ! Dact 39 in process1. LP reply from SPOOLR
          -> OUT0
    NEWLA(12):     ! D/DIRPRINT
          ! Param specifies number of files to be printed, where
          !     D/PRINT  (no param) mans print current file
          !     D/PRINT 0         means go to new file without printing current file
          !     and otherwise D/PRINT n  means print current and n-1 subsequent
          !     files.
          N1=P_DEST
          IF  N1=-1 THEN  N1=1; ! default value
          PRINT ON=N1
    NEWLA(11):     ! Dact 40 in process1. Trigger DIRPRINT from user process
          IF  DIRLOGAD=0 THEN  RES=90 AND  -> OUT; ! no #DIRLOG file, or not connected
          GIVE NEW SECTION(-1)
          FILL LOGSPACE(0)
          -> OUT0
OUT0:
          RES=0
!
OUT:
      RESULT  = RES
END ; ! LOGLINK
!
!-----------------------------------------------------------------------
!
ROUTINE  RECORD LOST FILE(STRING (6) USER,STRING (11) FILE,  C 
   BYTEINTEGER  FSYS,CODES2,CODES)
OWNINTEGER  N=0
INTEGER  J,SEG,GAP,FAD,KB
INTEGER  ALLOC, MODE, APF
RECORD (FHDRF)NAME  FILEH
RECORD (LOSTF)NAME  LARDEST
RECORD (LOSTF)ARRAYNAME  LARY
RECORD (LOSTF)ARRAYFORMAT  LARYF(0:TOPLOST)
      LARY==ARRAY(LOSTARAD,LARYF)
      IF  LENGTH(USER)=6 START ; ! call with l(USER)=6 records entry in LARY
         IF  N>=TOPLOST START 
            DOPER2("Too many files lost!")
            RETURN 
         FINISH 
         LARY(N)_DATE=DATE
         LARY(N)_TIME=TIME
         LARY(N)_USER=USER
         LARY(N)_FILE=FILE
         LARY(N)_CODES2=CODES2
         LARY(N)_CODES=CODES
         LARY(N)_CHERISHED=(CODES&CHERSH)>>4
         N=N+1
         RETURN 
      FINISH 
! copy the contents of the array into file VOLUMS.LOSTFILES
      RETURN  IF  N=0; ! No lost files
      DOPER2("FSYS ".ITOS(FSYS).": ".ITOS(N)." files lost")
      KB=4; ! Kbytes
      ALLOC = X'00000011'; ! ZERO
      MODE = WRSH
      APF = 0
      SEG = 0
      GAP = 0
      J = CREATE AND CONNECT("VOLUMS.LOSTFILES", FSYS, KB, C 
            ALLOC, MODE, APF, SEG, GAP)
      DOPERR("CCK LOSTFILE", 1, J) AND  RETURN  UNLESS  J = 0
      FAD=SEG<<18
      FILEH==RECORD(FAD)
      J=FILEH_NEXTFREEBYTE-FILEH_TXTRELST
      UNLESS  FILEH_TXTRELST#0#FILEH_NEXTFREEBYTE AND   C 
         J-(J//LOSTFLEN)*LOSTFLEN=0 START 
            FILEH=0
            FILEH_TXTRELST=32
            FILEH_NEXTFREEBYTE=32
            FILEH_MAXBYTES=KB<<10
      FINISH 
      J=0
      WHILE  J<N CYCLE 
         LARDEST==RECORD(FAD+FILEH_NEXTFREEBYTE)
         LARDEST=LARY(J)
         J=J+1
         FILEH_NEXTFREEBYTE=FILEH_NEXTFREEBYTE+LOSTFLEN
         IF  FILEH_NEXTFREEBYTE>FILEH_MAXBYTES - LOSTFLEN C 
         THEN  FILEH_NEXTFREEBYTE=32
      REPEAT 
      N=0
!
      J = DDISCONNECTI("VOLUMS.LOSTFILES",FSYS,0)
      DOPERR("CCK LOSTFILE", 5, J) UNLESS  J = 0
END ; ! RECORD LOST FILE
!
!-----------------------------------------------------------------------
!
integerfn  DOINDEX2(integer  ITYPE, INDAD, INDNO, FSYS, CROSSEDA,
      COPYA, string (6)USER, integername  CLOSE USERS FLAG)
!
! Parameters
!     ITYPE = 0 process index
!             1 file index
!
!     INDAD address of index
!
!     FSYS
!
!     CROSSEDA address of 'crossed pages' bitlist
!
!     COPYA address of 'bit map copy'
!
!     USER index owner
!
! Result flag
!
!     2**0 TEMP/VTEMP
!        1 PRIV VIOL/UNAVA
!        2 filename corrupt
!        3 PD list corrupt
!        4 SD list corrupt
!        5 PGS not consistent with SDs
!        6 bit map bits set twice
!        7 DA out of range
!        8 index corrupt
!
! Local procedures
!
CONSTSTRING (15)ARRAY  MSG(0:8) = C 
      "temp",
      "priv viol",
      "fname bad",
      "PD list bad",
      "SD list bad",
      "PGS bad",
      "bits twice",
      "DA bad",
      "index bad"
!
ROUTINE  WRITE FLAG(INTEGER  FLAG)
INTEGER  J
      CYCLE  J = 0, 1, 8
         IF  FLAG & 1 > 0 START 
            SPACE
            PRINTSTRING(MSG(J))
         FINISH 
         FLAG = FLAG >> 1
      REPEAT 
      NEWLINE
END 
!
routine  SETB(byteintegername  B, integer  N)
      B <- N unless  B = N
end 
!
!
!
routine  SETI(integername  I, integer  N)
      I = N unless  I = N
end 
!
!
!
!
!
!
integerfn  SETBIT(integer  ADR, N); ! used to set bits in SDBITS and PDBITS
integer  X
      *LDTB_8192
      *LDA_ADR
      *LB_N
      *LSS_(dr +b )
      *ST_X
      *LSS_1
      *ST_(dr +b )
      result  = X
end 
!
!
!
integer  J, B, LOBIT, HIBIT, USEDPGS, CHERPGS, CHERFILES, W, c 
      NPD, NSD, NFD, NF, FI, FLAG, CODES, CODES2, PAGS, TEMP, CHER, c 
      LINK, DA, LEN, IFLAG, MASK, SDX, PDX, L, NSECTS, c 
      SDBITSA, PDBITSA
!
record (FF)name  F
record (HF)name  H
RECORD (PARMF)NAME  P
record (FDF)name  FD
record (PDF)name  PD
integername  SD
record (PDF)arrayname  PDS
record (FDF)arrayname  FDS
integerarrayname  SDS
byteintegerarray  SDBITS(0:1023)
byteintegerarray  PDBITS(0:31)
RECORD (DISCDATAF)DATA
!
string (11)NAME
STRING (255)TEXT
!
constinteger  TOPSIZE = 19
constbyteintegerarray  FSIZES(1:TOPSIZE) = 1, 3, 4, 7, 8, c 
      15, 16,23,24, 31, 32,39,40,47,48,55,56, 63, 64
constinteger  PDSTART = 128
!
constinteger  B0=1, B1=2, B2=4, B3=8, B4=16, B5=32, B6=64, B7=128, c 
      B8=256
!
CONSTSTRING (31)ARRAY  CORRUPTION(1:9) = C 
"Badname",
"H_MARK not 1",
"PDSTART overwritten",
"PD not< SD not< FD(START)",
"PD bytes not n * PDSIZE",
">255 PDs",
"SD bytes not n * SDSIZE",
">8191 SDs",
"FD bytes not n * FDSIZE"
!
!
      FLAG = 1; ! various tests for 'index corrupt'. Flag is
                 ! set to give specific reason then reset to B8
      -> OUTA UNLESS  UNOK(USER) = 0
      -> DOFILEINDEX if  ITYPE = 1
!
      H == RECORD(INDAD)
      FLAG = 2
      -> OUTA unless  H_MARK = 1
      SETI(H_MSGSEMA, -1)
      SETI(H_DIRMON, 0)
!
      SETB(H_IUSE, 0)
      SETB(H_BUSE, 0)
      SETB(H_SB0, 0)
      SETB(H_SB1, 0)
      SETB(H_PASSFAILS, 0)
      SETB(H_SIGMON, 0)
      SETB(H_FSYS, FSYS)
      SETB(H_DIRVSN, 255); ! check various fields
      H_GPHOLDR = "" unless  LENGTH(H_GPHOLDR) = 6
      H_SURNAME = "Initials.Surname" unless  2<LENGTH(H_SURNAME)<32
      H_DELIVERY = "Please set delivery" c 
         unless  2 < LENGTH(H_DELIVERY) < 32
      ! BASEFILE etc
      ! LOGFILE, MAIN, DATA
!
      result  = DOINDEX2(1,INDAD+512,INDNO,FSYS,CROSSEDA,COPYA,USER,
            CLOSE USERS FLAG)
!
!
!
DO FILE INDEX:
      J = FBASE2(FSYS, ADDR(DATA))
      unless  J = 0 START 
         TEXT = "FBASE gives " . ITOS(J)
         -> OUT
      FINISH 
!
      LOBIT = DATA_FILESTART
      HIBIT = DATA_END
!
      B = SYSAD(BITKEY, FSYS)
      SDBITSA = ADDR(SDBITS(0))
      PDBITSA = ADDR(PDBITS(0))
      FILL(1024, SDBITSA, 0); ! to check for multiple use
      FILL(32,  PDBITSA, 0)
      SDX = 0
      PDX = 0
      F == RECORD(INDAD)
!
      LEN = F_SIZE
      cycle  J = 1, 1, TOPSIZE
         -> FSIZEOK if  LEN = FSIZES(J)
      repeat 
      TEXT = "BAD Size " . ITOS(LEN)
      -> OUT; ! bad size
FSIZEOK:
      !now check essential structure
      FLAG = 3
      -> OUTA unless  F_PDSTART = PDSTART
      FLAG = 4
      -> OUTA unless  F_PDSTART < F_SDSTART < F_FDSTART
!
      W = F_SDSTART - F_PDSTART
      NPD = W // PDSIZE
      FLAG = 5
      -> OUTA unless  W = NPD * PDSIZE
      FLAG = 6
      -> OUTA if  NPD > 255
!
      W = F_FDSTART - F_SDSTART
      NSD = W >> 2
      FLAG = 7
      -> OUTA unless  W & 3 = 0
      FLAG = 8
      -> OUTA if  NSD > 8191
!
      W = F_SIZE << 9 - F_FDSTART
      NFD = W // FDSIZE
      FLAG = 9
      -> OUTA unless  W = NFD * FDSIZE
!
      PDS == ARRAY(INDAD+F_PDSTART, PDSF)
      SDS == ARRAY(INDAD+F_SDSTART, SDSF)
      FDS == ARRAY(INDAD+F_FDSTART, FDSF)
!
      SETB(F_TEMPFILES, 0)
      SETI(F_TEMPKB, 0)
      SETI(F_SEMA, -1)
      SETI(F_SEMANO, FSYS << 16 ! INDNO)
      SETI(F_ASEMA, -1)
!
      USED PGS = 0
      CHER PGS = 0
      CHER FILES = 0
!
      SETB(F_FSYS, FSYS)
      F_OWNER = USER unless  F_OWNER = USER
      ! NAME
!
      NF = 7; ! number of files surviving CCK
      IFLAG = 0; ! file flags or'd together
!
      cycle  FI = 1, 1, NFD
         FD == FDS(FI)
         FLAG = 0
         -> BAD FD if  LENGTH(FD_NAME) > 11
         NAME = FD_NAME
!
         if  NAME = ".NULL" START 
            -> NEXTF IF  FI < LEAVE
            -> CLEAR FD
         FINISH 
!
         if  NAME = "" START 
            -> CLEAR FD IF  FI < LEAVE
            -> NEXTF
         FINISH 
!
         -> BAD FD unless  S11OK(NAME) = 0
         CODES = FD_CODES
         CODES2 = FD_CODES2
         PAGS = FD_PGS
         TEMP = CODES & (TEMPFS ! VIOLAT)
         CHER = CODES & CHERSH
!
         SD == FD_SD; ! preliminary scan
         L = 32
         NSECTS = (PAGS + 31) >> 5; ! alleged
         cycle 
            LINK = SD >> 19
            DA = SD << 13 >> 13
            L = (PAGS - 1)&31 + 1 if  LINK = 0; ! last section
            FLAG = FLAG ! B7 unless  LOBIT <= DA <= HIBIT-L; ! DA out of range
            NSECTS = NSECTS - 1
            exit  if  NSECTS < 0
            exit  unless  0 < LINK <= NSD
            SDX = SDX ! SETBIT(SDBITSA, LINK)
            SD == SDS(LINK)
         repeat 
         FLAG = FLAG ! B4 unless  LINK = 0; ! bad link
         FLAG = FLAG ! B5 unless  NSECTS = 0; ! pags not consistent with number of sections
!
         if  FLAG = 0 and  CODES & UNAVA = 0 start ; ! section list appears OK
                                                     ! look for crossed pages
            SD == FD_SD
            L = 32
            MASK = -1
            cycle ; ! through sections
               LINK = SD >> 19
               DA = SD << 13 >> 13
               if  LINK = 0 start ; ! last section
                  L = (PAGS - 1)&31 + 1
                  MASK = ((-1) << (32-L)) >> (DA & 31)
               finish 
               W = (DA>>3)&(¬3); ! word offset
               if  INTEGER(B+W) & MASK # 0 orc 
                   INTEGER(CROSSEDA+W) & MASK # 0 c 
               start 
                   INTEGER(CROSSEDA+W) = INTEGER(CROSSEDA+W) ! MASK
                   FLAG = FLAG ! B6; ! crossed pages
               finish 
               INTEGER(B+W) = INTEGER(B+W) ! MASK
               INTEGER(COPYA+W) = INTEGER(COPYA+W) ! MASK unless  TEMP=NO
               exit  if  LINK = 0
               SD == SDS(LINK)
            repeat 
         finish 
!
         LINK = FD_PHEAD; ! check file permissions
         FD_PHEAD = 0 if  LINK > NPD
         J = 16; ! max number of permissions
         while  0 < LINK <= NPD cycle 
            J = J - 1
            exit  if  J < 0
            PDX = PDX ! SETBIT(PDBITSA, LINK)
            LINK = PDS(LINK)_LINK
         repeat 
         FLAG = FLAG ! B3 unless  LINK = 0 and  J >= 0
!
         FLAG = FLAG ! B1 unless  CODES & (VIOLAT ! UNAVA) = 0
         FLAG = FLAG ! B0 unless  CODES & TEMPFS = 0
!
         if  FI < LEAVE OR  FLAG > 0 or  CODES2 & OLDGE > 0 start ; ! dispose of file
            if  FLAG & B3 = 0 start ; ! pd chain ok
               LINK = FD_PHEAD
               while  LINK > 0 cycle 
                  PD == PDS(LINK)
                  LINK = PD_LINK
                  PD = 0
               repeat 
            finish 
!
            if  FLAG & B4 = 0 start ; ! sd chain ok
               LINK = FD_SD >> 19
               while  LINK > 0 cycle 
                  SD == SDS(LINK)
                  LINK = SD >> 19
                  SD = 0
               repeat 
            finish 
            -> CLEAR FD
         finish 
!
         SETB(FD_USE, 0)
         SETB(FD_CODES2, 0)
!
         NF = NF + 1
         IF  FI > NF START 
            FDS(NF) = FD
            FD = 0
         FINISH 
!
         USED PGS = USED PGS + PAGS
         unless  CHER = NO start 
            CHER FILES = CHER FILES + 1
            CHER PGS = CHER PGS + PAGS
         finish 
         -> NEXTF
BADFD:
         FLAG = FLAG ! B2
         P == RECORD(ADDR(FD))
         PREC("Bad FD: ", P, 0)
CLEAR FD:
         FD = 0
         FD_NAME = ".NULL" IF  FI < 8
NEXTF:
         unless  FLAG = 0 start 
            PRINTSTRING("...".NAME)
            SPACES(15 - LENGTH(NAME))
            WRITEFLAG(FLAG)
            RECORD LOST FILE(USER, NAME, FSYS, CODES2, CODES) ifc 
               FLAG & B0 = 0
            IFLAG = IFLAG ! FLAG
         finish 
      repeat ; ! through all files
!
      LINK = F_FIPHEAD; ! scan file index permissions
      F_FIPHEAD = 0 if  LINK > NPD
      J = 16
      while  0 < LINK <= NPD cycle 
         J = J - 1
         EXIT  IF  J < 0
         PDX = PDX ! SETBIT(PDBITSA, LINK)
         LINK = PDS(LINK)_LINK
      repeat 
      IFLAG = IFLAG ! B3 unless  LINK = 0 and  PDX = 0 AND  J >= 0
      IFLAG = IFLAG ! B4 unless  SDX = 0
!
      SETI(F_FILES, NF - 7)
      SETI(F_TOTKB, USED PGS  << 2)
      SETI(F_CHER FILES, CHERFILES)
      SETI(F_CHER KB, CHER PGS  << 2)
!
      unless  IFLAG & (B2!B3!B4!B5!B7) = 0 start ; ! bad filename, pd or sd
         -> DONT REMOVE IF  USER = "MANAGR"
         CYCLE  J = 1, 1, NEXECPROCS
            -> DONT REMOVE IF  USER = EXECPROCS(J)
         REPEAT 
         IFLAG = IFLAG & (¬(B2!B3!B4!B5!B7)); ! remove b2,b3,b4,b5 and b7
      finish 
DONT REMOVE:
      result  = IFLAG & B6 if  IFLAG & (¬(B0!B1!B6)) = 0; ! anything apart from temp/viol/bits twice files
!
      TEXT = ""
      FLAG = IFLAG
      CYCLE  J = 0, 1, 7
         TEXT = TEXT . " " . MSG(J) IF  FLAG & 1 > 0
         FLAG = FLAG >> 1
      REPEAT 
!
      DOPER2(USER." corrupt fsys".ITOS(FSYS) . TEXT)
      CLOSE USERS FLAG = 1
      RESULT  = IFLAG
OUTA:
      TEXT = CORRUPTION(FLAG)
OUT:
      DOPER2(USER." corrupt fsys".ITOS(FSYS) . " " . TEXT)
      RESULT  = B8; ! index corrupt flag
end ; ! DO INDEX2
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  DDUMPINDNO(INTEGER  FSYS,INDNO)
INTEGER  J,INDAD,TOP
RECORD (HF)NAME  H
RECORD (FF)NAME  F
      J=NINDA(FSYS,INDNO,INDAD)
      -> OUT UNLESS  J = 0
!
      H == RECORD(INDAD)
      J = 99
      -> OUT IF  H_MARK = 0
!
      F == RECORD(INDAD + 512)
      TOP = F_SIZE << 9 + 512
!
      TOP=X'1000' UNLESS  TOP=X'800' ORC 
          (X'1000'<=TOP<=X'8000' AND  TOP<<21=0)
      DDUMP(INDAD,INDAD+TOP,-1,-1)
      J = 0
OUT:
      RESULT =J
END ; ! DDUMPINDNO
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  ADJUST DLVN BIT(INTEGER  FSYS, SET)
! SET = 0 to clear bit and make disc available, else 1
INTEGER  J
BYTEINTEGERARRAYNAME  DLVNA
INTEGERARRAYFORMAT  DITF(0:COM_NDISCS-1)
INTEGERARRAYNAME  DIT
RECORD (DDTF)NAME  DDT
      RETURN  UNLESS  0 <= FSYS <= 99
!
      DLVNA == ARRAY(COM_DLVNADDR, DLVNAF)
      DIT == ARRAY(COM_DITADDR, DITF)
      J = DLVNA(FSYS)
      RETURN  IF  J > 250
      DDT == RECORD(DIT(J))
!
      IF  SET = 0 START 
         DDT_DLVN = DDT_DLVN & 255
         EMPTY DVM
         DDT_CONCOUNT = 0
      FINISH  ELSE  START 
         DDT_DLVN = DDT_DLVN ! (1 << 31)
      FINISH 
END ; ! ADJUST DLVN BIT
!
!-----------------------------------------------------------------------
!
ROUTINE  CHECK BADS(INTEGER  FSYS)
INTEGER  P, J, K, BITMAP SIZE
RECORD (DISCDATAF)DATA
      J = FBASE2(FSYS, ADDR(DATA))
      BITMAPSIZE = DATA_BITSIZE
!
      P = SYSAD(BADKEY, FSYS)
      J = 0
      CYCLE  K = 0, 4, BITMAP SIZE - 4
         EXIT  IF  J > 256
         J = J + DBITS(INTEGER(P + K))
      REPEAT 
      FILL(BITMAP SIZE, P, 0) IF  J > 256
END ; ! OF CHECK BADS
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  CLEAR FSYS(INTEGER  FSYS)
INTEGER  P, J, K, INDAD, B
RECORD (DISCDATAF)DATA
      ADJUST DLVN BIT(FSYS, 0)
      J = FBASE2(FSYS, ADDR(DATA))
                                        ! IF THIS IS A NEW DISC AND OPS
                                        ! HAVE OMITTED TO DO A
                                        !   'CLEAR BAD PAGES'
                                        ! WE DO ONE IF THERE ARE > 256
      CHECK BADS(FSYS)
!
      CYCLE  P = DATA_INDEXSTART, 1, DATA_FILESTART-1
         J = MOVESECTION(-1, 0, FSYS, DATA_START+P, 1); ! sets badpage bit if transfer fails
!
         IF  J = 0 START 
            CYCLE  K = 0, 1, 3
               J = NINDA(FSYS, P<<2+K, INDAD)
               WRSN("Clear fsys/NINDA", J) AND  RETURN  UNLESS  J = 0
               STRING(INDAD) = "NEVER"; ! MARK EACH 'K' OF PAGE 'NEVER'
            REPEAT 
         FINISH 
!
      REPEAT 
!
      B = SYSAD(BITKEY, FSYS)
      MOVE(DATA_BITSIZE, SYSAD(BADKEY,FSYS), B); ! init bitmap with badpages
      INTEGER(B) = -1
      INTEGER(B+8) = FSYS
!
      FILL(DATA_NNTSIZE, SYSAD(NNTKEY,FSYS), 0); ! clear NNT
!
      DOPER2("FSYS ".ITOS(FSYS)." cleared OK")
END ; ! CLEAR FSYS
!
!-----------------------------------------------------------------------
!
INTEGERFN  HOW FULL(INTEGER  FSYS)
INTEGER  I, N, B, PC, LO, HI
RECORD (DISCDATAF)DATA
      I = FBASE2(FSYS, ADDR(DATA))
      RESULT  = -1 UNLESS  I = 0
!
      B = SYSAD(BITKEY, FSYS)
      LO = (B+(DATA_START + DATA_FILESTART) >> 3) & (-4)
      HI = (B+DATA_END >> 3) & (-4)
      N = 0
!
      CYCLE  I = LO, 4, HI-4
         N = N + DBITS(INTEGER(I))
      REPEAT 
!
      RESULT  = (100*N) // ((HI-LO) << 3)
END ; ! HOW FULL
!
!-----------------------------------------------------------------------
!
ROUTINE  NEQS(INTEGER  LENGTH, FROM, TO)
      *LB_LENGTH
      *JAF_13,<L99>; ! J IF NOT > ZERO
      *LDTB_X'18000000'
      *LDB_B 
      *LDA_FROM
      *CYD_0
      *LDA_TO
      *NEQS_L =DR 
L99:
END ; ! OF NEQS
!
!-----------------------------------------------------------------------
!
ROUTINE  ADVISE EXECUTIVES(INTEGER  FSYS)
INTEGER  I,J
RECORD  (PARMF)P
      P=0
      CYCLE  I=1,1,NEXECPROCS
         P_DEST=X'FFFF0000' ! X'15'; ! CCK DONE DACT
         P_P1=FSYS
         J=DPON3I(EXECPROCS(I),P,0,SYNC1 TYPE,PON AND CONTINUE)
      REPEAT 
END ; ! ADVISE EXECUTIVES
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  CCK(INTEGER  FSYS, CHECK, INTEGERNAME  PERCENT)
! CHECK = 0 ordinary CCK
!     not 0 do CCK but dont make disc available or tell executives
INTEGER  X, K, NNTTOP, PAGE, CLOSE USERS FLAG
INTEGER  PT, STOP, NNTA
INTEGER  B, J, L, INDNO, HI, ADD, DONE, PASS
INTEGER  BIT MAP COPY A
INTEGER  GOOD, CH, INDAD, FLAG, TYPE
STRING (6)OWNER
STRING (12)NAME
STRING (18)INDEX
STRING (31)S
RECORD (NNF)NAME  NN
RECORD (NNF)ARRAYFORMAT  NNTF(0 : 16384)
RECORD (NNF)ARRAYNAME  NNT
RECORD (HF)NAME  H
RECORD (FF)NAME  F
BYTEINTEGERARRAY  BITMAPCOPY(0:X'4FFF')
BYTEINTEGERARRAY  CROSSEDPAGES(0:X'4FFF')
RECORD (LOSTF)ARRAY  LOSTARRAY(0:TOPLOST)
BYTEINTEGERARRAY  NNC(0:1364); ! for a 4-page NNT
RECORD (DISCDATAF)DATA
!
!
ROUTINE  OPMESS(STRING (255)S)
INTEGER  OCP
      *LSS_(3)
      *USH_-26
      *AND_3
      *ST_OCP
!
      DOPER2("OCP".ITOS(OCP)." ".S)
END 
!
      WRS3N("CCK ", VSN, "FSYS", FSYS)
      PERCENT = -1; ! just in case of failure
      CLOSE USERS FLAG = 0; ! initialise
!
      RESULT  = 23 UNLESS  FBASE2(FSYS, ADDR(DATA)) = 0; ! not on-line
      RESULT  = 69 UNLESS  AV(FSYS, 0) = 0; ! CCK already done
!
      LOSTARAD=ADDR(LOSTARRAY(0))
!
      LOG ACTION = LOG ACTION & (¬DT); ! knock out date
!
      X = 40; ! 'free' bytes at start of bitmap
      X = 288 IF  FSYS = COM_SUPLVN
!
      NNTA = SYSAD(NNTKEY, FSYS)
      NNTTOP = DATA_NNTTOP
!
      NNT == ARRAY(NNTA, NNTF)
!
      CYCLE  PT = 0, 1, NNTTOP; ! set marker bits for in-use entries
         IF  LENGTH(NNT(PT)_NAME) = 6 C 
         THEN  NNC(PT) = 1 C 
         ELSE  NNC(PT) = 0
 
      REPEAT 
!
      FILL(DATA_BITSIZE, ADDR(CROSSEDPAGES(0)), 0)
      B = SYSAD(BITKEY,FSYS)
      PASS = 0
PASS2:
      DONE = 0
      PAGE = 0; ! last page checked by BADPAGE
      BIT MAP COPY A = ADDR(BITMAPCOPY(0))
      FILL(DATA_BITSIZE, BIT MAP COPY A, 0); ! CLEAR BIT MAP COPY
      CHECK BADS(FSYS)
      MOVE(DATA_BITSIZE-X, SYSAD(BADKEY,FSYS)+X, B+X); ! INIT BIT MAP WITH BAD PAGES ONLY
         ! but do not clear the 'DIRCOM' area, it may hold filenames
      INTEGER(B) = -1; ! BIT MAP SEMAPHORE
      INTEGER(B+8) = FSYS
      J = PACKDT
      INTEGER(B+16) = J << 15 >> 15; ! Time
      INTEGER(B+20) = J >> 17; ! Date
!
      INDNO = DATA_INDEX START << 2
      HI = DATA_FILE START << 2 - 1
      WHILE  INDNO<=HI CYCLE 
!
         ADD = X'800'; ! just examine even numbered K's
         J = INDNO >> 2
         UNLESS  PAGE = J START 
            PAGE = J
            IF  BAD PAGE(3, FSYS, DATA_START+J) = YES START 
               WRSN("BAD PAGE AT", J)
               INDNO = (J<<2) + 2; ! SO THAT WE GO TO NEXT PAGE !!!!
               -> NEXTI
            FINISH 
         FINISH 
!
!
         J = SYSAD(NNTKEY, FSYS)
         UNLESS  J = NNTA START 
            OPMESS("NNT Remapped on fsys".itos(fsys))
            NNTA = J
            NNT == ARRAY(NNTA, NNTF)
            B = SYSAD(BITKEY, FSYS)
         FINISH 
!
         J=NINDA(FSYS,INDNO,INDAD)
         OPMESS("CCK/NINDA".ITOS(J)) UNLESS  J = 0
!
         L = BYTEINTEGER(INDAD); ! length of name of main or empty index
                                 ! top byte of 'sdstart' of file index
         IF  L > 6 START ; ! reject outright
            S = "1st byte > 6"
            -> BAD INDEX
         FINISH 
!
         H == RECORD(INDAD); ! now decide if EMPTY, MAIN or FILE index
         OWNER = H_OWNER
         IF  OWNER = "NEVER" START 
            WRSN("CCK exits on NEVER at", INDNO)
            EXIT 
         FINISH 
         -> NEXTI IF  OWNER = "EMPTY"
!
         IF  L = 6 START ; ! looks like a main index
            TYPE = 0
            NAME = ""
            ADD = H_TOP
            -> CHECK
         FINISH 
!
   -> BAD INDEX; ! no file indexes for now
         F == RECORD(INDAD); ! see if FILE index
         UNLESS  LENGTH(F_OWNER) = 6 START 
            S = "L(F_OWNER) not 6"
            -> BAD INDEX
         FINISH 
!
         UNLESS  0 < LENGTH(F_NAME) < 12 START 
            S = "L(F_NAME) not 1-11"
            -> BAD INDEX
         FINISH 
!
         TYPE = 1
         OWNER = F_OWNER
         NAME = F_NAME
         ADD = F_SIZE << 9
!
         IF  NAME = "#ARCH" START 
            S = NAME
            -> BAD INDEX
         FINISH 
CHECK:
         GOOD = 0
!
         CYCLE  J = 1, 1, 6; ! check owner
            CH = CHARNO(OWNER, J)
            GOOD = 1 UNLESS  'A'<=CH<='Z' OR  '0'<=CH<='9'
         REPEAT 
!
         GOOD = 2 UNLESS  ADD=X'800' OR  (X'1000'<=ADD<=X'8000' ANDC 
                                (ADD & X'FFF' = 0))
         ! report
         WRSNT(OWNER, INDNO, X'36'); ! index no 3 hex digits
         IF  GOOD = 2 C 
         THEN  WRSNT(" Bad size", ADD, 4) C   {dec if small else hex}
         ELSE  WRSNT("", ADD>>10, X'25')      {2 dec digits}
         PRINTSTRING("K")
!
         PRINTSTRING(" file index: " . NAME) IF  TYPE = 1
!
         UNLESS  GOOD = 0 START 
-> bad index
            WRS(" BAD index")
            ADD = X'800'
            -> NEXTI
         FINISH 
         NEWLINE
!
!
! Now check that there is an NNT entry for this owner. If name is not null,
! it signifies a file index for owner. If the entry is found, the
! corresponding entry in the NNT copy is deleted. If the NNT entry is
! found, but it points to a different index (only applicable to main
! indexes) 'duplicate index' is reported. If no NNT entry is found, an attempt
! is made to create one. If this fails, 'NNT full' is reported.
!
         K = -1; ! remember first free entry, in case reqd
         PT = HASH(OWNER, DATA_NNTHASH)
         STOP = PT
         UNTIL  PT = STOP CYCLE 
            NN == NNT(PT)
            IF  OWNER = NN_NAME START ; ! possible
               IF  NAME = "" START ; ! looking for a main index
                  IF  NN_TAG = 0 START ; ! it is an entry for a main index
                     IF  INDNO = NN_INDNO C  {correct INDNO}
                     THEN  NNC(PT) = 0 AND  -> DOINDEX C 
                     ELSE  START ; ! already present with different index
                        DOPER2(OWNER . " Duplicate") IF  PASS = 0
                        -> DOINDEX
                     FINISH 
                  FINISH 
               FINISH  ELSE  START 
                  IF  NN_TAG > 0 START ; ! this NNT entry is for a file index
                     IF  INDNO = NN_INDNO C 
                     THEN  NNC(PT) = 0 AND  -> DO INDEX
                  FINISH 
               FINISH 
            FINISH  ELSE  START 
               K = PT IF  K < 0 AND  LENGTH(NN_NAME) < 6; ! first free
            FINISH 
            IF  PT = NNTTOP THEN  PT = 0 ELSE  PT = PT + 1
         REPEAT 
!
!     appropriate entry not found, so make one
         INDEX = OWNER
         INDEX = OWNER . ISEP . NAME UNLESS  NAME = ""
!
         DOPER2("New Index ".INDEX." on Fsys" . ITOS(FSYS))
!
         IF  K < 0 THEN  DOPER2("NNT FULL") ELSE  START 
            NN == NNT(K)
            NN = 0
            NN_NAME = OWNER
            NN_KB = ADD >> 10
            NN_INDNO = INDNO
            NN_TAG = 1 UNLESS  NAME = ""
         FINISH 
DOINDEX:
         FLAG = DOINDEX2(TYPE, INDAD, INDNO, FSYS, ADDR(CROSSEDPAGES(0)),
                  BITMAPCOPYA, OWNER, CLOSE USERS FLAG)
!
         DONE=DONE ! FLAG
         -> NEXTI
BAD INDEX:
         opmess("BAD INDEX at " . HTOS(INDNO, 3) . " Fsys" . ITOS(FSYS) . " " . S)
CLOSE USERS FLAG = 120
-> OUT
NEXTI:
         ADD=ADD>>10
         INDNO=INDNO + ADD
      REPEAT 
!
      IF  DONE&(1<<6)#0 AND  PASS=0 START ; ! Crossed pages
         PASS=1
         DOPER2("FSYS ".ITOS(FSYS)." PASS2")
         -> PASS2
      FINISH 
!
      DONE = DONE & (¬(1<<6)); ! remove 'bits twice' bit
!
      LOG ACTION = LOG ACTION ! DT; ! put date back in
      NEQS(DATA_BITSIZE-X, BITMAPCOPY A+X, B+X); ! clear tempfile bits in bitmap
                                                ! avoiding 'free' bytes at front
!
      PERCENT = HOW FULL(FSYS)
!
      CYCLE  PT = 0, 1, NNTTOP
         IF  NNC(PT) = 1 START 
            NN == NNT(PT)
            CLOSE USERS FLAG = 1
            DOPER2(NN_NAME . " MISSED, FSYS" . ITOS(FSYS))
            DOPER2("(file index)") IF  NN_TAG > 0
         FINISH 
      REPEAT 
OUT:
      ADJUST DLVN BIT(FSYS, 0)
!
      ! Place records of files lost in file VOLUMS.LOSTFILES
      RECORD LOST FILE("","",FSYS,0,0)
!
      FLAG = DONE ! CLOSE USERS FLAG
!
      IF  CHECK = 0 START 
         IF  FLAG = 0 ORC  {it worked}
             FSYS = COM_SUPLVN ORC  {didn't work, but is IPL disc}
             COM_IPLDEV >= 0 C  {... but not in 'auto' mode}
         THEN  ADVISE EXECUTIVES(FSYS)
      FINISH  ELSE  ADJUST DLVN BIT(FSYS, 1)
!
      RESULT  = FLAG
END ; ! CCK
!
!-----------------------------------------------------------------------
!
ROUTINE  SORT STRINGS(STRINGARRAYNAME  U, INTEGER  N)
INTEGER  I, J, K, M
STRING (255)W
      RETURN  IF  N < 1
!
      M = 1
      M = M << 1 WHILE  M <= N
      M = M - 1
!
      CYCLE 
         M = M >> 1
         EXIT  IF  M = 0
         CYCLE  I = 1, 1, N-M
            K = I
            WHILE  K > 0 CYCLE 
               J = K + M
!
               EXIT  IF  U(K) <= U(J)
               W = U(J)
               U(J) = U(K)
               U(K) = W
!
               K = K - M
            REPEAT 
         REPEAT 
      REPEAT 
END ; ! SORT STRINGS
!
!-----------------------------------------------------------------------
!
INCLUDE  "PD22S_A03TEST"
EXTERNALROUTINE  SET STOP
INTEGER  J
      IF  PROCUSER = "DIRECT" C 
      THEN  J = PRIME CONTINGENCY(PROCESS1) C 
      ELSE  J = PRIME CONTINGENCY(STOP ONE)
END ; ! SET STOP
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  DERR2(STRING (31)S,INTEGER  FN,ERR)
STRING (15) LOC
CONSTINTEGER  TOP = 8
CONSTSTRING (15)ARRAY  NAME(1:TOP) = C 
" 1 DCREATE",
" 2 DCONNECT",
" 3 DPERMISSION",
" 4 DSFI",
" 5 DDISCONNECT",
" 6 DSTOP",
" 7 DGETDA",
" 8 STARTP"
      LOC = NAME(FN)
      PRINTSTRING(S)
      IF  1 <= FN <= TOP C 
      THEN  PRINTSTRING(FROMSTRING(LOC, 3, LENGTH(LOC))) C 
      ELSE  WRITE(FN, 6)
      SPACE
      WRS(DERRS(ERR))
END ; ! DERR2
!
!-----------------------------------------------------------------------
!
externalroutine  NCODE(integer  PC)
!
!
conststring  (4) array  OPS(0 : 127) =       c 
"    ","JCC ","JAT ","JAF ","TEST","    ","CLR*","SET*",
"VAL ","CYD ","INCA","MODD","PRCL","J   ","JLK ","CALL",
"ADB ","SBB ","DEBJ","CPB ","SIG ","MYB ","VMY ","CPIB",
"LCT ","MPSR","CPSR","STCT","EXIT","ESEX","OUT ","ACT ",
"SL  ","SLSS","SLSD","SLSQ","ST  ","STUH","STXN","IDLE",
"SLD ","SLB ","TDEC","INCT","STD ","STB ","STLN","STSF",
"L   ","LSS ","LSD ","LSQ ","RRTC","LUH ","RALN","ASF ",
"LDRL","LDA ","LDTB","LDB ","LD  ","LB  ","LLN ","LXN ",
"TCH ","ANDS","ORS ","NEQS","EXPA","AND ","OR  ","NEQ ",
"PK  ","INS ","SUPK","    ","COMA","DDV ","DRDV","DMDV",
"SWEQ","SWNE","CPS ","TTR ","FLT ","IDV ","IRDV","IMDV",
"MVL ","MV  ","CHOV","    ","FIX ","RDV ","RRDV","RDVD",
"UAD ","USB ","URSB","UCP ","USH ","ROT ","SHS ","SHZ ",
"DAD ","DSB ","DRSB","DCP ","DSH ","DMY ","DMYD","CBIN",
"IAD ","ISB ","IRSB","ICP ","ISH ","IMY ","IMYD","CDEC",
"RAD ","RSB ","RRSB","RCP ","RSC ","RMY ","RMYD","    "
!
!
ROUTINE  MASK(INTEGER  N)
! prints bottom 4 bits in binary
INTEGER  J
      PRINTSTRING(" MASK=B'")
      CYCLE  J = 3, -1, 0
         PRINTSYMBOL('0' + ((N >> J) & 1))
      REPEAT 
END 
!
!
routine  PHX(integer  N, PLACES, SIGN)
      PRINTSYMBOL(SIGN)
!
      N = N & X'7F' IF  PLACES = 2
      N = N & X'3FFFF' IF  PLACES = 5
!
      if  0<=N<=9  C  
      then  PRINTSTRING(ITOS(N)) C 
      else  PRINTSTRING("X'" . HTOS(N, PLACES) . "'")
end ; ! PHX
!
!
!
integer  I, K, KP, KPP, N, N1, OPCODE
integer  INSL, DEC,LITERAL,JUMP,N7,N18
integer  H, Q, INS, KPPP
integer  START, FINISH
integer  SIGN,ILLEGAL
integer  ALL
STRING (7)W
SWITCH  DECSW(1:3)
!
!
!
   conststring  (12) array  PREFPOP(0 : 31) =       c 
"","***         ","(LNB","(XNB",
"(PC","(CTB","TOS         ","B           ",
"(DR","***         ","(DR+(LNB","(DR+(XNB",
"(DR+(PC","(DR+(CTB","(DR+TOS)    ","(B",
"IS LOC N   ","***         ","((LNB","((XNB",
"((PC","((CTB","(TOS)       ","(DR)        ",
"IS LOC B   ","***         ","((LNB","((XNB",
"((PC","((CTB","(TOS+B)   ","(DR+B)    "
   conststring (8) array  SUFPOP(0:31) = c 
"","",")     ",")     ",
")      ",")     ","","",
")      ","",")) ",")) ",
")) ",")) ","",")*      ",
"","","))    ","))   ",
"))    ","))    ","","",
"","",")+B) ",")+B) ",
")+B)  ",")+B) ","",""
   conststring  (8) array  TOP(0 : 7) =     c 
"","(DR+","(LNB+","(XNB+","(PC+","(CTB+","(DR)   ","(DR+B) "
conststring (7) array  JAS(0:15)= c 
"FACC=0","FACC>0","FACC<0"," ? ","ACC=0","ACC>0","ACC<0", 
" ? ","DACC=0","DACC>0","DACC<0","DRLEN=0",
" B=0 "," B>0 "," B<0 ","OV SET"
!
!
!
!
      START = PC - 128
      FINISH = PC + 128
!
      START = FINISH >> 18 << 18 unless  START>>18 = FINISH>>18
      ALL = FINISH-START
      PRINTSTRING("Code around")
      PHX(PC, 5, ' ')
      NEWLINE
      PC = 0
      I = X'18000000'!ALL
      *LDTB_I
      *LDA_START
      *VAL_(lnb +1)
      *JCC_3,<BADADDR>
!!
   while  PC < ALL  cycle 
      H = 0
      LITERAL=0
      JUMP=0
      INSL = 32
      MOVE(4,START+PC,ADDR(INS))
      N1 = INS & X'3FFFF'
      N = INS << 9 >> 25
      KP = N >> 5
      KPP = INS << 11 >> 29
      KPPP = KPP
!
      OPCODE = INS>>25<<1
      if  OPCODE = 0 or  OPCODE = 254 or  8 <= OPCODE <= 14 start 
         INSL = 16
         ILLEGAL = 1
         DEC = 0
      finish  else  start 
         ILLEGAL = 0
         IF  2 <= OPCODE < 8 START 
            DEC = 3; ! tertiary
            N = N1
            IF  KPPP > 5 START 
               INSL = 16
               ILLEGAL = 1 UNLESS  (INS >> 16) & 3 = 0
            FINISH 
         FINISH  ELSE  START 
            IF  8 <= OPCODE >> 4 <= X'B' AND  OPCODE & 15 < 8 START 
               DEC = 2; ! secondary
               H = INS << 7 >> 31
               Q = INS << 8 >> 31
               INSL = 16 UNLESS  Q = 1
            FINISH  ELSE  START 
               DEC = 1; ! primary
               K = INS << 7 >> 30
               IF  K = 3 START 
                  LITERAL = 1 IF  KP = 0 = KPP
                  IF  KPP < 6 C 
                  THEN  N = N1 C 
                  ELSE  START 
                     INSL = 16
                     ILLEGAL = 1 UNLESS  INS & X'30000' = 0
                  FINISH 
               FINISH  ELSE  START 
                  LITERAL = 1 IF  K = 0
                  INSL = 16
               FINISH 
            FINISH 
         FINISH 
      FINISH 
      JUMP=1 if  X'1A'<=OPCODE<=X'1E' or  OPCODE=X'24'
!
      WRSNT("", (START+PC) & X'3FFFF', X'56'); ! address
      if  INSL = 16 c 
      then  SPACES(6) and  PRINTSTRING(HTOS(INS >> 16, 4)) c 
      else  SPACES(2) and  PRINTSTRING(HTOS(INS, 8)); ! instruction in hex
!
      W = OPS(OPCODE >> 1)
      ->END if  ILLEGAL=1 or  W="    " or  INS=X'81818181'
      SPACES(2)
      PRINTSTRING(W); ! opcode
      SPACE
!
      SIGN = '+'
      N7 = -(N ! X'FFFFFF80')
      N18 = -(N ! X'FFFC0000')
      -> DECSW(DEC)
!
DECSW(1):         ! PRIMARY FORMAT
      -> END if  OPCODE=X'3A' or  OPCODE=X'4E' or  OPCODE=X'12' orc 
                 OPCODE=X'EE' or  OPCODE=X'DE'
      SPACE IF  LITERAL = 0
      if  K < 3 start 
         PRINTSYMBOL('(') IF  K = 2
         PRINTSTRING("(LNB") IF  K > 0
         SIGN = '-' AND  N = N7 IF  N>>6 # 0 = K
         PHX(N, 2, SIGN) UNLESS  JUMP = 1 = LITERAL
         PRINTSYMBOL(')') IF  K = 2
         PRINTSTRING(")   ") IF  K > 0
      FINISH  ELSE  START 
         PRINTSTRING(PREFPOP(KP*8+KPP))
         if  INSL = 32 start 
            if  (KP = 0 = KPP) or  KPP = 4 start 
               N = N18 and  SIGN = '-' if  (N>>17) > 0
               PRINTSYMBOL(SIGN) if  KPP = 4
            finish  else  PRINTSYMBOL(SIGN)
            PHX(N, 5, ' ') unless  LITERAL # 0 # JUMP
            PRINTSTRING(SUFPOP(KP*8+KPP))
         finish 
         N = -N if  SIGN = '-'
         WRSNT("ie ", (PC+START+(N*2)),X'56') if  KP=0 and  KPP=4
      finish 
      if  LITERAL # 0 = JUMP and  IMOD(N)>9 start 
         PRINTSYMBOL('[')
         PRINTSYMBOL('-') if  SIGN = '-'
         PRINTSTRING(ITOS(N)."]")
      finish 
      if  LITERAL # 0 # JUMP start 
         PRINTSTRING(" TO") 
         N = -N if  SIGN = '-'
         PHX((PC+START+(N*2)), 5, ' ')
      finish 
      -> END
DECSW(2):         ! SECONDARY FORMAT
      PHX((INS>>16),2,0) if  H=0
      if  INSL = 32 start 
         MASK(INS >> 8)
         PRINTSTRING(" LITERAL")
         PHX(INS, 2, '=')
      finish 
      -> END
DECSW(3):         ! TERTIARY FORMAT
      PRINTSTRING(TOP(KPPP))
      if  INSL = 32 start 
         SIGN = ' '
         if  KPPP = 0 or  KPPP = 4 start 
            if  (N>>16) > 1 c 
            then  N = N18 and  SIGN = '-'
         finish 
         if  KPPP = 0 start  
            N = -N if  SIGN = '-'
            PRINTSTRING(" TO") 
            PHX((PC+START+(N*2)), 5, ' ') 
         finish  else  PHX(N, 5, SIGN)
         PRINTSYMBOL(')') if  1 <= KPPP <= 5
         if  4<=OPCODE<=6 c 
         then  PRINTSTRING("   ON ".JAS((INS>>21)&15)) c 
         else  MASK(INS >> 21)
      finish 
END:
         NEWLINE
         PC = PC+(INSL>>3)
      repeat 
      RETURN 
!
!
!
BADADDR:
      WRSNT("NCODE: validation fails ", START, 6)
      WRSNT(" : ", FINISH, 2)
end ; ! NCODE
!
!-----------------------------------------------------------------------
!
SYSTEMROUTINE  NDIAG(INTEGER  PC, OLD, FAULT, I)
INTEGER  NEW, GLA, LANG, T, N
STRING (255)S
L1:
      NEW = INTEGER(OLD)
      GLA = INTEGER(OLD + 16)
      *LDTB_X'18000020'
      *LDA_GLA
      *VAL_(LNB  + 1)
      *JCC_3,<L3>
!
      LANG = BYTEINTEGER(GLA + 16)
      RETURN  IF  LANG > 5 OR  LANG = 4 OR  LANG = 0
      -> L2 UNLESS  LANG & 1 > 0; ! IMP
      T = INTEGER(OLD + 12) & X'FFFFFF'
      CYCLE 
         RETURN  IF  T = 0
         T = T + INTEGER(GLA + 12)
         EXIT  UNLESS  INTEGER(T + 12) = 0
         T = INTEGER(T + 4) & X'FFFF'
      REPEAT 
      S = STRING(T + 12) . " " . ITOS(INTEGER(T) >> 16)
      IF  PROCUSER = "DIRECT" C 
      THEN  DOPER2(S) C 
      ELSE  WRS(S)
L2:
      N = OLD + 20
      N = (N + 267) & (-32) IF  PC >> 18 = 2
      DDUMP(OLD, N, -1, -1)
L3:
      RETURN  IF  NEW = OLD
      PC = INTEGER(OLD + 8)
      OLD = NEW
      -> L1 UNLESS  NEW < COM36
END ; ! NDIAG
!
!-----------------------------------------------------------------------
!

! LAYOUT OF DIAGNOSIC TABLES
!+**********************

! THE BOUND FIELD OF PLT DESCRIPTOR STORED AT (LNB+3 & LNB+4) IF
! USED TO CONTAIN A DISPLACEMENT RELATIVE TO THE START OF SST OF THE
! DIAGNOSTIC TABLES FOR THE BLOCK OR ROUTINE BEING EXECUTED.
! A ZERO BOUND MEANS NO DIAGNOSTIC REQUESTED.(NB THIS MAY MEAN A DUMMY
! FIRST WORD IN THE SST).
! THE ABSOLUTE ADDRESS OF THE SST FOR THE CURRENT CODE SEGMENT WILL
! ALWAYS BE FOUND IN THE STANDARD 10 WORDS OF THE GLA/PLT

! FORM OF THE TABLES:-

! WORD 0    =   LINE OF RT IN SOURCE PROG <<16 ! LINE NO POSN(FROM LNB)
! WORD 1    =   (12 LANG DEPENDENT BITS)<<20 ! ENVIRONMENT
! WORD 2    =   DISPLAY POSN (FROM LNB)<<16 ! RT TYPE INFO
! WORD 3    =   ZERO FOR BLKS OR STRING(<=11BYTES) BEING THE
!               RT NAME. THIS WILL TAKE WORDS 4 AND 5 IF NEEDED
! WORD 6    =  LANGUAGE DEPENDENT INFO . IMP ON CONDITIONS ETC

! THE REST IS MADE UP OF VARIABLE ENTRIES AND THE SECTION IS TERMINATED BY
! A WORD OF X'FFFFFFFF'

!  EACH VARIABLE ENTRY CONSISTS OF THE VARIABLE WORD FOLLOWED BY
! THE VARIABLE NAME AS A STRING. THE WORD CONSISTS OF
! BITS 2**31 TO 2**20 TYPE INFORMATION (MAY BE LANGUAGE DEPENDENT
! BIT  2**19  =0 UNDER LNB =1 IN GLA
! BITS 2**18 TO 2**0 DISPLACEMENT FROM LNB(GLA) IN BYTES


! THE ENVIRONMENT IS A POINTER (RELATIVE TO SST) OF THE NEXT OUTERMOST
! BLOCK OR A POINTER TO GLOBAL OWNS, EXTERNAL OR COMMON AREAS
! A ZERO MEANS NO ENCLOSING BLOCK. WORD1=WORD3=0 IS AN
! IMP MAIN PROGRAM AND WILL TERMINATE THE DIAGNOSTICS.

STRINGFN  JUST(STRING (255)S)
INTEGER  A, J, K
      J = 1
      K = LENGTH(S)
      J = J + 1 WHILE  J < K AND  CHARNO(S, J) = ' '
      K = K - 1 WHILE  J < K AND  CHARNO(S, K) = ' '
      J = J - 1
      A = ADDR(S) + J
      BYTEINTEGER(A) = K - J
      RESULT  = STRING(A)
END 
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  DBITMAP2(INTEGERNAME  LO, HI, INTEGER  FSYS)
                                        ! IF LO < 0, L := ADDR(BAD PAGES LIST)
                                        ! ELSE LO := ADDR(FIRST WORD OF BITMAP)
                                        !  AND HI := ADDR(LAST)-4
INTEGER  B
RECORD (DISCDATAF)DATA
      RESULT  = 23 IF  AV(FSYS, 0) = 0; ! NOT AVAILABLE
!
      IF  LO < 0 C 
      THEN  LO = SYSAD(BADKEY, FSYS) AND  RESULT  = 0
!
      B = FBASE2(FSYS, ADDR(DATA))
      B = SYSAD(BITKEY, FSYS)
      LO = (B + (DATA_START + DATA_FILE START)>>3) & (-4)
      HI = (B + DATA_END>>3) & (-4)
      RESULT  = 0
END ; ! DBITMAP2
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  DDELUSER(STRING (18)FILE INDEX, INTEGER  FSYS)
INTEGER  G0, FSYSW, K, NNAD
INTEGER  JUNK, J,INDAD,CYCLE TO, I, N, MAX
STRING (31)UNA, INA, INDEX
RECORDFORMAT  KYFF(STRING (11)NAME, INTEGER  A,B,C,D,E)
RECORD (KYFF)ARRAY  FILE(0:31)
RECORD (FF)NAME  F
RECORD (HF)NAME  H
RECORD (NNF)NAME  NN
      J = IN2(15)
      -> OUT UNLESS  J = 0
!
      J = 93
      -> OUT UNLESS  DTRYING << 5 < 0
!
      J = UIO(FILE INDEX, UNA, INA, INDEX)
      -> OUT UNLESS  J = 0
!
      G0 = -1
AGAIN:
      FSYSW = FSYS; ! FSYS specified may be specific or -1
      J = FIND NNT ENTRY(INDEX, FSYSW, NNAD, 0)
      G0 = J IF  G0 = -1; ! remember first result
      J = G0 AND  -> OUT UNLESS  J = 0
!
      J = DPERMISSIONI(INDEX, PROCUSER, "", "", FSYSW, 7, 0); ! REMOVE
      J = DPERMISSIONI(INDEX, PROCUSER, "", "", FSYSW, 6, 7); ! FULL INDEX PERM
      -> XIND UNLESS  J = 0
!
NAMES:
      MAX = 32
      J = DFILENAMES(INDEX, FILE, JUNK, MAX, N, FSYSW, 0)
      -> XIND UNLESS  J = 0
!
      IF  N > 0 = MAX START ; ! very curious, does it happen?
         WRSN("Dfilenames N", N)
      FINISH 
!
      I = 0
      WHILE  I < MAX CYCLE 
         K = DDESTROYF(INDEX . "." . FILE(I)_NAME, FSYSW, 1)
         DOPERR("DDELUSER", 15, K) UNLESS  K = 0
         J = J ! K
         I = I + 1
      REPEAT 
      -> NAMES IF  MAX > 0 AND  J = 0
XIND:
      J = FIND NNT ENTRY(INDEX, FSYSW, NNAD, 0)
      -> OUT UNLESS  J = 0
!
      NN == RECORD(NNAD)
      J = NINDA(FSYSW, NN_INDNO, INDAD)
      -> OUT UNLESS  J = 0
      NN_NAME = ".NULL"
!
    ! Write "EMPTY" at the start of each 1K, but first make sure
    ! that the index we are deleting (in particular the index size field) is
    ! reasonable.
      CYCLE TO=0
      J = 87
      IF  INA = "" START ; ! main index
         H == RECORD(INDAD)
         -> OUT UNLESS  UNA = H_OWNER
         J = H_TOP >> 10
         IF  J = 2 OR  (4 <= J <= 32 AND  H_TOP & X'FFF' = 0) C 
         THEN  CYCLE TO = H_TOP - X'400'
      FINISH  ELSE  START ; ! file index
         F == RECORD(INDAD)
         -> OUT UNLESS  UNA = F_OWNER
         -> OUT UNLESS  INA = F_NAME
         CYCLE TO = (F_SIZE << 9) - X'400'
      FINISH 
!
      CYCLE  J=0,X'400',CYCLE TO
         H==RECORD(INDAD+J)
         H_OWNER="EMPTY"
      REPEAT 
      -> AGAIN; ! round again in case there are more
OUT:
      EMPTY DVM
      RESULT  = OUT(J, "SI")
END ; ! DDELUSER
!
!-----------------------------------------------------------------------
!
!<DEMPTYI
externalintegerfn  DEMPTYI(integer  FSYS, INDNO)
!
! This privileged procedure writes 'EMPTY' at the start of the 1K block
! INDNO on FSYS.
!>
INTEGER  INDAD,J
RECORD (HF)NAME  H
RECORD (DISCDATAF)DATA
      J = IN2(21)
      -> OUT UNLESS  J = 0
!
      J = 93
      -> OUT UNLESS  DTRYING << 5 < 0
!
      J = 23
      -> OUT IF  AV(FSYS, 0)=0
!
      J = FBASE2(FSYS, ADDR(DATA))
      -> OUT UNLESS  J = 0
!
      J = 8
      -> OUT UNLESS  DATA_INDEX START <= INDNO >> 2 < DATA_FILE START
!
      J=NINDA(FSYS,INDNO,INDAD)
      -> OUT UNLESS  J = 0
!
      H==RECORD(INDAD)
      H_OWNER="EMPTY"
OUT:
      RESULT  = OUT(J, "II")
    END ; ! DEMPTYI
!
!-----------------------------------------------------------------------
!
!<DGETINDEXES
externalintegerfn  DGETINDEXES(integername  N, integer  ADR, FSYS)
!
! This procedure supplies a sorted list of index names accredited on
! FSYS.  The names are either listed (to DIRLOG) if ADR = -1 or written
! as a series of 18 byte strings to ADR onwards. N is set to the number
! of names returned. The array at ADR must be able to hold as many index
! names as there are on the disc. The current max is 1365,
! i.e. 19 * 1365 (=25935) bytes.
!>
INTEGER  PT,NNA,J, LN, I, L, W, FINDAD
STRING (255) USER
STRING (18)ARRAY  U(1:1365)
RECORD (NNF)ARRAYFORMAT  NNTF(0:16384)
RECORD (NNF)ARRAYNAME  NN
RECORD (FF)NAME  F
RECORD (DISCDATAF)DATA
      J = IN2(98)
      -> OUT UNLESS  J = 0
!
      J = 23
      -> OUT IF  AV(FSYS, 0) = 0
!
      J = FBASE2(FSYS, ADDR(DATA))
      -> OUT UNLESS  J = 0
!
      NNA = SYSAD(NNTKEY, FSYS)
      NN == ARRAY(NNA, NNTF)
      LN = 0
!
      CYCLE  PT = 0, 1, DATA_NNTTOP
         USER = NN(PT)_NAME
         IF  LENGTH(USER) = 6 START 
               IF  NN(PT)_TAG > 0 START ; ! a file index
                  J = NINDA(FSYS, NN(PT)_INDNO, FINDAD)
                  IF  J = 0 START 
                     F == RECORD(FINDAD)
                     USER = USER . ISEP . F_NAME
                  FINISH 
               FINISH 
               LN = LN + 1
               U(LN) = USER
         FINISH 
      REPEAT 
!
      N = LN
      IF  LN = 0 START 
         WRSN("No indexes on FSYS", FSYS) IF  ADR = -1
      FINISH  ELSE  START 
!
         IF  ADR # -1 START 
            J = 45
            -> OUT IF  VAL(ADR, 19 * LN, 1, D CALLERS PSR) = 0
            -> OUT IF  VAL(ADDR(N), 4, 1, D CALLERS PSR) = 0
         FINISH 
!
         SORT STRINGS(U, LN)
         IF  ADR = -1 START 
            WRSN("Indexes on FSYS", FSYS)
            PT = 0
            CYCLE  I = 1, 1, LN
               L = LENGTH(U(I))
               W = 1
               IF  L > 6 START 
                  W = 2
                  W = 3 IF  L > 13
               FINISH 
               PT = 0 AND  NEWLINE IF  PT + W > 9
               PRINTSTRING(U(I))
               SPACES(6*W + 1 - L)
               PT = PT + W
            REPEAT 
            NEWLINE
         FINISH  ELSE  START 
            MOVE(LN*19, ADDR(U(1)), ADR)
         FINISH 
      FINISH 
      J = 0
OUT:
      RESULT  = OUT(J, "")
END ; ! DGETINDEXES
!
!-----------------------------------------------------------------------
!
!<DGETINDEXES2
externalintegerfn  DGETINDEXES2(integername  N, integer  ADR, FSYS)
!
! This procedure supplies a list of the indexes accreditted on FSYS, 
! sorted into INDEX NO order.  A series of records of format
!
!            (string(18)NAME, byteinteger KB, integer INDNO)
!
! is returned to ADR onwards, a maximum of 24 * 1365 bytes.   N is set to
! the number of records supplied.
!>
INTEGER  PT, NNA, J, I, K, MM, FINDAD, NUSERS
STRING (255)USER
RECORD (NNF)ARRAYFORMAT  NNTF(0:16384)
RECORD (NNF)ARRAYNAME  NN
RECORDFORMAT  UF(STRING (18)NAME, BYTEINTEGER  KB, INTEGER  INDNO)
RECORD (UF)ARRAYNAME  UNN
RECORD (UF)ARRAYFORMAT  UNNF(0:1364)
RECORD (UF)NNW
RECORD (FF)NAME  F
RECORD (DISCDATAF)DATA
      J = IN2(99)
      -> OUT UNLESS  J = 0
!
      J = 23
      -> OUT IF  AV(FSYS, 0) = 0
!
      J = FBASE2(FSYS, ADDR(DATA))
      -> OUT UNLESS  J = 0
!
      NNA = SYSAD(NNTKEY, FSYS)
      NN == ARRAY(NNA, NNTF)
!
      NUSERS = 0
      CYCLE  PT = 0, 1, DATA_NNTTOP
         NUSERS = NUSERS + 1 IF  LENGTH(NN(PT)_NAME) = 6
      REPEAT 
      J = 45
      -> OUT IF  VAL(ADR, 24*NUSERS, 1, D CALLERS PSR) = 0
      -> OUT IF  VAL(ADDR(N), 4, 1, D CALLERS PSR) = 0
!
      N = 0
      UNN == ARRAY(ADR, UNNF)
      CYCLE  PT = 0, 1, DATA_NNTTOP
         USER = NN(PT)_NAME
         IF  LENGTH(USER) = 6 START 
            UNN(N)_KB = NN(PT)_KB
            UNN(N)_INDNO = NN(PT)_INDNO
            IF  NN(PT)_TAG > 0 START ; ! a file index
               J = NINDA(FSYS, NN(PT)_INDNO, FINDAD)
               IF  J = 0 START 
                  F == RECORD(FINDAD)
                  USER = USER . ISEP . F_NAME
               FINISH 
            FINISH 
            UNN(N)_NAME = USER
            N = N + 1
         FINISH 
      REPEAT 
!
      IF  N > 0 START 
         MM = 1
         MM = MM << 1 WHILE  MM <= N
         MM = MM - 1
!
         CYCLE 
            MM = MM >> 1
            EXIT  IF  MM = 0
            CYCLE  I = 1, 1, N-MM
               K = I
               WHILE  K > 0 CYCLE 
                  J = K + MM
!
                  EXIT  IF  UNN(K-1)_INDNO <= UNN(J-1)_INDNO
                  NNW = UNN(J-1)
                  UNN(J-1) = UNN(K-1)
                  UNN(K-1) = NNW
!
                  K = K - MM
               REPEAT 
            REPEAT 
         REPEAT 
      FINISH 
      J = 0
OUT:
      RESULT  = OUT(J, "")
END ; ! DGETINDEXES2
!
!-----------------------------------------------------------------------
!
!<DINDEX2
externalintegerfn  DINDEX2(string (18)NAME, integer  FSYS, ADR)
! 
!   This procedure returns the index NAME on fsys FSYS into ADR
!   onwards, with sensitive fields blanked off.
!>
INTEGER  J, TOP, INDAD, FINDAD, A
STRING (31)UNA, INA, INDEX
RECORD (FF)NAME  F
RECORD (HF)NAME  H, NH
CONSTSTRING (7)FN = "DINDEX "
      J = IN2(32)
      -> OUT UNLESS  J = 0
!
      J = 93
      -> OUT UNLESS  DTRYING << 5 < 0
!
      J = UIO(NAME, UNA, INA, INDEX)
      -> OUT UNLESS  J = 0
!
      IF  INA = "" START 
         J = HINDA(UNA, FSYS, INDAD, 0)
         -> OUT UNLESS  J = 0
         H == RECORD(INDAD)
         A = INDAD
         TOP = H_TOP
         FINDAD = INDAD + 512
         F == RECORD(FINDAD)
      FINISH  ELSE  START 
         J = FINDA(INDEX, FSYS, FINDAD, 0)
         -> OUT UNLESS  J = 0
         F == RECORD(FINDAD)
         A = FINDAD
         TOP = F_SIZE << 9
      FINISH 
!
      J = 45
      -> OUT UNLESS  VAL(ADR, TOP, 1, DCALLERSPSR) = YES
!
      J = PP(ADDR(F_SEMA), F_SEMANO, FN)
      -> OUT UNLESS  J = 0
!
      MOVE(TOP, INDAD, ADR)
!
      IF  INA = "" START 
         NH == RECORD(ADR)
         NH_DWSP = 0
         NH_BWSP = 0
         NH_TRYING = 0
      FINISH 
      VV(ADDR(F_SEMA), F_SEMANO)
OUT:
      RESULT  = OUT(J, "SIX")
END ; ! DINDEX2
!
!-----------------------------------------------------------------------
!
!<DNEWUSER
externalintegerfn  DNEWUSER(string (18)FILE INDEX, integer  FSYS, NKB)
!
! This privileged procedure creates either a user record+main file index
! (if FILE INDEX is simply a username)  or a file index (if FILE INDEX
! is supplied as username@fileindexname).  The index is created on disc
! pack FSYS with NKB Kbytes.  NKB must either be 2 or a multiple of 4
! between 4 and 32.
!>
INTEGER  NNAD, INDNO, N, HI, PAGE, NPD, NFD
INTEGER  INDAD, TESTAD, J, AMINUS1
STRING (18)UNA, INA, INDEX
RECORD (FF)NAME  F
RECORD (HF)NAME  H
RECORD (NNF)NAME  NN
RECORD (DISCDATAF)DATA
      J = IN2(50)
      -> OUT UNLESS  J = 0
!
      J = 93
      -> OUT UNLESS  DTRYING << 5 < 0
!
      J = UIO(FILE INDEX, UNA, INA, INDEX)
      -> OUT UNLESS  J = 0
!
      J = 12
      NPD = NKB >> 24
      NFD = (NKB >> 8 ) & 2047
      NKB = NKB & 255
      ! restrict to 2K or multiples of 4K
      -> OUT UNLESS  (4<=NKB<=32 AND  NKB&3=0) OR  NKB=2
!
      J = NEW NNT ENTRY(INDEX, FSYS, NNAD)
      -> OUT UNLESS  J = 0
!
      J = FBASE2(FSYS, ADDR(DATA))
      -> OUT UNLESS  J = 0
!
      AMINUS1 = 3; ! indexes are aligned on page boundaries, except 2K ones
      AMINUS1 = 1 IF  NKB = 2 
      INDNO = DATA_INDEX START << 2
!
      INDNO = INDNO + 8 IF  DATA_END > X'10000'; ! space for 4 page NNT on 640
!
      HI = DATA_FILE START << 2 - 1
      PAGE = 0; ! last page to be checked by BADPAGE
!
      WHILE  INDNO < HI CYCLE 
         J = INDNO >> 2
         UNLESS  PAGE = J START 
            PAGE = J
            UNLESS  BADPAGE(3, FSYS, DATA_START+PAGE) = 0 START ; ! skip over bad page
               INDNO = (PAGE+1) << 2
               -> NO GO
            FINISH 
         FINISH 
!
         J = NINDA(FSYS, INDNO, INDAD)
         -> OUT UNLESS  J = 0
!
         H == RECORD(INDAD)
         IF  H_OWNER = "EMPTY" OR  H_OWNER = "NEVER" START ; ! potential hole
            J = (INDNO+AMINUS1) & (¬AMINUS1); ! align
            J = (J+NKB)>>8<<8 IF  (J+NKB-1)>>8 > (J>>8); ! crosses segment
!
            IF  J > INDNO START ; ! can't start here
               H_OWNER = "EMPTY" IF  H_OWNER = "NEVER"; ! mustn't leave any NEVER holes
               INDNO = INDNO + 2
               -> NOGO
            FINISH 
!
            -> ENTER IF  NKB = 2; ! no need to check further
!
            CYCLE  N = INDNO+2, 2, INDNO+NKB-2; ! check availability of rest
               J = N >> 2
               UNLESS  PAGE = J START 
                  PAGE = J
                  UNLESS  BADPAGE(3, FSYS, DATA_START+PAGE) = 0 START 
                     INDNO = (PAGE+1) << 2
                     -> NO GO
                  FINISH 
               FINISH 
!
               J = NINDA(FSYS, N, TESTAD)
               -> OUT UNLESS  J = 0
!
               H == RECORD(TESTAD)
               UNLESS  H_OWNER = "EMPTY" OR  H_OWNER = "NEVER" START ; ! won't do
                  INDNO = N
                  -> CHECK
               FINISH 
!
               H_OWNER = "EMPTY" IF  H_OWNER = "NEVER"; ! just in case
            REPEAT 
            -> ENTER; ! successfully found a hole
         FINISH 
CHECK:
         J = 2
         IF  LENGTH(H_OWNER) = 6 START ; ! main index
            J = H_TOP >> 10
            J = 2 UNLESS  (4<=J<=32 AND  H_TOP & X'FFF' = 0)
         FINISH 
         INDNO = INDNO + J
NO GO:
      REPEAT ; ! INDNO <= HI
      J =7; ! NO SPACE FOR INDEX
      -> OUT
ENTER:
      NN == RECORD(NNAD)
      NN_NAME=UNA
      NN_KB=NKB
      NN_INDNO=INDNO
      NN_TAG=0
!
      IF  INA = "" C 
      THEN  J = SET USER RECORD(UNA, FSYS, NKB<<1, NPD, NFD, INDAD, INDNO) C 
      ELSE  START 
         NN_TAG = 1
         J = SET FILE INDEX(UNA, INA, FSYS, NKB<<1, NPD, NFD, INDAD, INDNO)
      FINISH 
OUT:
      RESULT  = OUT(J, "SII")
END ; ! DNEWUSER
!
!-----------------------------------------------------------------------
!
!<DPROCS
externalintegerfn  DPROCS(integername  MAXPROCS, integer  ADR)
!
! This procedure copies Supervisor's list of current processes to ADR
! onwards.  Each entry is 32 bytes long and the number of entries is
! returned in MAXPROCS.
!>
INTEGER  J, LEN
CONSTINTEGER  ENTRYLEN = 32
!MON M(10) = M(10) + 1
      J = IN2(68)
      -> OUT UNLESS  J = 0
!
      J = 45
      -> OUT IF  VAL(ADDR(MAXPROCS), 4, 1, DCALLERS PSR) = 0
      MAXPROCS = COM_MAXPROCS
!
      LEN = MAXPROCS * ENTRYLEN
      -> OUT IF  VAL(ADR, LEN, 1, DCALLERS PSR) = 0
!
      MOVE(LEN, COM_PROCAAD, ADR)
      J = 0
OUT:
      RESULT  = OUT(J, "")
END ; ! DPROCS
!
!-----------------------------------------------------------------------
!
!<DRENAMEINDEX
externalintegerfn  DRENAME INDEX(string (18)OLDNAME, NEWNAME, 
      integer  FSYS)
!
! This privileged procedure renames index OLDNAME to NEWNAME.  Both
! OLDNAME and NEWNAME must either be of the form:
!     username  or  username:indexname
! ie not one of each.  Note that only the specified index is renamed.
! If the user has other fileindexes, these are not renamed.
!>
INTEGER  J,INDAD, AINDAD
STRING (31)OLDU, OLDI, OLDINDEX
STRING (31)NEWU, NEWI, NEWINDEX
RECORD (HF)NAME  H
INTEGER  NNAD, NEWNNAD
RECORD (NNF)NAME  NN, NEWNN
RECORD (FF)NAME  F, AF
      J = IN2(71)
      -> OUT UNLESS  J = 0
!
      J = 93
      -> OUT UNLESS  DTRYING << 5 < 0
!
      J = UIO(OLDNAME, OLDU, OLDI, OLDINDEX)
      -> OUT UNLESS  J = 0
!
      J = UIO(NEWNAME, NEWU, NEWI, NEWINDEX)
      -> OUT UNLESS  J = 0
!
      J = 8
      -> OUT IF  OLDI = "" # NEWI OR  OLDI # "" = NEWI
!
      J = FIND NNT ENTRY(OLDINDEX, FSYS, NNAD, 0)
      -> OUT UNLESS  J = 0; ! OLDNAME DOES NOT EXIST
!
      NN == RECORD(NNAD)
!
      J = NINDA(FSYS, NN_INDNO, INDAD)
      -> OUT UNLESS  J = 0
!
      J = NEW NNT ENTRY(NEWNAME, FSYS, NEWNNAD)
      -> OUT UNLESS  J = 0
!
      NEWNN == RECORD(NEWNNAD)
      NEWNN = NN
      NEWNN_NAME = NEWU
      NN_NAME = ".NULL"
!
      IF  OLDI = "" START ; ! process index + main file index
         H == RECORD(INDAD)
         H_OWNER = NEWU
         INDAD = INDAD + 512
      FINISH 
!
      F == RECORD(INDAD)
      F_OWNER = NEWU; ! file index
      F_NAME = NEWI
      J = NEWAINDA(NEWINDEX, FSYS, AINDAD)
      IF  J = 0 START 
         AF == RECORD(AINDAD)
         AF_OWNER = NEWU
         J = NEWAINDA("", 0, J)
      FINISH 
OUT:
      EMPTY DVM
      RESULT  = OUT(J, "SSI")
END ; ! DRENAME INDEX
!
!-----------------------------------------------------------------------
!
!<DREPLACEINDEX
externalintegerfn  DREPLACE INDEX(integer  FSYS, INDNO, ADR)
!
! Allows 1024 bytes in the index area of a disc to be overwritten by a
! privileged process.
!>
INTEGER  J, INDAD
      J = IN2(73)
      -> OUT UNLESS  J = 0
!
      J = 93
      -> OUT UNLESS  DTRYING << 7 < 0
!
      J = 45
      -> OUT UNLESS  VAL(ADR, 1024, 0, 0) = YES
!
      J = NINDA(FSYS, INDNO, INDAD)
      -> OUT UNLESS  J = 0
!
      MOVE(1024, ADR, INDAD)
OUT:
      RESULT  = OUT(J, "II")
END ; ! DREPLACE INDEX
!
!-----------------------------------------------------------------------
!
!<DSYSAD
externalintegerfn  DSYSAD(integer  TYPE, ADR, FSYS)
!
! This privileged procedure returns
!        the bitmap (TYPE=0)
!        the name-number table (TYPE=1)
!        the DIRCOM record (TYPE=5)
!        or the bad pages bitmap (TYPE=6)
! in ADR onwards for FSYS.
!>
INTEGER  J, L, SEMA, SEMANO
RECORD (DISCDATAF)DATA
      J = IN2(81)
      -> RES UNLESS  J = 0
!
      J = 93
      -> RES UNLESS  DTRYING << 11 < 0
!
      J = 23
      -> RES IF  AV(FSYS, 1) = 0
!
      J = FBASE2(FSYS, ADDR(DATA))
      -> RES UNLESS  J = 0
!
      J = 8
      L = 0
      L = DATA_BITSIZE IF  TYPE = 0 OR  TYPE = 6
      L = DATA_NNTSIZE IF  TYPE = 1
      L = DIRCOMSIZE IF  TYPE = 5
      -> RES UNLESS  L > 0
!
      J = 45
      -> RES UNLESS  VAL(ADR, L, 1, DCALLERSPSR) = YES
!
      SEMA = SYSAD(BITKEY, FSYS)
      SEMANO = FSYS << 16
      J = PP(SEMA, SEMANO,"DSYSAD")
      -> RES UNLESS  J = 0
      MOVE(L, SYSAD(TYPE, FSYS), ADR)
      VV(SEMA, SEMANO)
RES:
      RESULT  = OUT(J, "IXI")
END ; ! DSYSAD
!
!-----------------------------------------------------------------------
!
!<DUSERINDEXES
externalintegerfn  DUSERINDEXES(string (6)USER, integer  FSYS, ADR,
      integername  N)
!
! Searches FSYS (or all fsys's if FSYS is -1) for indexes belonging to
! USER. Returns N records to ADR onwards of the form:
!
!        %string(11)index, %integer fsys
!>
!
!
INTEGER  NDISCS, J, I, NNA, PT, STOP, FINDAD
STRING (11)INAME
INTEGERARRAY  FS(0:99)
RECORD (NNF)ARRAYFORMAT  NNTF(0:16384)
RECORD (NNF)ARRAYNAME  NNT
RECORD (NNF)NAME  NN
RECORD (FF)NAME  F
RECORD (DISCDATAF)DATA
      J = IN2(100)
      -> OUT UNLESS  J = 0
!
      J = UNOK(USER)
      -> OUT UNLESS  J = 0
!
      J = 45
      -> OUT IF  VAL(ADDR(N), 4, 1, D CALLERS PSR) = 0
      N = 0
!
      IF  FSYS = -1 START 
         GET AV FSYS2(0, NDISCS, FS)
         NDISCS = NDISCS - 1
      FINISH  ELSE  START 
         J = 23 AND  -> OUT IF  AV(FSYS, 0) = 0
         FS(0) = FSYS
         NDISCS = 0
      FINISH 
!
      FOR  I = 0, 1, NDISCS CYCLE 
         FSYS = FS(I)
         NNA = SYSAD(NNTKEY, FSYS)
         NNT == ARRAY(NNA, NNTF)
         J = FBASE2(FSYS, ADDR(DATA))
         PT = HASH(USER, DATA_NNTHASH)
         STOP = PT
         UNTIL  PT = STOP CYCLE 
            NN == NNT(PT)
            IF  NN_NAME = USER START 
               INAME = ""
               IF  NN_TAG > 0 START 
                  J = NINDA(FSYS, NN_INDNO, FINDAD)
                  -> OUT UNLESS  J = 0
                  F == RECORD(FINDAD)
                  INAME = F_NAME
               FINISH 
!
               J = 45
               -> OUT IF  VAL(ADR, 16, 1, D CALLERS PSR) = 0
               STRING(ADR) = INAME
               INTEGER(ADR+12) = FSYS
               ADR = ADR + 16
               N = N + 1
            FINISH  {%ELSE %IF NN_NAME = "" %THEN %EXIT}
            PT = PT + 1
            PT = 0 IF  PT > DATA_NNTTOP
         REPEAT 
      REPEAT 
      J = 0
OUT:
      RESULT  = OUT(J, "SI")
END ; ! DUSERINDEXES
!
!-----------------------------------------------------------------------
!
!<GETUSNAMES
externalintegerfn  GET USNAMES(integername  N, integer  ADR, FSYS)
!
! This procedure supplies a sorted list of users who have user records
! on disc pack FSYS. The names are either listed (to DIRLOG) if ADR = -1
! or written as a series of 6 byte strings to ADR onwards. N is set to
! the number of names returned. The array at ADR must be able to hold 1365
! names, i.e. 9555 bytes.
!>
RECORD (NNF)ARRAYFORMAT  NNTF(0:16384)
RECORD (NNF)ARRAYNAME  NN
INTEGER  PT,NNA,J, LN, I
STRING (255) USER
STRING (6)ARRAY  U(1:1365)
RECORD (DISCDATAF)DATA
      J = IN2(89)
      -> OUT UNLESS  J = 0
!
      J = 23
      -> OUT IF  AV(FSYS, 0) = 0
!
      J = FBASE2(FSYS, ADDR(DATA))
      -> OUT UNLESS  J = 0
!
      NNA = SYSAD(NNTKEY, FSYS)
      NN == ARRAY(NNA, NNTF)
      LN = 0
!
      CYCLE  PT = 0, 1, DATA_NNTTOP
         USER=NN(PT)_NAME
         IF  LENGTH(USER)=6 AND  NN(PT)_TAG = 0 START ; ! a process index
            LN=LN+1
            U(LN) = USER
         FINISH 
      REPEAT 
!
      N = LN
      IF  LN = 0 START 
         IF  ADR = -1 START 
            WRSN("No users on FSYS", FSYS)
         FINISH 
      FINISH  ELSE  START 
         IF  ADR # -1 START 
            J = 45
            -> OUT IF  VAL(ADR, LN*7, 1, D CALLERS PSR) = 0
            -> OUT IF  VAL(ADDR(N), 4, 1, D CALLERS PSR) = 0
         FINISH 
!
         SORT STRINGS(U, LN)
         IF  ADR = -1 START 
            WRSN("Users on FSYS", FSYS)
            PT = 0
            CYCLE  I = 1, 1, LN
               PRINTSTRING(U(I))
               SPACES(2)
               PT = PT + 1
               IF  PT > 9 THEN  PT = 0 AND  NEWLINE
            REPEAT 
            NEWLINE
         FINISH  ELSE  START 
            MOVE(LN*7, ADDR(U(1)), ADR)
         FINISH 
      FINISH 
      J = 0
OUT:
      RESULT  = OUT(J, "")
END ; ! GET USNAMES
!
!-----------------------------------------------------------------------
!
!<GETUSNAMES2
externalintegerfn  GET USNAMES2(record (NNF)arrayname  UNN,
      integername  N, integer  FSYS)
!
! This procedure supplies the list of users who have user records on
! disc-pack FSYS, sorted into INDEX NO order.  A series of records of
! format
!
!            (string(6)NAME, byteinteger KB, integer INDNO)
!
! is returned in the array UNN, which should be declared (0:1364).  N is
! set to the number of names supplied.
!>
RECORD (NNF)ARRAYFORMAT  NNTF(0:16384)
RECORD (NNF)ARRAYNAME  NN
RECORD  (NNF)NNW
INTEGER  PT,NNA, J, I, K, MM, NUSERS
STRING (255) USER
RECORD (DISCDATAF)DATA
      J = IN2(90)
      -> OUT UNLESS  J = 0
!
      J = 23
      -> OUT IF  AV(FSYS, 0) = 0
!
      J = FBASE2(FSYS, ADDR(DATA))
      -> OUT UNLESS  J = 0
!
      NNA = SYSAD(NNTKEY, FSYS)
      NN == ARRAY(NNA, NNTF)
!
      NUSERS = 0
      CYCLE  PT = 0, 1, DATA_NNTTOP
         NUSERS = NUSERS + 1 IF  LENGTH(NN(PT)_NAME) = 6 AND  NN(PT)_TAG = 0
      REPEAT 
!
      J = 45
      K = 12 * NUSERS + 1
      K = K ! X'18000000'
      *LDA_UNN+4
      *LDTB_K
      *VAL_(LNB +1)
      *JCC_3,<OUT>; ! jump if no read access to descriptor
      *LD_UNN+8
      *VAL_(LNB +1)
      *JCC_3,<OUT>
      -> OUT IF  VAL(ADDR(UNN(0)), 12 * NUSERS, 1, D CALLERS PSR) = 0
      -> OUT IF  VAL(ADDR(N), 4, 1, D CALLERS PSR) = 0
!
      N = 0
      CYCLE  PT = 0, 1, DATA_NNTTOP
         USER=NN(PT)_NAME
         IF  LENGTH(USER)=6 AND  NN(PT)_TAG = 0 START ; ! a process index
            UNN(N)=NN(PT)
            N=N+1
         FINISH 
      REPEAT 
!
      IF  N > 0 START 
         MM = 1
         MM = MM << 1 WHILE  MM <= N
         MM = MM - 1
!
         CYCLE 
            MM = MM >> 1
            EXIT  IF  MM = 0
            CYCLE  I = 1, 1, N-MM
               K = I
               WHILE  K > 0 CYCLE 
                  J = K + MM
!
                  EXIT  IF  UNN(K-1)_INDNO <= UNN(J-1)_INDNO
                  NNW = UNN(J-1)
                  UNN(J-1) = UNN(K-1)
                  UNN(K-1) = NNW
!
                  K = K - MM
               REPEAT 
            REPEAT 
         REPEAT 
      FINISH 
      J = 0
OUT:
      RESULT  = OUT(J, "")
END ; ! GET USNAMES2
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  SETDIRMON(STRING (255)S)
      IF  S = "" OR  S = "0" THEN  DIRMON = 0 ELSE  DIRMON = 1
END ; ! SETDIRMON
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  PLACE(STRING (39)TEXT,INTEGER  SCREEN,LINE,COL,ACTION)
RECORD  (POPERF)P
      WHILE  LENGTH(TEXT)>20 CYCLE 
         PLACE(FROMSTRING(TEXT,1,20),SCREEN,LINE,COL,ACTION)
         TEXT=FROMSTRING(TEXT,21,LENGTH(TEXT))
         COL=COL+20
         ACTION=NULL IF  ACTION#NULL
      REPEAT 
      P=0
      P_DEST=X'320006'
      P_LINE=SCREEN*LINES PER PAGE + LINE
      P_POS=COL
      P_ZERO=ACTION
      P_TEXT=TEXT
      DPONI(P)
END ; ! PLACE
!
!-----------------------------------------------------------------------
!
ROUTINE  WRITE TO COL(INTEGER  I,LINE,COL)
STRING (31) S
      S=ITOS(I)
      PLACE(S,SCREEN2,LINE,COL-LENGTH(S),NULL)
END ; ! WRITE TO COL
!
!-----------------------------------------------------------------------
!
!
!
EXTERNALROUTINE  INIT DISPLAY
INTEGER  J
STRING (31) S
      J=COUNT PROCS IN("      ",CURIUSERS)
      PLACE("Interactive Use  -  Status",SCREEN2,0,6,CLEAR)
      WRITE TO COL(MAXIUSERS,5,31)
      WRITE TO COL(CURIUSERS,5,38)
      LINENO=9
      PLACE("Current Current",SCREEN2,2,25,NULL)
      PLACE("limit   number",SCREEN2,3,25,NULL)
      PLACE("Interactive users:",SCREEN2,5,0,NULL)
      S=TIME
      LENGTH(S)=5
      PLACE("Screen written at ".S."hrs",SCREEN2,  C 
        LINES PER PAGE - 1,13,NULL)
END ; ! INIT DISPLAY
!
!-----------------------------------------------------------------------
!
INTEGERFN  HASQS(STRING (6) U)
INTEGER  J
      CYCLE  J=1,1,6
         IF  CHARNO(U,J)='?' THEN  RESULT =1
         REPEAT 
      RESULT =0
END ; ! HASQS
!
!-----------------------------------------------------------------------
!
INTEGERFN  EQUSER(STRING (6) USER,PASSU)
! RESULT 1 IF USER IS IN THE CLASS "PASSU", ELSE RESULT 0
INTEGER  J,CHP,CHU
      RESULT =0 UNLESS  LENGTH(USER)=6=LENGTH(PASSU); ! does not belong
      CYCLE  J=1,1,6
         CHU=CHARNO(USER,J)
         CHP=CHARNO(PASSU,J)
         UNLESS  CHU=CHP OR  CHP='?' THEN  RESULT =0; ! does not belong
      REPEAT 
      RESULT =1
END ;                            ! EQUSER
!
!-----------------------------------------------------------------------
!
INTEGERFN  AMBIGUOUS(STRING (6) USER,PASSU)
! result is  1  if there exists a 6-char name which belongs to the group
!               USER and to the group PASSU, otherwise result 0.
INTEGER  J,CHP,CHU
      RESULT =1 UNLESS  LENGTH(USER)=6=LENGTH(PASSU); ! not unambiguous
      CYCLE  J=1,1,6
         CHU=CHARNO(USER,J)
         CHP=CHARNO(PASSU,J)
         IF  CHU#CHP AND  CHU#'?' AND  CHP#'?' THEN  RESULT =0; ! unambiguous
      REPEAT 
      RESULT =1; ! ambiguous
END ; ! AMBIGUOUS
!
!-----------------------------------------------------------------------
!
ROUTINE  UGRETLIST(INTEGER  PT)
INTEGER  K
      RETURN   IF  PT=ENDL
      K=ASL
      ASL=PT
      PT=UG(PT)_LINK WHILE  UG(PT)_LINK#ENDL
      UG(PT)_LINK=K
END ; ! UGRETLIST
!
!-----------------------------------------------------------------------
!
INTEGERFN  ONLIST(STRING (6) USER,INTEGERNAME  HEAD,INTEGER  ACT,MAX)
! FOR ADDTOMAINLIST WE ADD TO THE END OF THE LIST. FOR
! ADDTOSUBLIST, WE GO DOWN TO THE FIRST ENTRY TO WHICH THE USER IS NON-EQUIVALENT,
! AND ADD IN FRONT OF THAT.
INTEGER  CUR,J,K,INSET
INTEGERNAME  PREVLINK
RECORD (UGF)NAME  R
STRING (6) WU
      CUR=HEAD
      PREVLINK==HEAD
      WHILE  CUR#ENDL CYCLE 
         R==UG(CUR)
         IF  ACT&DISPLAY#0 START 
               IF  ACT&OLDSCREEN=0 THEN  ACT=ACT!OLDSCREEN
               INSET=5
               IF  ACT&SIDECHAIN#0 THEN  INSET=10
               PLACE(R_U,SCREEN2,LINENO,INSET,NULL)
               WRITE TO COL(R_MAX,LINENO,31)
               WRITE TO COL(R_N,LINENO,38)
               LINENO=LINENO+1
            J=ONLIST(USER,R_SUBLINK,DISPLAY!SIDECHAIN,0)
            FINISH 
         IF  ACT&PROCRESET#0 START 
      R_N = COUNT PROCS IN(R_U, J)
            IF  USER=R_U THEN  MAX=R_MAX ELSE  START 
               IF  EQUSER(R_U,USER)#0 AND  R_MAX>MAX THEN  R_MAX=MAX
            FINISH 
            J=ONLIST(USER,R_SUBLINK,ACT!SIDECHAIN,MAX)
            FINISH 
         IF  EQUSER(USER,R_U)#0 START ; ! 
            !
            ! Found equivalent or identical
            IF  ACT&(CHECKN!RESETN!INCRE)#0 START 
               IF  ACT&INCRE#0 THEN  R_N=R_N+MAX
               IF  ACT&RESETN#0 THEN  R_N=MAX
               IF  ACT&CHECKN#0 START 
                  IF  R_N>=R_MAX THEN  RESULT =GROUPFULLFLAG
                  FINISH 
               RESULT =ONLIST(USER,R_SUBLINK,ACT!SIDECHAIN,MAX)
               FINISH ; ! CHECKN PROCRESET INCRE
            !
            IF  USER#R_U START 
               !
               ! Found equivalent, but not identical
               IF  ACT&(ADDTO!SIDECHAIN)=ADDTO!SIDECHAIN START 
                  IF  MAX>R_MAX THEN  MAX=R_MAX
                  FINISH 
               IF  ACT&PROCRESET=0 THEN   C 
                  RESULT =ONLIST(USER,R_SUBLINK,ACT!SIDECHAIN,MAX)
            !
            FINISH  ELSE  START 
               !
               ! Then found identical
               IF  ACT&ADDTO#0 THEN  R_MAX=MAX AND  RESULT =0
               IF  ACT&REMOVE#0 START 
                  UGRETLIST(R_SUBLINK)
                  R_SUBLINK=ENDL
                  J=PREVLINK
                  PREVLINK=R_LINK
                  R_LINK=ENDL
                  UGRETLIST(J)
                  RESULT =0
                  FINISH ; ! ACT REMOVE
            FINISH ; ! FOUND IDENT
         FINISH  ELSE  START ; ! FINISH FOUND EQUIV
            ! Neither identical nor equivalent
            ! PREVLINK IS MAPPED ONTO LINK POINTING TO CURRENT
            ! CELL. GET NEW CELL AND PUT IN FRONT OF CURRENT CELL
            IF  ACT&SIDECHAIN=0 START 
               IF  EQUSER(R_U,USER)#0 AND  ACT&ADDTO#0 START 
                  PREVLINK==R_SUBLINK
                  WU=R_U
                  R_U=USER
                  USER=WU
                  J=R_MAX
                  R_MAX=MAX
                  MAX=J
                  EXIT 
                  FINISH 
               IF  AMBIGUOUS(USER,R_U)#0 THEN  RESULT =AMBIFLAG
               FINISH 
            IF  HASQS(R_U)#0 AND   C 
               ACT&(ADDTO!SIDECHAIN)=ADDTO!SIDECHAIN THEN  EXIT 
         FINISH ; ! Neither identical nor equivalent
         PREVLINK==UG(CUR)_LINK
         CUR=UG(CUR)_LINK
         REPEAT 
      IF  ACT&ADDTO#0 START 
         IF  ASL=ENDL THEN  RESULT =NOFREEFLAG
         K=PREVLINK
         J=ASL
         PREVLINK=J
         ASL=UG(ASL)_LINK; ! TAKE OFF FREELIST
         UG(J)_LINK=K
         UG(J)_U=USER
         UG(J)_MAX=MAX
         UG(J)_N=0
         RESULT =ADDEDFLAG; ! OK
         FINISH 
      RESULT =NOTINLISTFLAG
      END ; ! ONLIST
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  LISTMOD(STRING (6) USERGROUP,INTEGER  N1,N2)
! There are 4 calls on this all from XOP
!     process start                     LM(user, 0, 1)
!     process stops                     LM(user, 0, -1)
!     D/USERS                           LM(S1, N1, N2)
!     checkstart                        LM(user, 0, 0)
!
!
!  ADDTO 2  add or reset MAXusers for usergroup to value N
! REMOVE 32   remove USERGROUP from list
! CHECKN 128   check whether USERgroup may log on
! INCRE  4   add N to counts for usergroups including USERGROUP
! PROCRESET  1  reset N-values for all usergroups from process-list
! DISPLAY  64  display the lists on the screen.
INTEGER  N,ACT,J,K
!MON M(22) = M(22) + 1
      N=0
      IF  ASL=-2 START 
         ! FREELIST NOT INITIALISED
         CYCLE  J=0,1,TOPUG
            UG(J)_LINK=J+1
            UG(J)_SUBLINK=ENDL
         REPEAT 
         UG(TOPUG)_LINK=ENDL
         ASL=0
         MAXIUSERS=COM_MAXPROCS - NEXECPROCS - 1
      FINISH 
      IF  LENGTH(USERGROUP)=6 START 
         IF  N1=0 START ; ! NOT A D/USERS
            IF  N2=0 START 
               IF  CURIUSERS>=MAXIUSERS THEN  RESULT =SYSFULLFLAG
               ACT=CHECKN
           FINISH  ELSE  ACT=INCRE AND  N=N2 AND  CURIUSERS=CURIUSERS+N
         FINISH  ELSE  START 
            ! THEN D/USERS <USERGROUP> PAR
            IF  N2<0 THEN  ACT=REMOVE ELSE  START 
               ACT=ADDTO
               N=N2
               IF  N>MAXIUSERS THEN  N=MAXIUSERS
            FINISH 
         FINISH 
      FINISH  ELSE  START 
         IF  N1>=0 START 
            MAXIUSERS=N1
            MAXIUSERS=COM_MAXPROCS-NEXECPROCS - 1 C 
            IF  MAXIUSERS>COM_MAXPROCS-NEXECPROCS - 1
            RESULT =0
         FINISH 
         ACT=DISPLAY
         INIT DISPLAY
         USERGROUP="      "
      FINISH 
      J=ONLIST(USERGROUP,HEAD,ACT,N)
      ! If we have just successfully added a new item to the list,
      ! then reset all counts from process list.
      IF  ACT#CHECKN AND  ACT#INCRE AND  ACT#REMOVE  C 
         AND  ACT#DISPLAY C 
         THEN  K=ONLIST(USERGROUP,HEAD,PROCRESET,N)
      IF  ACT=DISPLAY C 
      THEN  PLACE("Sub groups:", SCREEN2, 7, 0, SCREEN SWITCH)
      IF  ACT=CHECKN AND  SYSFULLFLAG#J#GROUPFULLFLAG THEN  J=0
      RESULT =J
END ; ! LISTMOD
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  NEWPAGE CHAR(RECORD (PARMF)NAME  P)
OWNINTEGER  STATE=-1
SWITCH  NP(0:2)
OWNINTEGER  CDEX,IMNEM
!
RECORDFORMAT  RCBF(INTEGER  LIMFLAGS,LSTBA,LB BYTES,LBA,AL BYTES, C 
      ALA,INITWORD,SLOTNO)
RECORD (RCBF)NAME  RCB
!
CONSTSTRING (19)ARRAY  ALLMS(0:2)=C 
"SUCCESSFUL", "BAD PARAM(?)", "ALREADY ALLOCATED"
!
CONSTINTEGER  PROCESS1=1
!
RECORDFORMAT  ALEF(INTEGER  BYTES,ADDR)
INTEGERNAME  INIT0 LB,LOAD REP LB,INIT LB,WRITE CONTROL LB,NEWPAGE LB
INTEGERNAME  READ PROPS LB
RECORD (ALEF)NAME  AL0,AL2,AL4
INTEGER  FAD,REP ADDR,SNO,DEV ENT AD,INIT ADDR
INTEGER  PROP DAT ADDR,RESP0,RESP1, J
!
RECORDFORMAT  PROPF(BYTEINTEGER  SIX,DEVNO,SPEED REP,FORM STYLE,  C 
   FINAL LINE, OPTION CART)
RECORD (PROPF)NAME  PROPS
!MON M(23) = M(23) + 1
!
      IF  STATE<0 START 
         !Reject everything until init call from routine AUTO CLOSE
         IF  P_DEST#0 THEN  RESULT =0
         STATE=0
         FINISH 
      -> NP(STATE)
NP(0):
! ALLOCATE THE DEVICE
      IMNEM=M'LP0'
      P=0
      P_DEST=X'30000B'; ! GPC ALLOCATE
      P_SRCE=36; ! IN "PROCESS1"
      P_P1=IMNEM
      P_P2=((COM_SYNC1 DEST + PROCESS1)<<16) ! 36; ! to DACT 36 in rt PROCESS1
      DPONI(P)
      STATE=1
      RESULT =0
NP(1):
      IF  P_P1 # 0 START 
         J = P_P1
         J = 1 UNLESS  J = 2
         WRS3N("ALLOC REPLY", ALLMS(J), "", P_P1)
         RESULT  = 1
      FINISH 
      SNO=P_P2
      DEV ENT AD=P_P3
!
!NOW GET A PAGE
      P=0
      P_DEST=X'50000'; ! GET EPAGE DEST
      DOUTI(P)
      CDEX=P_P2
      FAD=P_P4
      REPADDR=FAD+128
!
! If the device has been powered off, initialisation data is lost, so we need
! to re-initialise. Setting "no auto-throw" is not enough to eliminate
! auto-throw - you have to do a write-control to set "lines-per-page"
! as well. EXTRAORDINARY !!
!
! Layout of the (public) page
!     OFFSET(BYTES)                     LENGTH(BYTES)
!      0          RCB                   32
!     52          INIT0 LB              4
!     56          READ PROP DATA LB     4
!     60          NEWPAGE LB            4
!     64          LOAD REP LB           4
!     68          INIT LB               4
!     72          WRITE-CONTROL LB      4
!     76          AL0-1                 8
!     84          AL2-3                 8
!     92          AL4-5                 8
!    100          INIT DATA             4
!    104          PROPERTIES DATA       8
!    128          LP                    384
!
! INITIALISE RCB ETC.
      INIT0 LB==INTEGER(FAD+52)
      READ PROPS LB==INTEGER(FAD+56)
      NEWPAGE LB==INTEGER(FAD+60)
      LOAD REP LB==INTEGER(FAD+64)
      INIT LB==INTEGER(FAD+68)
      WRITE CONTROL LB==INTEGER(FAD+72)
      AL0==RECORD(FAD+76)
      AL2==RECORD(FAD+84)
      AL4==RECORD(FAD+92)
!
      INIT ADDR=FAD+100
      PROP DAT ADDR=FAD+104
      PROPS==RECORD(PROP DAT ADDR)
!
      RCB==RECORD(FAD+0)
      RCB=0
      RCB_LIMFLAGS=X'00004000'; ! trusted RCB - to do the initialise
      RCB_LB BYTES=4
      RCB_LBA=ADDR(INIT0 LB)
      RCB_AL BYTES=24
      RCB_ALA=ADDR(AL0)
!
      INIT0 LB=     X'80F00002'
      READ PROPS LB=X'00F00E04'; ! short-block, long block, X & Y conditions suppressed
      NEWPAGE LB=   X'82F0030C'; ! write literal data X'C'=form feed
      LOAD REP LB=  X'80F02500'; ! Load repertoire, command chain
      INIT LB=      X'80F00102'; ! initialise
!
      AL0_BYTES=384
      AL0_ADDR=REPADDR
      AL2_BYTES=4
      AL2_ADDR=INIT ADDR
      AL4_BYTES=8
      AL4_ADDR=PROP DAT ADDR
!
      INTEGER(INIT ADDR)=0; ! suppress all secondary bits from setting primary
!
!---------------- Fire NEWPAGE command -------------------
      RCB_LB BYTES=4
      RCB_LBA=ADDR(NEWPAGE LB)
      P=0
      P_DEST=X'30000C'; ! GPC EXECUTE
      P_SRCE=36
      P_P1=ADDR(RCB)
      P_P2=SNO
      P_P3=1<<4 ! 3;                    ! PAWFN<<4 ! SAWFLAGS
      DPONI(P)
      STATE=2
      RESULT =0
NP(2):
      RESP0=P_P1
      RESP1=P_P2
      WRSNT("RESP0=", RESP0, 2)
      IF  (RESP0>>16)&255=X'10' THEN  RESULT =0; ! Attention response
      STATE=-1
!
! Now return page
      P=0
      P_DEST=X'60000'; ! RETURN EPAGE
      P_P2=CDEX
      DPONI(P)
! De=-allocate
      P=0
      P_DEST=X'300005'; ! GPC DE ALLOCATE
      P_P1=IMNEM
      DOUTI(P)
      IF  P_P1 # 0 START 
         WRSN("De-allocate reply =", P_P1)
      FINISH 
      RESULT =1
END ; ! NEWPAGE CHAR
!
!-----------------------------------------------------------------------
!
routine  SEND(string (255)TEXT, STRING (4)TYPE)
record  (PARMF)P
      return  if  LENGTH(TEXT) > 2 and  FROMSTRING(TEXT, 1, 3) = "IPL"
!
      P = 0
      P_DEST = X'32000E'; ! GOES TO 'PARSE COM' VIA OPER
      P_SRCE = 26; ! DISCARD REPLIES
      LENGTH(TEXT) = 23 if  LENGTH(TEXT) > 23
      STRING(ADDR(P_P1)) = TEXT
      DPONI(P)
!
      OPER(0, TYPE . " command:")
      OPER(0, TEXT)
      PRINTSTRING(TYPE)
      WRSS(": ", TEXT)
end ; ! SEND
!
!-----------------------------------------------------------------------
!
INTEGERFN  OBEYFILE(STRING (31)FULL)
! result = 0 for success
! All lines in file, except blank ones, must be at least 3 characs long
! and have '/' as the second ch. This stops garbage files filling up
! the param table. NOTE that Supervisor commands can start '0/'.
INTEGER  SEG, GAP, FAD, J, K, DATASTART, DATAEND, RES
STRING (255)S, A, B
RECORD (FHDRF)NAME  H
      SEG = 0
      GAP = 0
      J = DCONNECTI(FULL, -1, 1, 0, SEG, GAP)
      RESULT  = J UNLESS  J = 0
!
      FAD = SEG << 18
      H == RECORD(FAD)
      RES = 89; ! invalid file
      -> OUT UNLESS  0 < H_TXTRELST < H_NEXTFREEBYTE ANDC 
              H_NEXTFREEBYTE <= H_MAXBYTES ANDC 
              H_THREE = 3
!
      DATA START = FAD + H_TXTRELST
      DATA END   = FAD + H_NEXTFREEBYTE
      J = DATA START
      CYCLE 
         K = J
         EXIT  IF  J >= DATA END
         WHILE  BYTEINTEGER(J) # NL CYCLE 
            EXIT  IF  J >= DATA END
            J = J + 1
         REPEAT 
!
         S = ""
         WHILE  K < J CYCLE 
            S = S . TOSTRING(BYTEINTEGER(K)) IF  LENGTH(S) < 255
            K = K + 1
         REPEAT 
!
         J = J + 1
         S = A . " " . B WHILE  S -> A . ("  ") . B
         S = JUST(S)
         CONTINUE  IF  S = ""
         -> OUT IF  LENGTH(S) < 3 OR  CHARNO(S, 2) # '/'
         SEND(S, "Obey")
      REPEAT 
      RES = 0
OUT:
      J = DDISCONNECTI(FULL, -1, 0)
      RESULT  = RES
END ; ! OBEYFILE
!
!-----------------------------------------------------------------------
!
integerfn  VALID LINE(stringname  LINE,integer  LOBYTE,HIBYTE)
! Result=0  not an OK line
!        1  valid: ie line looks like  'ddddddd hh.mm command'
integer  J,CH,MINS
string (255) A,B,S
constintegerarray  FAC(1:5)=600,60,0,10,1
constintegerarray  HHMM(1:5)='2','9',0,'5','9'
      if  LOBYTE>HIBYTE start 
         J=LOBYTE
         LOBYTE=HIBYTE
         HIBYTE=J
      finish 
!
      S=""
      J=LOBYTE
      while  J<=HIBYTE cycle 
         S=S.TOSTRING(BYTEINTEGER(J)) if  LENGTH(S)<255
         J=J+1
      repeat 
!
      ! Remove NLs and multiple spaces
      S = A . " " . B while  S->A.("  ").B
      S = A . B while  S->A.(TOSTRING(NL)).B
      ! Remove leading spaces
      S = JUST(S)
      if  LENGTH(S)<=14 then  result =0
      if  CHARNO(S,11)#'.' then  result =0
      MINS=0
      cycle  J=1,1,7
         CH=CHARNO(S,J)
         unless  'A'<=CH<='Z' or  CH='-' then  result =0
      repeat 
      cycle  J=1,1,5
         if  J#3 start 
            CH=CHARNO(S,J+8)
            result  = 0 unless  '0' <= CH <= HHMM(J)
            MINS=MINS + FAC(J)*(CH-'0')
         finish 
      repeat 
      if  MINS>=24*60 then  result =0
      LINE=S
      result =1
      end ; ! VALID LINE
!
!-----------------------------------------------------------------------
!
integerfn  NEXTLINE(stringname  LINEDAYS,LINETIME,integername  c 
      LINEPTR,stringname  TEXT,integer  DIRECTION,LIMIT)
   ! Result = 0 if LIMIT reached and no valid line
   !          1 if valid line found
   !
   ! LINEPTR is set before entry to point where search is to start from
   ! and is set after call to where search may continue from.
   !
   ! For result=1, LINEDAYS is set to string(7) day letters,
   !  LINETIME is set to string(5) hh.mm, and TEXT to rest of line.
   integer  J,K,V
   string (255) S
         J=LINEPTR
         V=0; ! becomes 1 on finding valid line
         cycle 
            if  J=LIMIT then  exit 
            K=J
            until  BYTEINTEGER(J)=NL cycle 
               exit  if  J=LIMIT
               J=J+DIRECTION
            repeat 
            V=VALIDLINE(S,J,K)
            exit  if  V#0
         repeat 
         LINEPTR=J
         if  V=0 then  result =0
         LINEDAYS=FROMSTRING(S,1,7)
         LINETIME=FROMSTRING(S,9,13)
         S=FROMSTRING(S,14,LENGTH(S))
         S = JUST(S)
         TEXT=S
         result =1
end ; ! NEXTLINE
!
!-----------------------------------------------------------------------
!
externalintegerfn  AUTOCOMM(string (31)NEWFILE,integer  ACT)
! ACT = 0 clock tick (1 per minute)
!       1 disconnect file, re-connect file NEWFILE (D/AUTOFILE)
!       2 disconnect file (D/AUTOFILE 0)
!       3 connect file (system start-up)
!       4 say 'no autofile'
!        5 obey NEWFILE
ownstring (18)FILE="MANAGR.AUTOFILE"
owninteger  PROCEED FROM=-1
owninteger  DAY
string (7) LINEDAYS
owninteger  NORMAL STARTUP ACHIEVED=0,INHIBIT NORMAL STARTUP=0
owninteger  PREVSECSFRMN,DATA START,DATA END,FAD
string (255) TEXT
string (6)user
string (11)filename
string (8) NOW,LINETIME,PREV TIM
integer  J,K,SEG,GAP,LINEPTR,FLAG
record (FHDRF)name  H
constinteger  DOWN=-1, UP=1
switch  AA(0:5)
      NOW=TIME
      LENGTH(NOW)=5
      -> AA(ACT)
AA(3):     ! System start-up
      if  INHIBIT NORMAL STARTUP#0 then  result =0
      NORMAL STARTUP ACHIEVED=1
AA(2):     ! Disconnect file or inhibit normal startup
           ! (D/AUTOFILE 0)
      if  NORMAL STARTUP ACHIEVED=0 then  INHIBIT NORMAL STARTUP=1
AA(4):     ! say 'no autofile'
AA(1):     ! D/AUTOFILE <file>  (or null)
      TEXT = "no autofile"
      PLACE(TEXT, 0, 4, 0, 0)
      result  = 0 if  ACT = 4
!
      J = DDISCONNECTI(FILE, -1, 0)
      J = 0 IF  J = 39
      FILE = "MANAGR.AUTOFILE"
      PROCEED FROM = -1
      RESULT  = J IF  ACT = 2; ! disconnect the autofile
      DAY = DDAYNUMBER + 1
      DAY = 1 + DAY - 7*(DAY//7); ! Sun = 1 etc
!
      IF  NEWFILE = "" THEN  TEXT = FILE ELSE  TEXT = NEWFILE
!
      SEG = 0
      GAP = 0
      J = DCONNECTI(TEXT, -1, 1, 0, SEG, GAP)
      RESULT  = J UNLESS  J = 0
      FILE = TEXT
!
      file -> user . (".") . filename
      filename = filename . " " while  length(filename) < 11
      PLACE(filename, 0, 4, 0, 0)
!
      FAD=SEG<<18
      H==RECORD(FAD)
      unless  0<H_TXTRELST<H_NEXTFREEBYTE and   c 
         H_NEXTFREEBYTE<=H_MAXBYTES and  H_THREE=3 then  result =89; ! invalid autofile
      DATA START=FAD+H_TXTRELST
      DATA END=FAD+H_NEXTFREEBYTE
      ! Check that valid lines have monotonically increasing times
      PREV TIM="25.00"
      LINEPTR=DATA END
      until  LINEPTR=DATA START cycle 
         FLAG=NEXTLINE(LINEDAYS,LINETIME,LINEPTR,TEXT,DOWN,DATASTART)
         if  FLAG#0 start 
            result =89 if  LINE TIME > PREV TIM; ! INVALID AUTOFILE
            PREV TIM=LINE TIME
         finish 
      repeat 
!
      ! Find most recent IPL point (if any)
      LINEPTR = DATA END
      until  LINEPTR = DATA START cycle 
         FLAG = NEXTLINE(LINEDAYS,LINETIME,LINEPTR,TEXT,DOWN,DATASTART)
         if  FLAG#0 and  LENGTH(TEXT)>3 and  FROMSTRING(TEXT,1,3)="IPL" start 
            if  CHARNO(LINEDAYS,DAY)#'-' and  LINETIME<=NOW start 
                  PROCEED FROM = LINEPTR
                  exit 
            finish 
         finish 
      repeat 
      PROCEED FROM = DATA START if  PROCEED FROM < 0
AA(0):     ! regular tick (1-minute)
      result =0 if  PROCEED FROM < 0; ! no file
      ! Set up J and K to see whether we have passed midnight recently
      J = PREVSECSFRMN
      K = COM_SECSFRMN
      PREVSECSFRMN = K
      if  PROCEED FROM = 0 start 
         ! Nothing more in file. Has midnight passed recently?
         RESULT  = 0 UNLESS  J > K
!
         J = SYSAD(DATKEY, -1); ! reset last IPL date&time
         INTEGER(J + 4) = PACKDT >> 17; ! DATE
         INTEGER(J) = 0; ! TIME
!
         DAY = DDAYNUMBER + 1
         DAY = 1 + DAY - 7*(DAY//7); ! Sun = 1 etc
         PROCEED FROM=DATA START
      finish 
      ! Proceed up file, activating msgs prior to NOW and finishing at
      ! DATA END or msg-line timed for later than NOW
      LINEPTR = PROCEED FROM
      until  LINEPTR = DATA END cycle 
         FLAG = NEXTLINE(LINEDAYS,LINE TIME,LINEPTR,TEXT,UP,DATA END)
         if  FLAG # 0 start 
            exit  if  LINE TIME > NOW
            SEND(TEXT, "Auto") UNLESS  CHARNO(LINEDAYS,DAY) = '-'
            PROCEED FROM = LINEPTR
         finish 
      repeat 
      PROCEED FROM = 0 if  FLAG = 0; ! no more data
      result  = 0
AA(5):
      RESULT  = OBEYFILE(NEWFILE)
end ; ! AUTOCOMM
!
!-----------------------------------------------------------------------
!
endoffile