! 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
