File: NuthatchLogImpl.mesa
Last Edited by: Swinehart, October 14, 1985 4:03:08 pm PDT
DIRECTORY
AlpineCmds,
AlpineFS USING [StreamOpen],
FS USING [Error, ErrorDesc, ErrorFromStream],
GVBasics USING [RName],
IO,
Log USING [ Problem, Report ],
Names USING [ CurrentRName ],
Nuthatch USING [NuthatchUserHandle, pd],
NuthatchLog,
Process USING [ EnableAborts ],
Rope USING [Cat, ROPE],
UserProfile USING [ Token ]
;
NuthatchLogImpl: CEDAR MONITOR
IMPORTS AlpineCmds, AlpineFS, FS, IO, Log, Names, Nuthatch, Process, Rope, UserProfile
EXPORTS NuthatchLog = {
Handle: TYPE = Nuthatch.NuthatchUserHandle;
Error: PUBLIC ERROR[code: NuthatchLog.ErrorCode] = CODE;
WriteLogEntry: PUBLIC ENTRY PROC [ logRope: Rope.ROPE, handle: Handle] = {
DO
ENABLE {
UNWIND => { handle.inUse←FALSE; BROADCAST handle.doneUsing; };
IO.Error => IF HandleECError[handle, ec].retry THEN LOOP;
FS.Error => IF HandleFSError[handle, error].retry THEN LOOP;
};
WHILE handle.inUse DO WAIT handle.doneUsing; ENDLOOP;
IF handle.logStream = NIL THEN { OpenLog[handle]; LOOP; };
handle.logStream.SetIndex[handle.logStream.GetLength[]];
handle.logStream.PutRope[logRope];
IF Nuthatch.pd.reportLogOut THEN Log.Report[Rope.Cat["Log <= ", logRope]];
Do the flush, which commits the write to an Alpine file.
handle.logStream.Flush[];
EXIT; ENDLOOP;
};
HandleECError: INTERNAL PROC[handle: Handle, ec: IO.ErrorCode]
RETURNS[retry: BOOLFALSE] = {
handle.inUse←FALSE;
IF ec=StreamClosed THEN { handle.logStream←NIL; RETURN[TRUE]; };
RETURN[HandleFSError[handle, FS.ErrorFromStream[handle.logStream]]];
};
HandleFSError: INTERNAL PROC[handle: Handle, error: FS.ErrorDesc]
RETURNS [retry: BOOLFALSE] = {
Success=TRUE, in this instance, means that the original error should be rejected.
SELECT error.group FROM
$environment => SELECT error.code FROM
$transAborted => retry←TRUE;
ENDCASE => Log.Problem[error.explanation, $System];
$lock, $user => Log.Problem[error.explanation, $System];
ENDCASE;
handle.inUse←FALSE;
IF ~retry THEN handle.logStream.Close[abort: TRUE!IO.Error=>CONTINUE];
handle.logStream ← NIL;
};
SetLogIndex: PUBLIC ENTRY PROC [handle:Handle] = {
DO
ENABLE {
UNWIND => { handle.inUse ← FALSE; BROADCAST handle.doneUsing; };
IO.Error => IF HandleECError[handle, ec].retry THEN LOOP;
FS.Error => IF HandleFSError[handle, error].retry THEN LOOP;
};
logReadPoint: INT;
WHILE handle.inUse DO WAIT handle.doneUsing; ENDLOOP;
handle.inUse ← TRUE;
logReadPoint←handle.logReadPoint;
IF logReadPoint=-1 THEN ERROR;
IF handle.logStream = NIL THEN { OpenLog[handle]; handle.inUse ← FALSE; LOOP; };
Read the next entry in the user's Nuthatch log and return it as a rope.
IO.SetIndex[handle.logStream, logReadPoint];
EXIT; ENDLOOP;
};
ReadLogEntry: PUBLIC ENTRY PROC [handle:Handle]
RETURNS[logEntryRope: Rope.ROPENIL, endOfLog: BOOLFALSE] = {
ENABLE {
UNWIND => { handle.inUse ← FALSE; BROADCAST handle.doneUsing; };
IO.EndOfStream => { endOfLog←TRUE; CONTINUE; };
IO.Error => IF HandleECError[handle, ec].retry THEN ERROR Error[aborted];
FS.Error => IF HandleFSError[handle, error] THEN ERROR Error[aborted];
};
IF ~handle.inUse THEN ERROR;
IF handle.logStream = NIL THEN ERROR Error[aborted];
Read the next entry in the user's Nuthatch log and return it as a rope.
IF handle.logStream.EndOf[] THEN { endOfLog←TRUE; RETURN; };
[]←IO.SkipWhitespace[handle.logStream]; -- Flush blanks
logEntryRope←IO.GetLineRope[handle.logStream];
IF Nuthatch.pd.reportLogIn THEN Log.Report[Rope.Cat["Log => ", logEntryRope]];
};
UpdateLogIndex: PUBLIC ENTRY PROC [handle:Handle] = {
ENABLE {
UNWIND => { handle.inUse ← FALSE; BROADCAST handle.doneUsing; };
IO.Error => IF HandleECError[handle, ec].retry THEN ERROR Error[aborted];
FS.Error => IF HandleFSError[handle, error] THEN ERROR Error[aborted];
};
newReadPoint: INT;
IF ~handle.inUse THEN ERROR;
newReadPoint←IO.GetIndex[handle.logStream];
handle.logReadPoint ← newReadPoint;
handle.inUse ← FALSE;
BROADCAST handle.doneUsing;
};
OpenLog: PROC [handle: Handle] = {
logFilePrefix: Rope.ROPE = UserProfile.Token[
key: "NuthatchLogPrefix", default: "[Luther.Alpine]<Nuthatch>Strowger>"];
IF handle.userName=NIL THEN
handle.userName ← Names.CurrentRName[];
handle.logFileName← Rope.Cat[logFilePrefix, "Logs>", handle.userName];
TRUSTED { Process.EnableAborts[@handle.doneUsing]; }; 
handle.logStream ←
AlpineFS.StreamOpen[ name: handle.logFileName, accessOptions: $append ];
};
InitializeLog: PUBLIC PROC [handle: Handle] = {
This stuff has all been moved to OpenLog, which is called automatically when needed
logFilePrefix: Rope.ROPE = UserProfile.Token[
key: "NuthatchLogPrefix", default: "[Luther.Alpine]<Nuthatch>Strowger>"];
IF handle.userName=NIL THEN
handle.userName ← Names.CurrentRName[];
handle.logFileName← Rope.Cat[logFilePrefix, "Logs>", handle.userName];
TRUSTED { Process.EnableAborts[@handle.doneUsing]; };
};
Zap: PROC[files: Rope.ROPENIL, reallyZap: BOOLFALSE] = {
filePattern: Rope.ROPE ← Rope.Cat["[Luther.Alpine]<Nuthatch>", IF files#NIL THEN files ELSE "*"];
fileL: LIST OF REF ← AlpineCmds.List[filePattern];
FOR fl: LIST OF REF ← fileL, fl.rest WHILE fl#NIL DO
fileName: Rope.ROPENARROW[fl.first];
Log.Report[fileName];
IF reallyZap THEN [] ←
AlpineCmds.Delete[Rope.Cat["[Luther.Alpine]<Nuthatch>", fileName]];
ENDLOOP;
};
Get: PROC[file: Rope.ROPE] = {
remoteFile: Rope.ROPE←Rope.Cat["[Luther.Alpine]<Nuthatch>", file];
AlpineCmds.Copy[to: file, from: remoteFile];
};
}.