file: PasOut.mesa
modified by Ramshaw, January 20, 1984 4:04 pm
written by McCreight, October 3, 1980 12:23 PM
DIRECTORY
BasicTime USING [GMT, Now, nullGMT],
FS USING [StreamOpen],
IO USING [Close, PutChar, PutRope],
PasPrivate,
PasPrivateVars,
Process USING [Yield],
RefText USING [AppendChar],
Rope USING [Balance, Concat, Fetch, FromChar, FromRefText, Length];
PasOut: CEDAR PROGRAM
IMPORTS BasicTime, FS, IO, PasPrivateVars, Process, RefText, Rope EXPORTS PasPrivate =
BEGIN
OPEN PasPrivate, PasPrivateVars;
lastTimeFileWasOutput: BasicTime.GMT ← BasicTime.nullGMT;
Normal queues are represented as ropes. As an efficiency hack, the top queue
on the stack of output queues has outBuf, a REF TEXT, included at its end.
Thus, the common case of appending to the end of the top queue on the stack
doesn't demand an allocation.
FlushOutBuf: PROCEDURE =
BEGIN
q: OutputQueuePtr = outQStk[outLevel];
IF outBuf.length # 0 THEN
BEGIN
q.contents ← Rope.Concat[q.contents, Rope.FromRefText[outBuf]];
outBuf.length ← 0;
END;
END; -- of FlushOutBuf
CharToQueue: PUBLIC PROCEDURE [c: CHARACTER, q: OutputQueuePtr ← NIL] =
BEGIN
IF q = NIL THEN q ← outQStk[outLevel];
IF q = outQStk[outLevel] THEN
BEGIN
IF outBuf.length = outBuf.maxLength THEN FlushOutBuf[];
[] ← RefText.AppendChar[to: outBuf, from: c]; -- new char will fit cause we just checked
END
ELSE q.contents ← Rope.Concat[q.contents, Rope.FromChar[c]];
END; -- of CharToQueue
CharToQueueStart: PUBLIC PROCEDURE [c: CHARACTER, q: OutputQueuePtr ← NIL] =
BEGIN
IF q = NIL THEN q ← outQStk[outLevel];
q.contents ← Rope.Concat[Rope.FromChar[c], q.contents];
END; -- of CharToQueueStart
StringToQueue: PUBLIC PROCEDURE [s: ROPE, q: OutputQueuePtr ← NIL] =
BEGIN
IF q = NIL THEN q ← outQStk[outLevel];
IF q = outQStk[outLevel] THEN
BEGIN
sLen: NAT ← s.Length[];
bufLen: NAT ← outBuf.length;
IF sLen + bufLen > outBuf.maxLength THEN FlushOutBuf[];
IF sLen + bufLen > outBuf.maxLength THEN -- still too big
{q.contents ← Rope.Concat[q.contents, s]; RETURN};
FOR i:NAT IN [0..sLen) DO
outBuf[bufLen+i] ← s.Fetch[i];
ENDLOOP;
outBuf.length ← bufLen+sLen;
END
ELSE q.contents ← Rope.Concat[q.contents, s];
END; -- of StringToQueue
StringToQueueStart: PUBLIC PROCEDURE [s: ROPE, q: OutputQueuePtr ← NIL] =
BEGIN
IF q = NIL THEN q ← outQStk[outLevel];
q.contents ← Rope.Concat[s, q.contents];
END; -- of StringToQueueStart
MergeQueue: PUBLIC PROCEDURE [
from: OutputQueuePtr, to: OutputQueuePtr ← NIL] =
BEGIN
IF to = NIL THEN to ← outQStk[outLevel];
IF to = outQStk[outLevel] OR from = outQStk[outLevel] THEN FlushOutBuf[];
to.contents ← Rope.Concat[to.contents, from.contents];
ClearQueue[from];
END; -- of MergeQueue
MergeQueueStart: PUBLIC PROCEDURE [
from: OutputQueuePtr, to: OutputQueuePtr ← NIL] =
BEGIN
IF to = NIL THEN to ← outQStk[outLevel];
IF to = outQStk[outLevel] OR from = outQStk[outLevel] THEN FlushOutBuf[];
to.contents ← Rope.Concat[from.contents, to.contents];
ClearQueue[from];
END; -- of MergeQueueStart
CopyQueue: PUBLIC PROCEDURE [
from: OutputQueuePtr, to: OutputQueuePtr ← NIL] =
BEGIN -- not especially efficient
IF to = NIL THEN to ← outQStk[outLevel];
IF to = outQStk[outLevel] OR from = outQStk[outLevel] THEN FlushOutBuf[];
to.contents ← Rope.Concat[to.contents, from.contents];
END; -- of CopyQueue
ClearQueue: PUBLIC PROCEDURE [q: OutputQueuePtr ← NIL] =
BEGIN
IF q = NIL THEN q ← outQStk[outLevel];
IF q = outQStk[outLevel] THEN FlushOutBuf[];
IF q.fileName.Length[] # 0 THEN
BEGIN
s: STREAM;
TRUSTED {WHILE lastTimeFileWasOutput = BasicTime.Now[] DO Process.Yield[] ENDLOOP};
s ← FS.StreamOpen[q.fileName, $create];
s.PutRope[q.contents];
s.Close[];
TRUSTED {lastTimeFileWasOutput ← BasicTime.Now[]};
commandHandle.out.PutChar['.];
END;
q.contents ← "";
q.fileName ← "";
END; -- of ClearQueue
BalanceQueue: PUBLIC PROCEDURE [q: OutputQueuePtr ← NIL] =
BEGIN
IF q = NIL THEN q ← outQStk[outLevel];
IF q = outQStk[outLevel] THEN FlushOutBuf[];
q.contents ← Rope.Balance[base: q.contents];
END; -- of BalanceQueue
PushOut: PUBLIC PROCEDURE [q: OutputQueuePtr ← NIL] =
BEGIN
IF outLevel # 0 THEN FlushOutBuf[];
outLevel ← outLevel + 1;
IF q = NIL THEN outQStk[outLevel] ← Z.NEW[OutputQueue←[contents: "", fileName: ""]]
ELSE BEGIN outQStk[outLevel] ← q END;
END;
CopyAndPopOut: PUBLIC PROCEDURE RETURNS [OutputQueuePtr] =
BEGIN
q: OutputQueuePtr ← outQStk[outLevel];
FlushOutBuf[];
outQStk[outLevel] ← NIL;
outLevel ← outLevel - 1;
RETURN[q];
END;
PopOut: PUBLIC PROCEDURE =
BEGIN ClearQueue[outQStk[outLevel]]; outLevel ← outLevel - 1; END;
END. -- of PasOut --