!TITLE Procedures for manipulating Name-Number Tables
!DUMPNNT       lists the entries that are in use
!
!CLEARNNT      sets a NNT to zero.  It can (will) be re-created
!              automatically at the next CCK
!
!STATUSNNT     analyses a NNT and computes a histogram of the path
!              lengths to reach each name
!
!OPTNNT        saves the current NNT in a file N#NTf where f is the
!              fsys number.  Re-orders the entries in an optimum way
!
!RESTORENNT    returns the contents N#NTf to the NNT
!>
RECORDFORMAT  NNF(STRING  (6) USER, BYTEINTEGER  KB, TAG, MARKER,
    HALFINTEGER  INDNO)
CONSTINTEGER  NNTTOP= 1364
OWNRECORD  (NNF) ARRAY  NEWNNT(0:NNTTOP)
OWNRECORD  (NNF) ARRAYFORMAT  NNAF(0:NNTTOP)
CONSTSTRING (5)TFILE = "N#NT"
!
RECORDFORMAT  DATAF(INTEGER  START, BITSIZE, NNTSTART, NNTSIZE,
      NNTTOP, NNTHASH, INDEXSTART, FILESTART, END)
RECORDFORMAT  FF(INTEGER  SD, PD, FD, S,
      S1, S2, SNO, RESTORES,
      STRING (6)OWNER, BYTEINTEGER  SIZE, STRING (11)NAME)
!
!
!
EXTERNALSTRINGFNSPEC  DERRS(INTEGER  N)
! %EXTERNALINTEGERFNSPEC DERROR(%STRINGNAME TXT)
EXTERNALINTEGERFNSPEC  DLOWERACR(INTEGER  ACR)
EXTERNALINTEGERFNSPEC  DBITMAP2(INTEGERNAME  LO, HI, INTEGER  FSYS)
EXTERNALINTEGERFNSPEC  DNINDA(INTEGER  FSYS, INDNO, INTEGERNAME  INDAD)
EXTERNALINTEGERFNSPEC  FBASE(INTEGERNAME  LO, HI, INTEGER  FSYS)
SYSTEMSTRINGFNSPEC  HTOS(INTEGER  VALUE, PALCES)
SYSTEMSTRINGFNSPEC  ITOS(INTEGER  I)
EXTERNALROUTINESPEC  PROMPT(STRING  (15) S)
EXTERNALSTRINGFNSPEC  DATE
EXTERNALSTRINGFNSPEC  TIME
INTEGERFN  STOI2(STRING (255) S, INTEGERNAME  I2)
STRING  (63) P
INTEGER  TOTAL, SIGN, AD, I, J, HEX
!MON MON(1) = MON(1) + 1
   HEX = 0;  TOTAL = 0;  SIGN = 1
   AD = ADDR(P)
A: IF  S -> (" ").S THEN  -> A;         !CHOP LEADING SPACES
   IF  S -> ("-").S THEN  SIGN = -1
   IF  S -> ("X").S THEN  HEX = 1 AND  -> A
   P = S
   UNLESS  S -> P.(" ").S THEN  S = ""
   I = 1
   WHILE  I <= BYTEINTEGER(AD) CYCLE 
      J = BYTE INTEGER(I+AD)
      -> FAULT UNLESS  '0' <= J <= '9' OR  (HEX # 0 C 
         AND  'A' <= J <= 'F')
      IF  HEX = 0 THEN  TOTAL = 10*TOTAL C 
         ELSE  TOTAL = TOTAL<<4+9*J>>6
      TOTAL = TOTAL+J&15;  I = I+1
   REPEAT 
   IF  HEX # 0 AND  I > 9 THEN  -> FAULT
   IF  I > 1 THEN  I2 = SIGN*TOTAL AND  RESULT  = 0
FAULT:
   I2 = 0
   RESULT  = 1
END ;                            ! STOI2
!
!-----------------------------------------------------------------------
!
!-----------------------------------------------------------------------
!
!
!
ROUTINE  RSTRG(STRINGNAME  S)
INTEGER  I
      S = ""
      CYCLE 
         READSYMBOL(I)
         RETURN  IF  I = NL
         S = S . TOSTRING(I)
      REPEAT 
END 
!
!
!
ROUTINE  RDINT(INTEGERNAME  N)
STRING (63)S
      N = -1
      CYCLE 
         RSTRG(S)
         RETURN  IF  STOI2(S, N) = 0
      REPEAT 
END 
!
EXTERNALINTEGERFNSPEC  DDISCONNECT(STRING  (6) USER, STRING  (11) FILE,
    INTEGER  FSYS, DESTROY)
EXTERNALINTEGERFNSPEC  DCONNECT(STRING  (6) USER, STRING  (11) FILE,
    INTEGER  FSYS, MODE, APF, INTEGERNAME  SEG, GAP)
EXTERNALINTEGERFNSPEC  DCREATE(STRING  (6) USER, STRING  (11) FILE,
    INTEGER  FSYS, NKB, TYPE)
SYSTEMROUTINESPEC  MOVE(INTEGER  LEN, FROM, TO)
SYSTEMROUTINESPEC  FILL(INTEGER  LEN, FROM, PATTERN)
!
!
!
ROUTINE  WS(STRING  (255) S)
      PRINTSTRING(S)
      NEWLINE
END ; ! WS
!
!
!
ROUTINE  WSN(STRING (255)S, INTEGER  N)
      PRINTSTRING(S)
      WRITE(N, 1)
      NEWLINE
END ; ! WSN
!
!
!
INTEGERFN  FBASE2(INTEGER  FSYS, ADR)
!
! This returns the characteristics of an on-line disc in a record
! of format DISCDATAF at address ADR
INTEGER  J, LOB, HIB, TYPE, K
RECORD  (DATAF) NAME  DATA
CONSTINTEGER  TOPTYPE= 5
CONSTINTEGERARRAY  BITSIZE(1:TOP TYPE)= X'1000'(2), X'2000'(2), X'5000'
CONSTINTEGERARRAY  NNTSTART(1:TOP TYPE)= X'7000'(4), X'A000'
CONSTINTEGERARRAY  NNTSIZE(1:TOP TYPE)= X'4000'(4), X'1FF8'
CONSTINTEGERARRAY  NNTTOP(1:TOP TYPE)= 1364(4), 681
CONSTINTEGERARRAY  NNTHASH(1:TOP TYPE)= 1361(4), 667
CONSTBYTEARRAY  INDEXSTART(1:TOP TYPE)= 12(5)
CONSTINTEGERARRAY  FILESTART(1:TOP TYPE)= 1024(5)
CONSTINTEGERARRAY  HI(1:TOP TYPE)= X'3F1F', X'59F3', X'8F6F',
                  X'B3E7', X'24797'
      J = FBASE(LOB, HIB, FSYS)
      RESULT  = J UNLESS  J = 0
!
      TYPE =  - 1
      CYCLE  K = 1, 1, TOP TYPE
         TYPE = K ANDEXITIF  HIB = HI(K)
      REPEAT 
      RESULT  = 8 IF  TYPE < 0
!
      DATA == RECORD(ADR)
!
      DATA_START = LOB
      DATA_BITSIZE = BITSIZE(TYPE)
      DATA_NNTSTART = NNTSTART(TYPE)
      DATA_NNTSIZE = NNTSIZE(TYPE)
      DATA_NNTTOP = NNTTOP(TYPE)
      DATA_NNTHASH = NNTHASH(TYPE)
      DATA_INDEXSTART = INDEX START(TYPE)
      DATA_FILESTART = FILE START(TYPE)
      DATA_END = HIB
      RESULT  = 0
END ; ! FBASE2
!
!-----------------------------------------------------------------------
!
INTEGERFN  HASH(STRING  (6) USER, INTEGER  NNTHASH)
INTEGER  A, J, W
CONSTINTEGERARRAY  P(1:6)=157,257,367,599, 467,709
      A = ADDR(USER)
      W = 0
      CYCLE  J = 1, 1, 6
         W = W + (BYTEINTEGER(A + J) - 47) * P(J)
      REPEAT 
      RESULT  = W - (W // NNTHASH) * NNTHASH
END ; ! HASH
!
!
!
INTEGERFN  INIT(INTEGERNAME  FSYS, ANNT, RECORD (DATAF)NAME  DATA)
INTEGER  FLAG, LO, HI
      PROMPT("Which FSYS? ")
      RDINT(FSYS)
!
      FLAG = FBASE2(FSYS, ADDR(DATA)); ! get characteristics of disc
      ->FAIL UNLESS  FLAG = 0
!
      FLAG = DBITMAP2(LO, HI, FSYS)
      -> FAIL UNLESS  FLAG = 0
      ANNT = LO >> 18 << 18 + DATA_NNTSTART
FAIL:
      RESULT  = FLAG
END ; ! INIT
!
!
!
EXTERNALROUTINE  DUMPNNT(STRING (255)S)
INTEGER  FLAG, FSYS, ANNT, J
RECORD (DATAF)DATA
RECORD (NNF)ARRAYNAME  NNT
      FLAG = INIT(FSYS, ANNT, DATA)
      -> FAIL UNLESS  FLAG = 0
!
      NNT == ARRAY(ANNT, NNAF)
!
      CYCLE  J = 0, 1, DATA_NNTTOP
         IF  LENGTH(NNT(J)_USER) = 6 START 
            WRITE(J, 4)
            SPACE
            PRINTSTRING(NNT(J)_USER)
            WRITE(NNT(J)_KB, 3)
            WRITE(NNT(J)_INDNO, 5)
            PRINTSTRING(" (file index)") UNLESS  NNT(J)_TAG = 0
            NEWLINE
         FINISH 
      REPEAT 
FAIL:
      WS(DERRS(FLAG))
END ; ! DUMPNNT
!
!
!
EXTERNALROUTINE  OPTNNT(STRING  (255) S)
INTEGER  FLAG, I, NUSERS, PATH, NEWI, A, NNTTOP, ANNT, NNTHASH
INTEGER  FSYS, SEG, GAP
RECORD  (DATAF) DATA
RECORD  (NNF) ARRAY  AUX(0:1364)
RECORD  (NNF) ARRAYNAME  OLDNNT
STRING (11)FILE
      A =  0; ! POSITION ON AUXTAB
      NUSERS = 0
      PATH = 0
!
      FLAG = INIT(FSYS, ANNT, DATA)
      -> FAIL UNLESS  FLAG = 0
!
      FILE = TFILE . ITOS(FSYS)
      NNTTOP = DATA_NNTTOP
      NNTHASH = DATA_NNTHASH
      CYCLE  I = 0, 1, NNTTOP; ! clear own array
         NEWNNT(I) = 0
      REPEAT 
!
!
      FLAG = DCREATE("", FILE, -1, 20, 0); ! big enough for a 4-page NNT and hdr
      -> FAIL UNLESS  FLAG = 0
!
      SEG = 0
      GAP = 0
      FLAG = DCONNECT("", FILE, -1, 3, 0, SEG, GAP)
      -> FAIL UNLESS  FLAG = 0
!
      OLDNNT == ARRAY(SEG<<18 + 4096, NNAF)
!
      MOVE(DATA_NNTSIZE, ANNT, ADDR(OLDNNT(0)))
!
      CYCLE  I = 0, 1, NNTTOP
         IF  LENGTH(OLDNNT(I)_USER) = 6 START 
            NUSERS = NUSERS + 1
            NEWI = HASH(OLDNNT(I)_USER, NNTHASH)
            IF  NEWNNT(NEWI)_USER = "" C 
            THEN  NEWNNT(NEWI) = OLDNNT(I) C 
            ELSESTART 
               AUX(A) = OLDNNT(I)
               A = A + 1
            FINISH 
         FINISH 
      REPEAT 
!
      FLAG = DDISCONNECT("", FILE, -1, 0)
      -> FAIL UNLESS  FLAG = 0
!
      WSN("No of users:", NUSERS)
      WSN("No of clashes:", A)
!
      WHILE  A > 0 CYCLE 
         A = A - 1
         NEWI = HASH(AUX(A)_USER, NNTHASH)
         CYCLE 
            PATH = PATH + 1
            NEWI = NEWI + 1
            NEWI = 0 IF  NEWI > NNTTOP
            IF  NEWNNT(NEWI)_USER = "" C 
            THEN  NEWNNT(NEWI) = AUX(A) AND  EXIT 
         REPEAT 
      REPEAT 
      WSN("Path length:", PATH)
!
      FLAG = D LOWER ACR(2)
      -> FAIL UNLESS  FLAG = 0
!
      MOVE(DATA_NNTSIZE, ADDR(NEWNNT(0)), ANNT)
FAIL:
!      J = DERROR(TXT)
      WS(DERRS(FLAG))
END ; ! OPTNNT
!
!
!
EXTERNALROUTINE  RESTORE NNT(STRING (255)S)
INTEGER  FLAG, FSYS, ANNT, SEG, GAP, J
STRING (11)FILE
RECORD (DATAF)DATA
      FLAG = INIT(FSYS, ANNT, DATA)
      -> FAIL UNLESS  FLAG = 0
!
      SEG = 0
      GAP = 0
      FILE = TFILE . ITOS(FSYS)
      FLAG = DCONNECT("", FILE, -1, 1, 0, SEG, GAP)
      -> FAIL UNLESS  FLAG = 0
!
      FLAG = DLOWERACR(2)
      -> FAIL UNLESS  FLAG = 0
!
      MOVE(DATA_NNTSIZE, SEG<<18 + 4096, ANNT)
FAIL:
      J = DDISCONNECT("", FILE, -1, 0)
      WS(DERRS(FLAG))
END ; ! RESTORE NNT
!
!
!
EXTERNALROUTINE  CLEAR NNT(STRING (255)S)
INTEGER  FLAG, FSYS, ANNT
RECORD (DATAF)DATA
      FLAG = INIT(FSYS, ANNT, DATA)
      -> FAIL UNLESS  FLAG = 0
!
      FLAG = DLOWERACR(2)
      -> FAIL UNLESS  FLAG = 0
!
      FILL(DATA_NNTSIZE, ANNT, 0)
FAIL:
      WS(DERRS(FLAG))
END ; ! CLEAR NNT
!
!
!
EXTERNALROUTINE  STATUSNNT(STRING  (255) S)
INTEGER  ANNT, NUSERS, P
RECORD  (NNF) NAME  NN
RECORD  (NNF) ARRAYNAME  NNT
INTEGER  FLAG, I, J
INTEGER  FSYS, PATH
INTEGERARRAY  HISTO(0:8)
RECORD  (DATAF) DATA
      WS("STATUS at ".TIME." on ".DATE)
      NUSERS = 0
      PATH = 0
      CYCLE  J = 0, 1, 8
         HISTO(J) = 0
      REPEAT 
!
      FLAG = INIT(FSYS, ANNT, DATA)
      -> FAIL UNLESS  FLAG = 0
!
      NNT == ARRAY(ANNT, NNAF)
!
      CYCLE  J = 0, 1, DATA_NNTTOP
         NN == NNT(J)
         IF  LENGTH(NN_USER) = 6 START 
            NUSERS = NUSERS + 1
            P = J - HASH(NN_USER, DATA_NNTHASH)
            P = P + DATA_NNTTOP IF  P < 0
            PATH = PATH + P
!
            HISTO(P) = HISTO(P) + 1 IF  P <= 5 
            HISTO(6) = HISTO(6) + 1 IF  6 <= P <= 10 
            HISTO(7) = HISTO(7) + 1 IF  11 <= P <= 20 
            HISTO(8) = HISTO(8) + 1 IF  P > 20 
         FINISH 
      REPEAT 
      WSN("No of users :", NUSERS)
      WSN("Total path  :", PATH)
      PRINTSTRING("Average path:")
      PRINT(PATH/NUSERS, 3, 2)
      NEWLINE
      WS("Path length histogram")
      WS("      0      1      2      3      4      5   6-10  11-20    >20")
         CYCLE  I = 0, 1, 8
            WRITE(HISTO(I), 6)
         REPEAT 
         NEWLINE
FAIL:
      WS(DERRS(FLAG))
END ; ! STATUSNNT
!
!
!
EXTERNALROUTINE  CLEAR EXTRA INDEX(STRING (255)S)
INTEGER  J, FSYS, ANNT, INDNO, HI, P, INDAD
RECORD (DATAF)DATA
      J = INIT(FSYS, ANNT, DATA)
      -> FAIL UNLESS  J = 0
!
      INDNO = 256 << 2; ! first 1K after 'old' indexes
      HI = DATA_FILESTART << 2 - 1; ! last
      J = DLOWERACR(2)
      -> FAIL UNLESS  J = 0
      CYCLE  P = INDNO, 1, HI
         J = DNINDA(FSYS, P, INDAD)
         -> FAIL UNLESS  J = 0
         FILL(1024, INDAD, 0)
         STRING(INDAD) = "NEVER"
      REPEAT 
FAIL:
      WS(DERRS(J))
END ; ! CLEAR EXTRA INDEX
!
!
!
EXTERNALROUTINE  CHECK EXTRA INDEX(STRING (255)S)
INTEGER  J, FSYS, ANNT, INDNO, HI, P, K, INDAD, C
      RECORD (DATAF)DATA
      RECORD (FF)NAME  F
      J = INIT(FSYS, ANNT, DATA)
      -> FAIL UNLESS  J = 0
!
      INDNO = 256 << 2
      HI = DATA_FILESTART << 2 -1
      C = 0
      CYCLE  P = INDNO, 1, HI
         J = DNINDA(FSYS, P, INDAD)
         -> FAIL UNLESS  J = 0
         F == RECORD(INDAD)
         IF  LENGTH(F_OWNER) = 6 AND  0 < LENGTH(F_NAME) < 12 START 
            C = C + 1
            RETURN  IF  C > 20
            PRINTSTRING(HTOS(P<<2+K, 3))
            SPACE
            PRINTSTRING(F_OWNER . " " . F_NAME . "
")
         FINISH 
      REPEAT 
FAIL:
      WS(DERRS(J))
END ; ! CHECK EXTRA INDEX
!
!
!
EXTERNALROUTINE  WRITE NEVERS(STRING (255)S)
INTEGER  J, FSYS, ANNT, LO, HI, P, INDAD
RECORD (DATAF)DATA
      J = INIT(FSYS, ANNT, DATA)
      -> FAIL UNLESS  J = 0
!
      LO = 12 << 2
      HI = 256 << 2 - 2
!
      J = DLOWERACR(2)
      -> FAIL UNLESS  J = 0
!
      CYCLE  P = HI, -2, LO
         J = DNINDA(FSYS, P, INDAD)
         -> FAIL UNLESS  J = 0
         UNLESS  STRING(INDAD) = "EMPTY" START 
            WSN("LAST NON EMPTY", P)
            EXIT 
         FINISH 
         STRING(INDAD) = "NEVER"
      REPEAT 
FAIL:
      WS(DERRS(J))
END ; ! WRITE NEVERS
ENDOFFILE