! APM Filestore BITMAP utilities

%include "SYSTEM:CONFIG.INC"
%include "Inc:UTIL.IMP"

%begin

%const %integer True=1, False=0
%integer p, which, usage mode, map mode, Holes mode, first part, last part
%string (127) output

%routine do map (%integer first part, last part)
   %recordformat bitmap fm(%bytearray b(0 : bitmap size))
   %record(bitmap fm)%name bm
   %integer s, l, i, j, total
   %string(127) command = "", output
   %byte x
   %for which = first part, 1, last part %cycle
      newlines (2)
      printstring("Partition ");  write(which, 0);  print symbol(':')
      connect file("$:BITMAP." . to string(which + '0'), 0, s, l)
      bm == record(s)
      %unless l = bitmap size %start
         printstring(" -- expected bitmap size ")
         write(bitmap size, 0)
         printstring(", got ");  write(l, 0)
         printstring(" -- ")
      %finish
      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
      print(100 * total / (bitmap size << 3), 3, 3)
      printstring("% used")
      newline
      %for i = 0, 1, bitmap size - 1 %cycle
         %if i & 7 = 0 %start
            phex4(i << 3)
            printstring(":  ")
         %finish
         x = bm_b(i)
         %for j = 0, 1, 7 %cycle
            %if x & 1 = 0 %then space %c
                          %else print symbol('*') %and total = total + 1
            x = x >> 1
         %repeat
         newline %if i & 7 = 7
      %repeat
      newline %if i & 7 # 7
   %repeat
%end

%routine do holes (%integer first part, last part)
   %recordformat hole fm(%integer size, count, l, r)
   %constinteger hole fm size = 4 + 4 + 4 + 4
   %integer holes, column
   %recordformat bitmap fm(%bytearray b(0 : bitmap size))
   %record(bitmap fm)%name bm
   %integer which, s, l, current hole, i, j, m
   %string(127) command = "", output
   %byte x

   %routine add hole(%integer size, %integername where)
      %record(hole fm)%name it
         %if where = 0 %start
            it == nil
            it == new(it)
            where = addr(it)
            it_size = size;  it_count = 1
            it_l = 0;  it_r = 0
         %else
            it == record(where)
            %if it_size = size %start
               it_count = it_count + 1
            %else %if it_size < size
               add hole(size, it_l)
            %else
               add hole(size, it_r)
            %finish
         %finish
   %end

   %routine print holes(%integer where)
      %record(hole fm)%name it
         %return %if where = 0
         it == record(where)
         print holes(it_l)
         write(it_size, 5);  write(it_count, 6)
         %if column & 3 = 0 %then newline %c
                            %else spaces(5)
         column = column + 1
         print holes(it_r)
   %end
   
   %for which = first part, 1, last part %cycle
      Holes = 0
      Column = 1
      newline
      printstring("Hole analysis for partition ");  write(which, 0)
      printstring(" on ");  printstring(date)
      printstring(" at ");  printstring(time)
      newlines (2)
      connect file("$:BITMAP." . to string(which + '0'), 0, s, l)
      bm == record(s)
      %unless l = bitmap size %start
         printstring(" -- expected bitmap size ")
         write(bitmap size, 0)
         printstring(", got ");  write(l, 0)
         printstring(" -- ")
      %finish
      current hole = 0
      %for i = 0, 1, bitmap size %cycle
         x = bm_b(i)
         m = 1
         %for j = 1, 1, 8 %cycle
            %if x & m = 0 %start
               current hole = current hole + 1
            %else
               add hole(current hole, holes) %if current hole # 0
               current hole = 0
            %finish
            m = m << 1
         %repeat
      %repeat
      add hole(current hole, holes) %if current hole # 0
      printstring("  Size  Count       Size  Count     ")
      printstring("  Size  Count       Size  Count")
      newline
      print holes(holes)
      newline %if column & 3 # 1
   %repeat
%end

%routine do usage (%integer first part, last part)
   %recordformat bitmap fm(%bytearray b(0 : bitmap size))
   %record(bitmap fm)%name bm
   %integer s, l, i, which, j, total, total total
   %string(127) command = ""
   %byte x

   total total = 0
   %for which = first part, 1, last part %cycle
      connect file("$:BITMAP." . to string(which + '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(which, 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
   %return %unless first part = 0 %and last part = last partition
   printstring("Grand total of ");  write(total total, 0)
   printstring(" blocks (")
   print(100 * total total / (8 * bitmap size * (last partition + 1)), 0, 2)
   printstring("%) used")
   newline
%end

   p = -1
   define int param ("Partition", p, pam major)
   output = ":T"
   define param ("Output", output, pam new group + pam major)
   Holes mode = False
   define boolean params ("Holes", Holes mode, pam major)
   Map mode = False
   define boolean params ("Map", map mode, pam major)
   Usage mode = False
   define boolean params ("Usage", Usage mode, pam major)
   process parameters (cliparam)
   %if Usage mode = False %and Holes mode = False %and map mode = False %start
       Print string ("Options for BITMAP are -MAP, -HOLES or -USAGE")
       Newline
       %stop
   %finish
   %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 usage mode # false %start
       do usage (first part, last part)
   %else %if map mode # false
       do map   (first part, last part)
   %else %if holes mode # false
       do holes (first part, last part)
   %finish

%end %of %program
