! APM filestore File Statistics utilities
!Hacked JHB 24/6/86 to make it work for small disks (GIVE SUMMARY)
!Extended RWT 23/7/86 to get hitlists in decreasing order of blocks
include  "Inc:FS.IMP"
include  "Inc:UTIL.IMP"
include  "SYSTEM:CONFIG.INC"

ownbytearray  buffer(0 : 511)
ownbytename  next
ownbytename  last

routine  do(integer  what, string (31) param)
   integer  i
      i = fcommr(what << 8, param, buffer(0), 512)
      next == buffer(0)
      last == next[i]
end 

integerfn  next sym
   integer  ch
      result  = -1 if  next == last
      ch = next
      next == next[1]
      result  = ch
end 

routine  skip to(integer  what)
   integer  ch
      ch = next sym until  ch = what
end 

routine  dump
   integer  ch
      ch = next sym
      while  ch >= 0 cycle 
         print symbol(ch)
         ch = next sym
      repeat 
end 

string (31)fn  next word
   string (31) s
   integer  ch
      s = ""
      ch = next sym until  ch > ' '
      cycle 
         s = s . to string(ch)
         ch = next sym
      repeat  until  ch <= ' '
      result  = s
end 

integerfn  next integer
   integer  n, ch
      n = 0
      ch = next sym until  '0' <= ch <= '9'
      while  '0' <= ch <= '9' cycle 
         n = 10 * n + ch - '0'
         ch = next sym
      repeat 
      result  = n
end 

ownstring (7)array  users(1 : 128) = "***" (*)

routine  get users(integer  p)
   integer  i
      do('¬', to string('0' + (p << 1)))
      users(i) = next word for  i = 1, 1, 64
      do('¬', to string('0' + (p << 1) + 1))
      users(i) = next word for  i = 65, 1, 128
end 

routine  sort users(integer  from, to)
   string (7) d
   integer  l, u
   label  up, fi, fo, do
      return  if  from >= to
      l = from;  u = to
      d = users(u)
      -> fi
up:   l = l + 1;  -> fo if  l = u
fi:   -> up unless  users(l) >= d
      users(u) = users(l)
do:   u = u - 1;  -> fo if  l = u
      -> do unless  users(u) <= d
      users(l) = users(u)
      -> up
fo:   users(u) = d
      sort users(from, l - 1)
      sort users(u + 1, to)
end 

owninteger  total files   = 0
owninteger  total extents = 0
owninteger  total blocks  = 0

routine  show one(string (7) who)
   integer  files = 0, blocks = 0, extents = 0
      return  if  who = "---" or  who = "*bad*"
      do('F', who . ",0")
      printstring(who);  spaces(8 - length(who))
      skip to(':');  files   = next integer
      skip to(':');  extents = next integer
      skip to(':');  blocks  = next integer
      if  files = 0 start 
         printstring("         -         -         -         -")
         printstring("         -         -")
      else 
         write(files,   9);  total files   = total files   + files
         write(blocks,  9);  total blocks  = total blocks  + blocks
         write(extents, 9);  total extents = total extents + extents
         print(blocks  / files,   7, 1)
         print(extents / files,   7, 1)
         print(blocks  / extents, 7, 1)
      finish 
      newline
end 

routine  give summary (integer  first part, last part)
   {jhb was here 24/6/86}
   recordformat  bitmap fm(bytearray  b(0 : bitmap size))
   record (bitmap fm)name  bm
   integer  s, l, i, partno, j, total, total total
   string (127) command = ""
   byte  x

   !small disks will bomb out event 3 on reading past end of disk
   on  3 start ; last part=partno-1; -> eod; finish 

   total total = 0
   for  partno = first part, 1, last part cycle 
      connect file("$:BITMAP." . to string(partno + '0'), 0, s, l)
      bm == record(s)
      total = 0
      for  i = 0, 1, bitmap size - 1 cycle 
         x = bm_b(i)
         for  j = 0, 1, 7 cycle 
            total = total + 1 if  x & 1 # 0
            x = x >> 1
         repeat 
      repeat 
      printstring("Partition ");  write(partno, 0)
      printstring(" has ");  write(total, 0)
      printstring(" block");  printsymbol('s') if  total # 1
      printstring(" (")
      print(100 * total / (bitmap size << 3), 0, 2)
      printstring("%) used")
      newline
      total total = total total + total
   repeat 
eod:
   return  unless  first part = 0 and  last part#0
   printstring("Grand total of ");  write(total total, 0)
   printstring(" blocks (")
   print(100 * total total / (8 * bitmap size * (last part + 1)), 0, 2)
   printstring("%) used")
   newline
end 

routine  give details (integer  first part, last part)
   integer  p, i

   !Small disks will bomb out event 3 on reading ficticious partition
   on  3 start ; return ; finish 

   newlines (2)
   for  p=first part, 1, last part cycle 
      total files = 0;   total blocks = 0;   total extents = 0
      get users(p)
      sort users(1, 128)
      printstring("Partition ");  write(p, 0)
      printstring(" at ");  printstring(time)
      printstring(" on ");  printstring(date)
      newline
      printstring("User         Files    Blocks   Extents")
      printstring("       B/F       E/F       B/E")
      newline
      printstring("--------------------------------------")
      printstring("------------------------------")
      newline
      show one(users(i)) for  i = 1, 1, 128
      printstring("--------------------------------------")
      printstring("------------------------------")
      newline
      printstring("Totals  ")
      if  total files = 0 start 
         printstring("** none **")
      else 
         write(total files,   9)
         write(total blocks,  9)
         write(total extents, 9)
         print(total blocks  / total files,   7, 1)
         print(total extents / total files,   7, 1)
         print(total blocks  / total extents, 7, 1)
      finish 
      newline
      printstring("             Files    Blocks   Extents")
      printstring("       B/F       E/F       B/E")
      newline
      print symbol (12) and  newlines (2) if  p#last part
   repeat 
end 

routine  give hitlist (integer  first part, last part)
   integer  p, i

   recordformat  f(record (f)name  l,r,string (7)name,
     integer  blocks,files,extents,partition)
   record (f)name  tree == nil

   record (f)map  add(record (f)name  cell,tree)
     result  == cell if  tree==nil
     if  cell_blocks<=tree_blocks start 
       tree_l == add(cell,tree_l)
     else 
       tree_r == add(cell,tree_r)
     finish 
     result  == tree
   end 

   routine  print(record (f)name  t,integer  mask)
     returnif  t==nil
     print(t_r,mask)
     if  1<<t_partition&mask#0 start 
       printstring(t_name); spaces(8-length(t_name))
       write(t_files,9); write(t_blocks,9); write(t_extents,9)
       write(t_partition,3) if  mask<0; newline
     finish 
     print(t_l,mask)
   end 

   routine  get one(string (7) who)
   integer  files, blocks, extents
      record (f)name  r
      return  if  who = "---" or  who = "*bad*"
      do('F', who . ",0")
      skip to(':');  files   = next integer
      skip to(':');  extents = next integer
      skip to(':');  blocks  = next integer
      returnif  files=0
      r == new(r); r = 0; r_name = who; r_partition = p
      r_blocks = blocks; r_files = files; r_extents = extents
      tree == add(r,tree)
   end 

   routine  get some(integer  p)
   integer  i
      on  3 start ; return ; finish 
      get users(p)
      sort users(1, 128)
      get one(users(i)) for  i = 1,1,128
      newlines (2)
      printstring("Partition ");  write(p, 0)
      printstring(" at ");  printstring(time)
      printstring(" on ");  printstring(date)
      newline
      printstring("User         Files    Blocks   Extents")
      newline
      print(tree,1<<p)
      print symbol (12) and  newlines (2) if  p#last part
   end 

   get some(p) for  p = first part, 1, last part
   unless  first part = last part start 
     printsymbol(12); newlines(2)
     printstring("All partitions"); newline
     printstring("User         Files    Blocks   Extents   Partition")
     newline
     print(tree,-1)
   finish 
end 

begin 
   const  integer  summary=0,full=1,hitlist=2
   integer  i, p, first part, last part
   byte  mode
   string (127) output
      p = -1
      define int param ("Partition", p, pam major)
      output = ":T"
      define param ("Output", output, pam new group + pam major)
      mode = summary
      define enum param ("Summary,Full,Hitlist", mode, pam major)
      process parameters (cliparam)
      mode = summary if  mode=0
      if  P<0 or  P>last partition start 
          first part = 0
          last part = last partition
      else 
          first part = P
          last part = P
      finish 
      open output(3, output);  select output(3)
      if  mode=summary start 
          give summary (first part, last part)
      elseif  mode=full
          give details (first part, last part)
      else 
          give hitlist (first part, last part)
      finish 
      close output
end  of  program