<> <> 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]]; <> 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] = { <> 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; }; <> 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]; <> 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]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] = { <> <> <Strowger>"];>> <> <> <", handle.userName];>> <> }; Zap: PROC[files: Rope.ROPE_NIL, reallyZap: BOOL_FALSE] = { filePattern: Rope.ROPE _ Rope.Cat["[Luther.Alpine]", 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]", fileName]]; ENDLOOP; }; Get: PROC[file: Rope.ROPE] = { remoteFile: Rope.ROPE_Rope.Cat["[Luther.Alpine]", file]; AlpineCmds.Copy[to: file, from: remoteFile]; }; }.