-- file VirtCMFiles.Mesa
-- edited by Schroeder, February 13, 1981 10:52 AM
-- edited by Brotz, September 28, 1982 5:27 PM

DIRECTORY
exD: FROM "ExceptionDefs" USING [cantPut, DisplayBothExceptionLines, Exception, nil,
putWillOverwrite],
Inline USING [BITAND, LowHalf],
opD: FROM "OperationsDefs" USING [Expand, FileError, Stuff],
ovD: FROM "OverviewDefs" USING [CharMask],
TimeDefs USING [PackedTime],
vmD: FROM "VirtualMgrDefs" USING [AbandonMessageInsertion, CharIndex,
CMOCharMapTableSize, CMOMaxCharPerPage, ComposedMessagePtr,
EnsureCMBackingFile, GetMessageChar, GetMessageSize, InsertSubstringInMessage,
MessageOverflow, MessageRange, PageNumber, StartMessageInsertion,
StopMessageInsertion],
VMDefs USING [AllocatePage, GetFileLength, Page, PageNumber, Release, SetFileLength];

VirtCMFiles: PROGRAM
IMPORTS exD, Inline, opD, vmD, VMDefs
EXPORTS vmD =

BEGIN
OPEN vmD;


PutRangeInFile: PUBLIC
PROCEDURE [from: MessageRange, file: STRING, concurrenceNeeded: BOOLEAN,
UserConcurs: PROCEDURE [exD.Exception] RETURNS [BOOLEAN]] =
-- Overwrites the entire contents of the file with the characters from the message range.
-- Pseudo CR’s are converted to blanks. If file not empty and concurrenceNeeded, then
-- UserConcurs is called and the put proceeds only if TRUE is returned.
-- May raise opD.FileError.
BEGIN

OverwriteCheck: PROCEDURE RETURNS [BOOLEAN] =
{RETURN [NOT concurrenceNeeded OR UserConcurs[exD.putWillOverwrite]]};

Get: PROCEDURE RETURNS [POINTER, CARDINAL, BOOLEAN] =
BEGIN
copyCount, inPosition: CARDINAL;
outPosition: CARDINAL ← 0;
UNTIL outPosition = outBytes OR from.start >= from.end DO
[] ← GetMessageChar[from.message, from.start]; --load get cache with buffer page
inPosition ← from.start - from.message.get.first;
copyCount
← MIN[outBytes - outPosition, MIN[from.message.get.free, from.end] - from.start];
FOR i: CARDINAL IN [0 .. copyCount) DO
outChars[outPosition + i]
← Inline.BITAND[from.message.buffer.chars[inPosition + i], ovD.CharMask];
ENDLOOP;
outPosition ← outPosition + copyCount;
from.start ← from.start + copyCount;
ENDLOOP;
RETURN[outBuffer, outPosition, TRUE];
END; -- of Get --

outBytes: CARDINAL = 512;
outBuffer: VMDefs.Page ← VMDefs.AllocatePage[];
outChars: POINTER TO PACKED ARRAY [0 .. 0) OF CHARACTER ← LOOPHOLE[outBuffer];
opD.Stuff
[targetName: file, GetBlock: Get, OverwriteOK: OverwriteCheck, callerFileType: text
! opD.FileError =>
BEGIN
VMDefs.Release[outBuffer];
IF reason # cancel THEN
exD.DisplayBothExceptionLines[NIL, exD.cantPut, errorString, exD.nil];
END];
VMDefs.Release[outBuffer];
END; -- of PutRangeInFile --



InsertFileInMessage: PUBLIC PROCEDURE
[targetIndex: CharIndex, targetMessage: ComposedMessagePtr, file: STRING] =
-- Inserts the entire contents of the file in the message starting just before the targetIndex.
-- May raise opD.FileError or MessageOverflow.
BEGIN

AdjustFileSize: PROC [bytes: LONG CARDINAL, createTime: TimeDefs.PackedTime] =
BEGIN
cmMaxPages: CARDINAL = CARDINAL[CMOCharMapTableSize];
pages: PageNumber ← 0;
size: CharIndex ← GetMessageSize[targetMessage];
IF bytes < 2561 THEN RETURN; --dont extend in advance for small file--
IF bytes + size > LONG[CMOMaxCharPerPage] * cmMaxPages
THEN ERROR MessageOverflow;
IF size # 0 THEN pages ← targetMessage.filePageFF;
pages ← pages +
Inline.LowHalf [(bytes + CMOMaxCharPerPage - 1) / CMOMaxCharPerPage];
pages ← MIN[pages, cmMaxPages];
EnsureCMBackingFile[targetMessage];
IF VMDefs.GetFileLength[targetMessage.file].page < pages THEN
VMDefs.SetFileLength[targetMessage.file, [pages, 0]];
END; -- of AdjustFileSize --

AcceptBlock: PROCEDURE [bP: POINTER, bytes: CARDINAL] RETURNS [BOOLEAN] =
BEGIN
IF bytes # 0 THEN
InsertSubstringInMessage[targetMessage, LOOPHOLE[bP - 2, STRING], 0, bytes];
RETURN[TRUE];
END; -- of AcceptBlock --

StartMessageInsertion[targetMessage, targetIndex];
opD.Expand[file, AcceptBlock, AdjustFileSize
! opD.FileError, vmD.MessageOverflow => AbandonMessageInsertion[targetMessage]];
StopMessageInsertion[targetMessage]
END; -- of InsertFileInMessage --


END. -- of VirtCMFiles --