-- 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: BOOL ← FALSE;
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[]];
}.