File: WalnutFileImpl.mesa
Contents:
procedures for reading & writing Walnut log-Style files
This module is NOT a MONITOR; its assumes that its caller has a lock on the
stream if that is appropriate
Created by: Willie-Sue on October 15, 1982
Last edited by:
Rick Cattell on XXX
Willie-Sue on February 18, 1983 1:35 pm
DIRECTORY
IO,
Rope,
Runtime,
WalnutDB,
WalnutDBLog,
WalnutWindow;
WalnutFileImpl:
CEDAR
PROGRAM
IMPORTS IO, Rope, Runtime, WalnutDB, WalnutDBLog, WalnutWindow
EXPORTS WalnutDBLog =
BEGIN OPEN WalnutDBLog;
ROPE: TYPE = Rope.ROPE;
********************************************************
ReadPrefixInfo:
PUBLIC
PROC[strm:
IO.
STREAM, headersPos:
INT]
RETURNS[msgID: ROPE, categories: ROPE, outOfSynch: BOOL] =
BEGIN
curPos: INT;
outOfSynch← FALSE;
UNTIL (curPos← strm.GetIndex[]) = headersPos
DO
tag, value: ROPE;
IF curPos > headersPos THEN {outOfSynch← TRUE; RETURN};
[tag, value]← TagAndValue[h: strm, inc: 2];
IF Rope.Equal[tag, msgIDRope,
FALSE]
THEN msgID← value
ELSE IF Rope.Equal[tag, categoriesRope, FALSE] THEN categories← value;
ENDLOOP;
END;
TagAndValue:
PUBLIC
PROC[h:
IO.Handle, inc:
INT]
RETURNS[tag, value:
ROPE] =
BEGIN
line: ROPE← GetLine[h];
pos: INT← line.Find[":"];
IF pos < 0 THEN RETURN;
tag← line.Substr[0, pos];
value← line.Substr[pos+inc, Rope.MaxLen ! Runtime.BoundsFault => {tag← NIL; CONTINUE}];
END;
ReadStartOfMsg:
PUBLIC
PROC[strm:
IO.
STREAM, doReport:
BOOL]
RETURNS[startPos, prefixLength, entryLength: INT, entryChar: CHAR] =
BEGIN
ENABLE
IO.EndOfStream =>
GOTO eoS;
line: ROPE;
prefixLength← entryLength← 0;
entryChar← 'X; -- not a valid entryType char
IF strm.EndOf[] THEN RETURN;
startPos← strm.GetIndex[];
line← GetLine[strm];
IF
NOT line.Equal["*start*"]
THEN
BEGIN
foo: INT← startPos;
IF doReport
THEN
WalnutWindow.Report[
IO.PutFR["**start** not found at pos %g. Skipping to next entry.", IO.int[startPos]]];
UNTIL line.Equal["*start*"]
DO
IF strm.EndOf[]
THEN
RETURN;
startPos← strm.GetIndex[]; line← GetLine[strm] ENDLOOP;
END;
Read entry info line from log, e.g.: 00101 00029 US+
entryLength← strm.GetInt[];
prefixLength← strm.GetInt[];
line← GetLine[strm];
entryChar← line.Fetch[line.Length[]-1];
EXITS
eoS => {entryLength← -1; RETURN};
END;
RopeFromStream:
PUBLIC
PROC[strm:
IO.
STREAM, len:
INT]
RETURNS[
ROPE] =
reads arbitrary length ROPE from a stream
BEGIN
Get1:
SAFE
PROC
RETURNS[
CHAR] =
CHECKED {
RETURN[strm.GetChar[]]};
RETURN[Rope.FromProc[len, Get1]];
END;
GetLine:
PROC[h:
IO.
STREAM]
RETURNS[r:
ROPE] =
BEGIN
r← h.GetSequence[];
[]← h.GetChar[ ! IO.EndOfStream => CONTINUE]; -- read the CR
END;
********************************************************
MakeLogEntry:
PUBLIC
PROC [
entryType: LogEntryType, entryText: ROPE, strm: IO.STREAM, msgID: ROPE← NIL]
RETURNS [INT] =
Puts entryText out in Laurel format on the given stream.
There are four kinds of entries:
(1) messages: entryText is the header and body of the message
(2) insertions: entryText is of form (insertion of new relship):
Relation: relation
attr1: xxx
attr2: yyy
or of the form (for insertion of new entity):
Domain: domain
name: xxx
(3) deletions: entryText same form as above, but represents deletion of that relship or entity
(4) hasbeenread: entryText is the messageID
Returns integer position in file of end of log entry.
BEGIN OPEN IO;
typeChar: CHAR;
length: INT← entryText.Length[];
prefixLen: INT← minPrefixLength + msgID.Length[]; -- magic number
isMessage: BOOL← (entryType=message) OR (entryType=newMessage);
Put1:
SAFE
PROC[c:
CHAR]
RETURNS[stop:
BOOL] =
CHECKED
{ strm.PutChar[c]; RETURN[FALSE]};
SELECT entryType
FROM
message => typeChar← ' ;
newMessage => typeChar← '?;
insertion => typeChar← '+;
deletion => typeChar← '-;
hasbeenread => typeChar← '←
ENDCASE;
IF isMessage
THEN
{
IF entryText.Fetch[length-1] #
CR
THEN
{ entryText← Rope.Concat[entryText, "\n"]; length← length + 1};
};
strm.SetIndex[strm.GetLength[]];
strm.PutF["**start**\n%05d %05d US%g\n",
int[prefixLen+length], int[prefixLen], char[typeChar]];
strm.Put[ rope[entryText] ] doesn't work if entryText.Length > 77777B
IF msgID.Length[]#0 THEN strm.PutRope[msgID];
IF entryText.Length[] < 77777B
THEN strm.PutRope[entryText]
ELSE []← Rope.Map[base: entryText, action: Put1];
strm.Flush[]; -- flush after every write
RETURN[strm.GetIndex[]]
END;
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
this used to be in WalnutDBLogImpl, until that file got too big
OldMessageFile:
PUBLIC
PROC[strm:
IO.
STREAM, defaultPrefix:
ROPE]
RETURNS [ok: BOOL] =
BEGIN ENABLE UNWIND => NULL;
DO
beginMsgPos, msgLength, entryLength, prefixLength: INT;
msgID, msgText, prefix, categories: ROPE;
outOfSynch: BOOL← FALSE;
[beginMsgPos, prefixLength, entryLength, ]← ReadStartOfMsg[strm, TRUE];
IF entryLength = -1 THEN RETURN[FALSE];
IF (entryLength=0)
OR (entryLength=prefixLength)
THEN
EXIT;
-- Hardy's null message at end
IF
IO.PeekChar[strm] = '@
THEN strm.SetIndex[beginMsgPos + prefixLength]
-- hardy file
ELSE [msgID, categories, outOfSynch]← ReadPrefixInfo[strm, beginMsgPos+prefixLength];
IF outOfSynch
THEN
{ WalnutWindow.Report["Can't find prefix info; skipping to next entry"];
strm.SetIndex[beginMsgPos + entryLength];
LOOP
};
prefix←
IF categories#NIL THEN Rope.Cat[categoriesRope, ": ", categories, "\n"] ELSE defaultPrefix;
IF msgID # NIL THEN prefix← Rope.Cat[msgIDRope, ": ", msgID, "\n", prefix];
msgLength← entryLength-prefixLength;
msgText← RopeFromStream[strm, msgLength];
WalnutDB.AddMessageToLog[entryText: msgText, prefix: prefix];
WalnutWindow.ReportRope["."];
ENDLOOP;
RETURN[TRUE];
END;
END.