! 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