<> <> <> <<>> DIRECTORY IO, PupStream, PupWKS USING [telnet], Rope, RuntimeError USING [UNCAUGHT], SimpleTerminal USING [TurnOn], XNSPSCommander USING [TalkToUser], XNSPSMessages, XNSPSUser; XNSPSNetworkUserImpl: CEDAR MONITOR IMPORTS IO, PupStream, RuntimeError, SimpleTerminal, XNSPSCommander, XNSPSMessages EXPORTS XNSPSUser = { ROPE: TYPE ~ Rope.ROPE; STREAM: TYPE ~ IO.STREAM; TalkWithUser: PUBLIC PROC [stream: IO.STREAM] = { inProcs: REF IO.StreamProcs _ IO.CreateStreamProcs[ variety: input, class: $pupRemoteUser, getChar: GetChar, endOf: EndOf, flush: Flush, close: Close]; data: StreamState _ NEW[StreamStateRep]; in: STREAM _ IO.CreateStream[streamProcs: inProcs, streamData: data]; outProcs: REF IO.StreamProcs _ IO.CreateStreamProcs[ variety: output, class: $pupRemoteUser, putChar: PutChar, endOf: EndOf, flush: Flush, eraseChar: EraseChar, close: Close]; out: STREAM _ IO.CreateStream[streamProcs: outProcs, streamData: data]; data.stream _ stream; XNSPSCommander.TalkToUser[in, out]; }; StreamState: TYPE ~ REF StreamStateRep; StreamStateRep: TYPE ~ RECORD [ flushed: BOOL _ FALSE, stream: STREAM, closed: BOOL _ FALSE]; GetChar: PROC [self: STREAM] RETURNS [CHAR] = TRUSTED {c: CHAR; ignore: INT _ 0; data: StreamState _ NARROW[self.streamData]; IF NOT data.flushed THEN SendNow[self]; WHILE ignore >= 0 DO ENABLE PupStream.StreamClosing, PupStream.Timeout => GOTO Closing; mark: NAT _ 0; timingMark: NAT = 5; timingMarkReply: NAT = 6; dataMark: NAT = 1; charsAvail: INT _ 0; bytes: PACKED ARRAY [0..4] OF CHAR; charsAvail _ IO.UnsafeGetBlock[data.stream, [LOOPHOLE[LONG[@bytes]], 0, 1]]; IF charsAvail # 0 THEN c _ bytes[0] ELSE {gotMark: BOOL _ FALSE; IF data.stream.GetInfo.class = $Pup THEN {gotMark _ TRUE; mark _ PupStream.ConsumeMark[data.stream ! RuntimeError.UNCAUGHT => {gotMark _ FALSE; CONTINUE}]}}; SELECT mark FROM 0 => NULL; dataMark => {ignore _ 1}; timingMark => {ignore _ 1}; ENDCASE => ignore _ 2; ignore _ ignore - 1; REPEAT Closing => {data.closed _ TRUE; ERROR IO.EndOfStream[stream: self]}; ENDLOOP; RETURN [c] }; PutChar: PROC[self: IO.STREAM, char: CHAR] = BEGIN data: StreamState _ NARROW[self.streamData]; IO.PutChar[data.stream, char]; IF char = '\n THEN IO.PutChar[data.stream, '\l]; data.flushed _ FALSE; END; EndOf: PUBLIC PROC[self: IO.STREAM] RETURNS [BOOL] = BEGIN data: StreamState _ NARROW[self.streamData]; RETURN [IO.EndOf[data.stream]]; END; EraseChar: PROC [self: STREAM, char: CHAR] = { data: StreamState _ NARROW[self.streamData]; IO.PutChar[data.stream, IO.BS]; data.flushed _ FALSE; }; Flush: PUBLIC PROC [ self: IO.STREAM ] = BEGIN data: StreamState _ NARROW[self.streamData]; IO.Flush[data.stream]; data.flushed _ TRUE; END; Close: ENTRY PROC [self: IO.STREAM, abort: BOOL _ FALSE] = BEGIN data: StreamState; IF self = NIL THEN RETURN; IF self.streamData = NIL THEN RETURN; data _ NARROW[self.streamData]; IF NOT data.closed THEN IO.Close[data.stream]; data.closed _ TRUE; END; SendNow: PROC [self: IO.STREAM] = BEGIN data: StreamState _ NARROW[self.streamData]; IO.Flush[data.stream]; data.flushed _ TRUE; END; logStream: IO.STREAM _ NIL; MakeLogViewer: PROC = { IF logStream # NIL THEN RETURN; <> logStream _ SimpleTerminal.TurnOn[].out; XNSPSMessages.RegisterTTY[logStream]; }; NewConnection: PupStream.ListenerProc = TRUSTED { TalkWithUser[stream ! PupStream.StreamClosing => CONTINUE]; IO.Close[stream]; }; Init: PROC = TRUSTED { <> pupListener _ PupStream.CreateListener[ local: PupWKS.telnet, worker: NewConnection, getTimeout: 300000, putTimeout: 300000 ]; }; pupListener: PupStream.Listener _ NIL; Init[]; }.