-- ParseObjectDirs.mesa

-- M. D. Schroeder, February 8, 1983 8:42 am

DIRECTORY
CIFS USING [Error, Reset],
 FileIO USING [Open, OpenFailed],
IO USING [Close, EndOf, Error, GetRope, int, PutF, PutRope, real, STREAM,
  UnsafeBlock, UnsafeGetBlock],
 ObjectDirDefs USING [ObjectType],
 ObjectDirXDefs,
 Process USING [Detach],
 Rope USING [Cat, Equal, ROPE],
 ViewerIO USING [CreateViewerStreams],
 VMDefs USING [PageIndex, PageNumber, pageSize, pageByteSize];

ParseObjectDirs: PROGRAM
IMPORTS CIFS, FileIO, IO, Process, Rope, ViewerIO =
BEGIN

ObjectCount: TYPE = CARDINAL;

ObjDirPageSpace: TYPE = [0..255];

DirData: TYPE = RECORD[ SELECT freedom: * --w0,b15-- FROM
free => [ next: VMDefs.PageIndex,--w0,b[7..0]--
dopc: ObjectDirXDefs.DOPC --w[1..2]-- ],
used => [ type: ObjectDirDefs.ObjectType,--w0,b[14..11]--
word: VMDefs.PageIndex,--w0,b[7..0]--
page: VMDefs.PageNumber,--w1--
count: ObjectCount --w2-- ],
ENDCASE];

DirPageHeader: TYPE = RECORD[ nextFreePage: ObjDirPageSpace,
nextFreeIndex: VMDefs.PageIndex ];

entriesPerPage: CARDINAL =
(VMDefs.pageSize-SIZE[DirPageHeader])/SIZE[DirData];

DirIndex: TYPE = [ 0 .. entriesPerPage );
-- NOTE: DirIndex values must fit in the "index" field of an object number --

DirPage: TYPE = RECORD[ header: DirPageHeader,
data: ARRAY DirIndex OF DirData ];

CountsIndex: TYPE = [0 .. 999);
Counts: TYPE = ARRAY CountsIndex OF INTALL[0];

DoIt: PROCEDURE =
BEGIN
 in, out: IO.STREAM;
 server, total: REF Counts;
 dirPage: DirPage;
 dirPagePtr: LONG POINTER = @dirPage;
 serverName: Rope.ROPE;
 grandNum, grandProd: INT ← 0;
 total ← NEW [Counts];
 [in, out] ← ViewerIO.CreateViewerStreams[name: "ParseObjectDirs"];
DO
  server ← NEW [Counts];
  out.PutF["\n\nServer name: "];
  serverName ← in.GetRope[ ! IO.Error => EXIT ];
  IF Rope.Equal[serverName, "*"]
   THEN BEGIN
    out.PutRope["\n\n\n\nTotals for all servers\n\n"];
    FOR i: CountsIndex IN CountsIndex DO
     IF total[i] # 0 THEN out.PutF["%g %g\n", IO.int[i], IO.int[total[i]]];
     ENDLOOP;
    IF grandNum#0 THEN
     out.PutF["\nmessage bodies: %g, ave. references: %3.2f",
      IO.int[grandNum], IO.real[REAL[grandProd]/REAL[grandNum]]];
    END
   ELSE BEGIN    
    num, prod: INT ← 0;
    objectDirName: Rope.ROPE = Rope.Cat["/", serverName, "/Heap.ObjectDir"];
    file: IO.STREAM;
    --Parse the file--
    CIFS.Reset[objectDirName ! CIFS.Error => CHECKED {CONTINUE}];
file ← FileIO.Open[objectDirName ! FileIO.OpenFailed, CIFS.Error =>
       CHECKED {out.PutRope["\nOpen failed"]; LOOP}];
    UNTIL file.EndOf[] DO
     IF file.UnsafeGetBlock[
        block: IO.UnsafeBlock[dirPagePtr, 0, VMDefs.pageByteSize]]
      # VMDefs.pageByteSize THEN ERROR;
     FOR i: DirIndex IN DirIndex DO
      WITH entry: dirPage.data[i] SELECT FROM
       free => NULL;
       used => IF entry.type = body THEN BEGIN
        server[entry.count] ← server[entry.count] + 1;
        total[entry.count] ← total[entry.count] + 1;
        END;
       ENDCASE => ERROR;
      ENDLOOP;
     ENDLOOP;
    out.PutF["\n\n"];
    FOR i: CountsIndex IN CountsIndex DO
     IF server[i]#0 THEN BEGIN
      out.PutF["%g %g\n", IO.int[i], IO.int[server[i]]];
      IF i#0 THEN {num ← num + server[i]; prod ← prod + server[i]*i};
      END;
     ENDLOOP;
    IF num#0 THEN
     out.PutF["\nmessage bodies: %g, ave. references: %3.2f",
      IO.int[num], IO.real[REAL[prod]/REAL[num]]];
    grandNum ← grandNum + num;
    grandProd ← grandProd + prod;
    file.Close[];
    END
  ENDLOOP;
END;

Process.Detach[FORK DoIt];

END.