File: NuthatchLogImpl.mesa
Last Edited by: Swinehart, January 17, 1984 2:34 am
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: BOOL←FALSE] = {
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: BOOL←FALSE] = {
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.ROPE←NIL, endOfLog: BOOL←FALSE] = {
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] = {
handle.logStream ←
AlpineFS.StreamOpen[ name: handle.logFileName, accessOptions: $append ];
};
InitializeLog:
PUBLIC
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]; };
};
Zap:
PROC[files: Rope.
ROPE←
NIL, reallyZap:
BOOL←
FALSE] = {
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.ROPE ← NARROW[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];
};
}.