// IfsTelnetProt.bcpl -- BSP interface and Telnet protocol stuff // Copyright Xerox Corporation 1979, 1980, 1981 // Last modified January 2, 1981 2:11 PM by Taft get "Pup.decl" get "IfsRs.decl" get "IfsTelnet.decl" get "IfsFiles.decl" external [ // outgoing procedures TelnetGets; TelnetEndofs; TelnetResetIn TelnetPuts; TelnetResetOut TelnetProcess; TelnetErrors; TelnetOtherPup // incoming procedures BSPPutMark; BSPPutInterrupt; BSPForceOutput; BSPGetMark; ReleasePBI ReadRingBuffer; WriteRingBuffer; ResetRingBuffer RingBufferEmpty; RingBufferFull Block; Yield; Dismiss; SetTimer; TimerHasExpired; Max; IFSError Gets; Puts; Endofs; Errors // incoming statics CtxRunning; keys ] // Stream operations on "keys" (the telnet input stream) and // "dsp" (the telnet output stream) //--------------------------------------------------------------------------- let TelnetGets(keys) = valof //--------------------------------------------------------------------------- // Gets character from server context's telnet input ring buffer. // If an abort (connection failure or control-C interrupt) has occurred, // returns delete. Also replies to timing marks and handles timeouts. [ CtxRunning>>TCtx.thisLine = 0 let timer = nil SetTimer(lv timer, telnetGetsTimeout) [ // repeat if CtxRunning>>TCtx.aborting resultis $*177 let char = ReadRingBuffer(lv CtxRunning>>TCtx.iRBD) if char ge 0 then [ if char eq #377 then [ BSPPutMark(CtxRunning>>TCtx.bspSoc, markTimingReply); loop ] resultis char ] // Inactivity timeout check (bypass if user is enabled wheel) if TimerHasExpired(lv timer) & not CtxRunning>>TCtx.userInfo>>UserInfo.capabilities.wheel then Errors(CtxRunning>>TCtx.bspStream, ecGetsTimeout) Dismiss(1) ] repeat ] //--------------------------------------------------------------------------- and TelnetEndofs(keys) = RingBufferEmpty(lv CtxRunning>>TCtx.iRBD) //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- and TelnetResetIn(keys) be //--------------------------------------------------------------------------- // Resets telnet input stream by invoking timing mark protocol. [ ResetRingBuffer(lv CtxRunning>>TCtx.iRBD) CtxRunning>>TCtx.oTimingMarks = CtxRunning>>TCtx.oTimingMarks+1 BSPPutMark(CtxRunning>>TCtx.bspSoc, markTiming) until CtxRunning>>TCtx.oTimingMarks eq 0 do [ unless Endofs(keys) do Gets(keys); Dismiss(1) ] ] //--------------------------------------------------------------------------- and TelnetPuts(dsp, char) be //--------------------------------------------------------------------------- // Puts character onto Telnet BSP stream. Flushing handled by TelnetProcess. [ unless CtxRunning>>TCtx.aborting do [ if char eq $*n then [ CtxRunning>>TCtx.thisLine = CtxRunning>>TCtx.thisLine+1 if CtxRunning>>TCtx.thisLine ge CtxRunning>>TCtx.numLine then [ // end of page, maybe pause for user if CtxRunning>>TCtx.pause & Endofs(keys) then [ Puts(dsp, #7); Gets(keys) ] CtxRunning>>TCtx.thisLine = 0 ] ] if char ls #40 & char ne $*n & char ne #7 then [ Puts(dsp, $↑); char = char+#100 ] Puts(CtxRunning>>TCtx.bspStream, char, telnetPutsTimeout) if char eq $*n then // insert lf after cr until Chat and Tenex fixed Puts(CtxRunning>>TCtx.bspStream, $*l, telnetPutsTimeout) ] Yield() // don't hog the machine ] //--------------------------------------------------------------------------- and TelnetResetOut(dsp) be //--------------------------------------------------------------------------- // Resets telnet output stream by invoking sync protocol. [ BSPPutInterrupt(CtxRunning>>TCtx.bspSoc, 0, "") BSPPutMark(CtxRunning>>TCtx.bspSoc, markSync) CtxRunning>>TCtx.thisLine = 0 ] //--------------------------------------------------------------------------- and TelnetProcess(ctx) be //--------------------------------------------------------------------------- // Performs processing for telnet context ctx. // Specifically, transfers bsp input to typeahead buffer, interprets // telnet protocol, and flushes the output stream. [ unless ctx>>TCtx.initialized return let soc, str = ctx>>TCtx.bspSoc, ctx>>TCtx.bspStream if ctx>>TCtx.timeout return if soc>>BSPSoc.state ne stateOpen then [ Errors(str, ecBadStateForGets); return ] BSPForceOutput(soc) [ // repeat until Endofs(str) do [ if RingBufferFull(lv ctx>>TCtx.iRBD) return let char = Gets(str) test ctx>>TCtx.pendingMark eq 0 ifso test char eq $*003 // control-C? ifso [ ResetRingBuffer(lv ctx>>TCtx.iRBD) ctx>>TCtx.controlC = true ] ifnot unless ctx>>TCtx.iSyncs gr 0 do WriteRingBuffer(lv ctx>>TCtx.iRBD, char±) ifnot [ switchon ctx>>TCtx.pendingMark into [ case markPageLength: [ ctx>>TCtx.numLine = Max(char, 5); endcase ] case markTerminalType: [ ctx>>TCtx.pause = char eq termTypeDisplay; endcase ] ] ctx>>TCtx.pendingMark = 0 ] ] unless soc>>BSPSoc.markPending break let mark = BSPGetMark(soc) switchon mark into [ case markSync: [ ctx>>TCtx.iSyncs = ctx>>TCtx.iSyncs-1; endcase ] case markTiming: [ WriteRingBuffer(lv ctx>>TCtx.iRBD, #377); endcase ] case markTimingReply: [ ctx>>TCtx.oTimingMarks = ctx>>TCtx.oTimingMarks-1; endcase ] case markLineWidth: case markPageLength: case markTerminalType: [ ctx>>TCtx.pendingMark = mark; endcase ] ] ] repeat ] //--------------------------------------------------------------------------- and TelnetErrors(str, ec) = valof //--------------------------------------------------------------------------- // Handles errors occurring on the telnet connection. // This occurs in the RsMgr context for Gets and the server context for Puts. [ let ctx = str>>BSPStr.par1 ctx>>TCtx.iSyncs = 0 // reset protocol interactions ctx>>TCtx.oTimingMarks = 0 switchon ec into [ case ecGetsTimeout: case ecPutsTimeout: ctx>>TCtx.timeout = true case ecBadStateForGets: case ecBadStateForPuts: ctx>>TCtx.logout = true resultis -1 default: IFSError(ecUncaughtTelnetError, ec) ] ] //--------------------------------------------------------------------------- and TelnetOtherPup(pbi) be //--------------------------------------------------------------------------- // Handles special Pups (specifically Interrupts) that pop out of the // Telnet socket. Note that this is called from within the socket's RTP // context. However, it is safe for this procedure to be in an overlay // because if any socket is open then there is a process executing inside // TelnetTopLevel, which is in the same overlay. [ if pbi>>PBI.pup.type eq typeInterrupt then [ let ctx = pbi>>PBI.socket>>BSPSoc.par1 ctx>>TCtx.iSyncs = ctx>>TCtx.iSyncs+1 if ctx>>TCtx.iSyncs gr 0 then ResetRingBuffer(lv ctx>>TCtx.iRBD) ] ReleasePBI(pbi) ]