-- VultureOpsImpl.mesa -- Scavanger for Mail Archive files. -- Maurice Herlihy August 17, 1982 DIRECTORY BodyDefs, IO, Rope, Space, VMDefs, VultureDefs, VultureOps; VultureOpsImpl: PROGRAM IMPORTS IO, Space EXPORTS VultureOps = { -- Public types. Item: PUBLIC TYPE = LONG POINTER TO BodyDefs.ItemHeader; -- Opaque types DirStream: PUBLIC TYPE = REF DirStreamRep; DirStreamRep: PUBLIC TYPE = RECORD[ dirPage: LONG POINTER TO UNSPECIFIED, -- first directory page pages: INT, -- number of pages next: INT -- index of next directory ]; ItemStream: PUBLIC TYPE = REF ItemStreamRep; ItemStreamRep: PUBLIC TYPE = RECORD[ start: LONG POINTER TO UNSPECIFIED, -- start of region size: INT, -- size of region next: INT -- offset of next item ]; ROPE: TYPE = Rope.ROPE; postMarkSize: INT = 6; -- Suggested by empirical observation. EndOfStream: PUBLIC ERROR = CODE; BadFormat: PUBLIC ERROR = CODE; OpenDirStream: PUBLIC PROC [space: Space.Handle] RETURNS [dirStream: DirStream] -- Signals EndOfStream -- ={ first: INT _ FindFirstMessage[space -- resignal EndOfStream --]; dirStream _ NEW[ DirStreamRep _ [ dirPage: Space.LongPointer[space], pages: first / VMDefs.pageSize, next: 0 ] ]; }; NextDirectory: PUBLIC PROC [dirStream: DirStream] RETURNS [directory: VultureDefs.Directory] -- Signals EndOfStream -- = { pageIndex: INT _ dirStream.next / VultureDefs.entriesPerPage; entryIndex: INT _ dirStream.next MOD VultureDefs.entriesPerPage; dirPage: VultureDefs.DirPage _ LOOPHOLE[dirStream.dirPage + pageIndex*VMDefs.pageSize]; IF pageIndex >= dirStream.pages THEN ERROR EndOfStream; dirStream.next _ dirStream.next+1; directory _ @dirPage.entries[entryIndex]; }; CloseDirStream: PUBLIC PROC [dirStream: DirStream] = { directory: VultureDefs.Directory; maxIndex: INT; index: INT; IF dirStream = NIL THEN RETURN; -- File is very sick. maxIndex _ dirStream.pages * VultureDefs.entriesPerPage - 1; FOR index IN [dirStream.next .. maxIndex] DO directory _ NextDirectory[dirStream]; directory.bodyStart _ 0; directory.bodyLength _ 0; ENDLOOP; }; FindFirstMessage: PROC [space: Space.Handle] RETURNS [first: INT] -- Signals EndOfStream -- ={ itemStream: ItemStream; -- read offset of first message from directory. dirPage: VultureDefs.DirPage _ LOOPHOLE[Space.LongPointer[space]]; first _ dirPage.entries[0].bodyStart; -- if this points to a page boundary, believe it. IF first > 0 AND first MOD VMDefs.pageSize = 0 THEN RETURN; -- otherwise try a linear scan from second page. itemStream _ NEW[ItemStreamRep _ [ start: LOOPHOLE[Space.LongPointer[space]], size: LONG[Space.GetAttributes[space].size]*LONG[VMDefs.pageSize], next: 0 ] ]; first _ FindPostmark[itemStream -- resignal EndOfStream --]; }; OpenItemStream: PUBLIC PROC [space: Space.Handle] RETURNS [stream: ItemStream] -- signals EndOfStream -- ={ stream _ NEW[ ItemStreamRep _ [ start: LOOPHOLE[Space.LongPointer[space]], size: LONG[Space.GetAttributes[space].size]*LONG[VMDefs.pageSize], next: FindFirstMessage[space -- Resignal EndOfStream --] ] ]; }; NextItem: PUBLIC PROC [stream: ItemStream] RETURNS [item: Item] ={ IF stream.next + SIZE[BodyDefs.ItemHeader] >= stream.size THEN ERROR EndOfStream; item _ LOOPHOLE[stream.start+stream.next]; IF ~IsItem[item] THEN ERROR BadFormat; stream.next _ (item.length+1)/2 + SIZE[BodyDefs.ItemHeader] + stream.next; -- check size for overflow IF stream.next > stream.size THEN { stream.next _ stream.size; ERROR BadFormat; } }; Current: PUBLIC PROC [stream: ItemStream] RETURNS [INT] ={ RETURN [stream.next]; }; Reset: PUBLIC PROC [stream: ItemStream, address: INT] = { IF address > stream.size THEN ERROR EndOfStream; stream.next _ address; }; FindPostmark: PUBLIC PROC [stream: ItemStream] RETURNS [INT] = { stream.next _ stream.next+1; WHILE stream.next < stream.size - SIZE[BodyDefs.ItemHeader] DO item: Item _ LOOPHOLE[stream.start+stream.next]; IF item.type = BodyDefs.ItemType[PostMark] AND item.length = postMarkSize THEN RETURN [stream.next]; stream.next _ stream.next + 1; ENDLOOP; ERROR EndOfStream; }; NoMoreItems: PUBLIC PROC [stream: ItemStream] RETURNS [BOOL] ={ RETURN[stream.next >= stream.size] }; ParseItem: PUBLIC PROC [item: Item] RETURNS [ROPE] = { format: ROPE; SELECT item.type FROM BodyDefs.ItemType[PostMark] => format _ "PostMark(%g)"; BodyDefs.ItemType[Sender] => format _ "Sender(%g)"; BodyDefs.ItemType[ReturnTo] => format _ "Return To(%g)"; BodyDefs.ItemType[Recipients] => format _ "Recipients(%g)"; BodyDefs.ItemType[Text] => format _ "Text(%g)"; BodyDefs.ItemType[Capability] => format _ "Capability(%g)"; BodyDefs.ItemType[Audio] => format _ "Audio(%g)"; BodyDefs.ItemType[updateItem] => format _ "updateItem(%g)"; BodyDefs.ItemType[reMail] => format _ "reMail(%g)"; BodyDefs.ItemType[LastItem] => format _ "Last Item(%g)"; ENDCASE => format _ "Unknown Item!(%g)"; RETURN [IO.PutFR[format, IO.int[item.length]]]; }; IsItem: PROC [item: Item] RETURNS [BOOL] = { SELECT item.type FROM BodyDefs.ItemType[PostMark] => RETURN[TRUE]; BodyDefs.ItemType[Sender] => RETURN[TRUE]; BodyDefs.ItemType[ReturnTo] => RETURN[TRUE]; BodyDefs.ItemType[Recipients] => RETURN[TRUE]; BodyDefs.ItemType[Text] => RETURN[TRUE]; BodyDefs.ItemType[Capability] => RETURN[TRUE]; BodyDefs.ItemType[Audio] => RETURN[TRUE]; BodyDefs.ItemType[updateItem] => RETURN[TRUE]; BodyDefs.ItemType[reMail] => RETURN[TRUE]; BodyDefs.ItemType[LastItem] => RETURN[TRUE]; ENDCASE => RETURN[FALSE]; }; }.