-- VultureDriver.mesa
-- Scavanger for Mail Archive files.
-- Maurice Herlihy August 7, 1982
-- Mike Schroeder April 8, 1983 10:21 am


DIRECTORY
 BodyDefs,
CIFS,
 File,
IO,
 IOExtras,
 Menus,
 Process,
 Rope,
 Space,
 UserExec,
 ViewerClasses,
 ViewerIO,
 ViewerOps,
 VMDefs,
 VultureDefs,
 VultureOps;

VultureDriver: MONITOR
IMPORTS CIFS, File, IO, IOExtras, Menus, Process, Rope, Space, UserExec,
 ViewerIO, ViewerOps, VultureOps
 = {
ROPE: TYPE = Rope.ROPE;

Vulture: PROC[fileName: Rope.ROPE, inTS, outTS: IO.STREAM] = {
  msgIndex, msgsDeleted, directoriesFixed, badMessages: INT ← 0;
  itemStream: VultureOps.ItemStream;
  dirStream: VultureOps.DirStream ← NIL;
  directory: VultureDefs.Directory ← NIL;
  file: CIFS.OpenFile;
  fc: File.Capability ;
  filePages: INT;
  start, size: INT;
  oldStart, oldSize: INT;
  space: Space.Handle;
  
  outTS.PutRope[IO.PutFR["\nOpening %g ... ", IO.rope[fileName]]];
  file ← CIFS.Open[name: fileName, mode: CIFS.read+CIFS.write];
  fc ← CIFS.GetFC[file];
  filePages ← File.GetSize[fc] - 1; -- don't mess with the header page
  outTS.PutRope[IO.PutFR[" %g pages", IO.int[filePages]]];
  IF filePages = 0 THEN {outTS.PutRope["Ignoring empty file\n"]; RETURN};
  space ← Space.Create[size: filePages, parent: Space.virtualMemory];
  Space.Map[space: space, window: Space.WindowOrigin[file: fc, base: 1]];
  Space.CreateUniformSwapUnits[parent: space];
  
  {ENABLE {VultureOps.EndOfStream => GOTO Done};
  
  itemStream ← VultureOps.OpenItemStream[space];
  dirStream ← VultureOps.OpenDirStream[space];
  DO
   messageBad, dirBad, alreadyDeleted: BOOLFALSE;
   directory ← VultureOps.NextDirectory[dirStream];  
   [start, size] ← ReadMessage[itemStream
    -- make sure that the last directory entry is deleted --
    ! VultureOps.EndOfStream => directory.bodyStart ← 0];
   oldStart ← directory.bodyStart;
   oldSize ← directory.bodyLength;
   
   -- say something informative.
   messageBad ← (start = 0);
   alreadyDeleted ← (oldStart = 0 AND size = oldSize);
   dirBad ← (~alreadyDeleted AND (oldSize # size OR oldStart # start));
   
   IF messageBad OR dirBad THEN
    outTS.PutRope[IO.PutFR["\n\tMessage %g:\t", IO.int[msgIndex]]];
   IF messageBad AND alreadyDeleted THEN {
    badMessages ← badMessages + 1;
    outTS.PutRope[IO.PutFR["message is bad, but already deleted. "]];
    };
   IF messageBad AND ~alreadyDeleted THEN {
     msgsDeleted ← msgsDeleted + 1;
     outTS.PutRope["message is bad, deleting. "];
     };
   IF dirBad THEN {
    directoriesFixed ← directoriesFixed + 1;
    outTS.PutRope["Fixing directory."];
    };
   directory.bodyStart ← start;
   directory.bodyLength ← size;
   msgIndex ← msgIndex+1;
   ENDLOOP;
   EXITS Done => {
    outTS.PutRope[IO.PutFR["\n%g messages found, ", IO.int[msgIndex]]];
    outTS.PutRope[IO.PutFR["%g messages deleted, ", IO.int[msgsDeleted]]];
    outTS.PutRope[IO.PutFR["%g directories fixed.\n", IO.int[directoriesFixed]]];
    };
   };
  -- mark other directories deleted.
  VultureOps.CloseDirStream[dirStream];
  -- write out changes
  Space.ForceOut[space];
  -- give back the space
  Space.Delete[space];
  CIFS.Close[file];
  };-- Vulture;
ReadMessage: PROC [itemStream: VultureOps.ItemStream] RETURNS [start, size: INT]
-- raises EndOfStream --
-- If message is good, returns start and size.
-- If message is bad, returns 0 and size.
 = {
 item: VultureOps.Item;
 start ← VultureOps.Current[itemStream];
 {ENABLE VultureOps.BadFormat => GOTO BadFormat;
  item ← VultureOps.NextItem[itemStream]; -- resignals EndOfStream
  WHILE item.type # BodyDefs.ItemType[LastItem] DO
   item← VultureOps.NextItem[itemStream];
   ENDLOOP;
  size ← VultureOps.Current[itemStream] - start;
  EXITS BadFormat => {
   VultureOps.Reset[itemStream, start];
   size ← VultureOps.FindPostmark[itemStream] - start;
   RETURN [start: 0, size: size];
   };
  };
 };
Init: UserExec.CommandProc = TRUSTED {Process.Detach[FORK MakeViewer[]]};
MakeViewer: PROC [] = {
 viewer: ViewerClasses.Viewer ← ViewerOps.CreateViewer[
  flavor: $Typescript,
  info: [name: "Vulture"]
  ];
 viewer.menu ← Menus.CreateMenu[];
 Menus.AppendMenuEntry[
  menu: viewer.menu,
  entry: Menus.CreateEntry[name: "Directory", proc: ScavangeDirectory]
  ];
 Menus.AppendMenuEntry[
  menu: viewer.menu,
  entry: Menus.CreateEntry[name: "File", proc: ScavangeFile]
  ];
 }; -- MakeViewer
ScavangeDirectory: ENTRY Menus.MenuProc = TRUSTED {
 viewer: ViewerClasses.Viewer = NARROW [parent];
 inTS, outTS: IO.STREAM;
 pattern, directory: ROPE;
 eproc: CIFS.EProc = TRUSTED {
  fileName: ROPE ← Rope.Cat[
   directory, "/",
   IO.PutFR["%g", IO.text[name]]
   ];
  Vulture[fileName, inTS, outTS];
RETURN [FALSE]; 
  };
 [in: inTS, out: outTS] ← ViewerIO.CreateViewerStreams[name: "", viewer: viewer];
 outTS.PutRope["Enter directory: " ];
 directory ← IOExtras.GetLine[inTS];
 outTS.PutRope["Enter pattern: "];
 pattern ← IOExtras.GetLine[inTS];
CIFS.Enumerate[directory, pattern, eproc
   ! CIFS.Error => TRUSTED {outTS.PutRope[error]; CONTINUE;}
   ];
  outTS.PutRope["\nDone.\n"];
 }; --ScavangeDirectory

ScavangeFile
: ENTRY Menus.MenuProc = TRUSTED {
viewer: ViewerClasses.Viewer = NARROW [parent];
 inTS, outTS: IO.STREAM;
 fileName: ROPE;
 [in: inTS, out: outTS] ← ViewerIO.CreateViewerStreams[name: "", viewer: viewer];
 outTS.PutRope["Enter file name: "];
 fileName ← IOExtras.GetLine[inTS];
 Vulture[fileName, inTS, outTS
  ! CIFS.Error => TRUSTED {outTS.PutRope[error]; CONTINUE;};
  ];
 outTS.PutRope["\nDone.\n"];
 }; -- ScavangeFile
  
-- Start code
 UserExec.RegisterCommand["Vulture", Init];
 [] ← Init[NIL, UserExec.GetExecHandle[]];
}.