-- GetOp.mesa
-- Edited by Schroeder, March 3, 1981 10:05 AM.
-- Edited by Brotz, October 16, 1980 5:41 PM.
-- Edited by Levin, February 24, 1981 4:49 PM.

DIRECTORY
crD: FROM "CoreDefs",
gsD: FROM "GlobalStorageDefs",
intCommon: FROM "IntCommon",
MailParse,
mfD: FROM "MailFormatDefs",
opD: FROM "OperationsDefs",
ovD: FROM "OverviewDefs",
ProcessDefs,
StringDefs,
vmD: FROM "VirtualMgrDefs";

GetOp: PROGRAM
IMPORTS crD, gsD, intC:intCommon, mfD, opD, ProcessDefs, StringDefs, vmD
EXPORTS opD
SHARES opD =

BEGIN

OPEN StringDefs;

fileAccessError: SIGNAL = CODE; -- Had error return trying to read or write a file

GetMailFileOperation: PUBLIC PROCEDURE [mailFile: crD.UFilename]
RETURNS [ovD.ErrorCode, vmD.TOCIndex] =
BEGIN
-- variables for GetMailFileOperation
bufferPage: gsD.MemoryPagePtr ← NIL;
bufferString: STRING; -- turns the BufferPage into a string
nextBufferSlot,
bytesInBuffer: CARDINAL;
pageInBuffer: crD.PageNumber;
pageNeeded: BOOLEAN ← TRUE; -- page needs to be read into buffer
dummyBufferRecord: opD.BufferRecord ← opD.BufferRecord[b1: NIL, s1: 0, b2: NIL, s2: 0];
eofPage: crD.PageNumber;
eofByte: CARDINAL;

charIndex: CARDINAL;
nextCharLimit: CARDINAL;

errorCode: ovD.ErrorCode;

tOCEntry1, tOCEntry2: vmD.TOCFixedPart; -- Buffers for TOC entries
tb1: vmD.TOCFixedPartPtr = @tOCEntry1;
tb2: vmD.TOCFixedPartPtr = @tOCEntry2;
tOCFileName: crD.UFilename;
firstUnSeenTOCIndex, lastTOCIndex: vmD.TOCIndex;
tOCString: STRING ← [opD.maxTOCStringLength];
parseHandle: mfD.Handle ← NIL;

parseStampLimit: CARDINAL = 24;

fName: STRING ← [75];
i: CARDINAL;

-- internal procedures for GetMailFileOperation

PositionNextChar: PROCEDURE [page: crD.PageNumber, byte, limit: CARDINAL] =
--sets buffer management variables so NextChar will produce char at specified position
BEGIN
page ← page + (byte / 512);
byte ← byte MOD 512;
pageNeeded ← pageNeeded OR page #pageInBuffer;
pageInBuffer ← page;
nextBufferSlot ← byte;
nextCharLimit ← limit;
charIndex ← 0;
END; -- of PositionNextChar --


NextChar: PROCEDURE RETURNS [lastChar: CHARACTER] =
BEGIN
IF charIndex >= nextCharLimit THEN lastChar ← MailParse.endOfInput
ELSE BEGIN -- normal case
IF pageNeeded THEN
BEGIN
[errorCode, bytesInBuffer] ← crD.ReadPages[bufferPage, 512, pageInBuffer,
intC.mailFileHandle];
-- if read is off the end of the file then 0 bytes will be returned
IF errorCode # ovD.ok THEN SIGNAL fileAccessError;
pageNeeded ← FALSE;
END;
lastChar ← bufferString[nextBufferSlot];
IF nextBufferSlot < 511 THEN nextBufferSlot ← nextBufferSlot + 1
ELSE -- about to return the last char in this page, so set to get new page next time
{nextBufferSlot ← 0; pageInBuffer ← pageInBuffer + 1; pageNeeded ← TRUE};
END;
charIndex ← charIndex + 1;
END; -- of NextChar --

BackupChar: PROCEDURE =
BEGIN
charIndex ← charIndex - 1;
IF nextBufferSlot = 0 THEN
BEGIN
nextBufferSlot ← 511;
pageInBuffer ← pageInBuffer - 1;
pageNeeded ← ~pageNeeded;
END
ELSE nextBufferSlot ← nextBufferSlot - 1;
END; -- of BackupChar --


-- code of GetMailFileOperation

BEGIN -- block for EXITS

fName.length ← 0;
AppendString[fName, mailFile];
mailFile ← fName;
FOR i IN [0 .. fName.length) UNTIL fName[i] = ’. DO ENDLOOP;
IF fName[i] # ’. THEN AppendString[fName, ".mail"L];
[errorCode, intC.mailFileHandle] ← crD.OpenFile[intC.user, mailFile, update];
IF errorCode # ovD.ok THEN GOTO simpleReturn;

ProcessDefs.Yield[];
ProcessDefs.Yield[];

tOCString.length ← 0;
AppendString[tOCString, mailFile];
AppendString[tOCString, "-dmsTOC"L];
tOCFileName ← tOCString;

[errorCode, eofPage, eofByte] ← crD.UFileLength[intC.mailFileHandle];
IF errorCode # ovD.ok THEN GOTO errorReturn;
IF eofPage = 0 AND eofByte = 0 THEN
BEGIN
[firstUnSeenTOCIndex, errorCode] ← vmD.VirtualizeTOC[intC.user,
tOCFileName, intC.mailFileHandle, new];
IF errorCode = ovD.ok THEN GOTO simpleReturn ELSE GOTO errorReturn;
END;

bufferPage ← gsD.GetMemoryPages[1];
bufferString ← LOOPHOLE[bufferPage - 2, STRING];

[firstUnSeenTOCIndex, errorCode] ←
vmD.VirtualizeTOC[intC.user, tOCFileName, intC.mailFileHandle, old];
IF errorCode # ovD.ok THEN GOTO errorReturn;

ProcessDefs.Yield[];
ProcessDefs.Yield[];

lastTOCIndex ← vmD.GetFirstFreeTOCIndex[] - 1;
IF lastTOCIndex = 0 THEN PositionNextChar[0, 0, parseStampLimit] -- no toc entries yet
ELSE BEGIN -- TOC has some entries already
vmD.GetTOCFixedPart[lastTOCIndex, tb1];
PositionNextChar[tb1.firstPage, tb1.firstByte, tb1.offsetToHeader];
IF mfD.ParseStamp[NextChar, tb2 ! fileAccessError => GOTO errorReturn] # ovD.ok
OR tb2.textLength # tb1.textLength OR tb2.offsetToHeader # tb1.offsetToHeader
THEN BEGIN -- last TOC entry is not reasonable
vmD.CleanupTOC[delete];
[firstUnSeenTOCIndex, errorCode] ←
vmD.VirtualizeTOC[intC.user, tOCFileName, intC.mailFileHandle, new];
IF errorCode # ovD.ok THEN GOTO errorReturn;
PositionNextChar[0, 0, parseStampLimit];
END -- last TOC entry is not reasonable
ELSE BEGIN -- TOC is reasonable
IF vmD.GetFirstChangedTOCIndex[] <= lastTOCIndex THEN
BEGIN -- TOC was not cleaned up last time, so do it.
gsD.ReturnMemoryPages[1, bufferPage];
bufferPage ← NIL;
errorCode ← opD.ReturnMailFileOperation[@dummyBufferRecord];
IF errorCode # ovD.ok THEN GOTO simpleReturn;
[errorCode, firstUnSeenTOCIndex] ← GetMailFileOperation[mailFile];
GOTO simpleReturn;
END -- TOC was not cleaned up last time, so do it.
ELSE PositionNextChar
[tb1.firstPage, tb1.firstByte+ tb1.offsetToHeader+ tb1.textLength, parseStampLimit];
END; -- TOC is reasonable
END; -- TOC has some entries already

parseHandle ← mfD.InitializeParseHeader[NextChar, BackupChar];
WHILE pageInBuffer < eofPage
OR (pageInBuffer = eofPage AND nextBufferSlot < eofByte) DO
ProcessDefs.Yield[];
ProcessDefs.Yield[];
tb1.firstPage ← pageInBuffer;
tb1.firstByte ← nextBufferSlot;
tb1.changed ← FALSE;
IF mfD.ParseStamp[NextChar, tb1 ! fileAccessError => GOTO errorReturn] # ovD.ok THEN
{errorCode ← ovD.notAMailFile; GOTO errorReturn};
IF ~tb1.deleted THEN
BEGIN
PositionNextChar[tb1.firstPage, tb1.firstByte+ tb1.offsetToHeader, tb1.textLength];
mfD.ParseHeaderForTOC[tOCString, parseHandle ! fileAccessError=>GOTO errorReturn];
IF firstUnSeenTOCIndex = 0 AND ~tb1.seen THEN
firstUnSeenTOCIndex ← vmD.GetFirstFreeTOCIndex[];
errorCode ← vmD.ExtendTOC[tb1, tOCString];
IF errorCode # ovD.ok THEN GOTO simpleReturn;
END;
PositionNextChar[tb1.firstPage, tb1.firstByte+ tb1.offsetToHeader+ tb1.textLength,
parseStampLimit];
ENDLOOP;

IF pageInBuffer = eofPage AND nextBufferSlot = eofByte
THEN GOTO simpleReturn; --last stamp pointed to first char beyond end of file

-- last stamp pointed beyond the end of the file, so contract last stamp
gsD.ReturnMemoryPages[1, bufferPage];
bufferPage ← NIL;
tb1.textLength ← (eofPage - tb1.firstPage) * 512 + eofByte - tb1.firstByte - tb1.offsetToHeader;
lastTOCIndex ← vmD.GetFirstFreeTOCIndex[] - 1;
vmD.PutTOCFixedPart[lastTOCIndex, tb1];
errorCode ← opD.ReturnMailFileOperation[@dummyBufferRecord];
IF errorCode # ovD.ok THEN GOTO simpleReturn;
[errorCode, firstUnSeenTOCIndex] ← opD.GetMailFileOperation[mailFile];
IF errorCode = ovD.ok THEN errorCode ← ovD.badMailFile;
GOTO simpleReturn;

EXITS
simpleReturn => NULL;
errorReturn =>
BEGIN
IF eofByte # 0 OR eofPage # 0
THEN [] ← crD.CloseFile[intC.mailFileHandle]
ELSE [] ← crD.DeleteFile[intC.mailFileHandle];
vmD.CleanupTOC[delete]; -- a nop if no toc exists yet
intC.mailFileHandle ← NIL;
END;
END; -- of EXITS block

IF parseHandle ~= NIL THEN mfD.FinalizeParseHeader[parseHandle];
IF bufferPage # NIL THEN gsD.ReturnMemoryPages[1, bufferPage];
ProcessDefs.Yield[];
ProcessDefs.Yield[];
RETURN[errorCode, firstUnSeenTOCIndex];
END; -- of GetMailFileOperation --


END.
-- of GetOp --