-- Transport mechanism: local terminal simulation of TELNET interface -- [Juniper]MS>LocalGlass.mesa -- Andrew Birrell 7-Jan-81 17:17:52 DIRECTORY Ascii, GlassDefs USING[ HandleObject, Handle, StringType ], InlineDefs USING[ BITAND, BITOR ], IODefs, ProcessDefs USING[ Abort, SecondsToTicks, SetTimeout, Yield ], PupDefs USING[ PupSocketID ], StreamDefs USING[ StreamHandle ], StringDefs USING[ AppendDecimal, AppendLongDecimal ]; LocalGlass: MONITOR IMPORTS InlineDefs, IODefs, ProcessDefs, StringDefs EXPORTS GlassDefs = BEGIN TimeOut: PUBLIC SIGNAL = CODE; SynchReply: PUBLIC SIGNAL = CODE; ReadString: PUBLIC PROC[str: GlassDefs.Handle, prompt: STRING, s: STRING, type: GlassDefs.StringType] RETURNS[end: CHARACTER] = BEGIN OPEN str; dummy: CHARACTER = '*; --used for echo if type = pwd-- IODefsStream: StreamDefs.StreamHandle = IODefs.GetOutputStream[]; ShowIt: PROC = BEGIN IF type # pwd THEN WriteString[s] ELSE FOR i: CARDINAL IN [0..s.length) DO WriteChar[dummy] ENDLOOP; END; Unwrite: PROCEDURE = BEGIN IF s.length > 0 THEN BEGIN old: CHARACTER = IF type = pwd THEN dummy ELSE s[s.length-1]; WITH IODefsStream SELECT FROM Display => clearChar[IODefsStream, old]; ENDCASE => { WriteChar['\]; WriteChar[old] }; s.length _ s.length - 1; END; END; ClearWord: PROCEDURE = BEGIN state: { alpha, other } _ other; WHILE s.length > 0 DO SELECT s[s.length-1] FROM IN ['a..'z], IN ['A..'Z], IN ['0..'9] => state _ alpha; ENDCASE => IF state # other THEN EXIT; Unwrite[]; ENDLOOP; END; c: CHARACTER; WriteString[prompt]; ShowIt[]; c _ ReadChar[]; SELECT c FROM Ascii.ControlA, Ascii.BS, Ascii.ControlW, --client wants to edit it-- Ascii.SP, Ascii.CR, Ascii.DEL => NULL; --client accepts it-- ENDCASE =>--client rejects it-- IF s.length > 0 THEN BEGIN IF (WITH IODefsStream SELECT FROM Display=>TRUE,ENDCASE => FALSE) THEN WHILE s.length > 0 DO Unwrite[] ENDLOOP ELSE { WriteString["_ "L]; s.length _ 0; }; END; DO SELECT c FROM Ascii.ControlA, Ascii.BS => Unwrite[]; Ascii.ControlW => ClearWord[]; Ascii.ControlR => { WriteChar[Ascii.CR]; WriteString[prompt]; ShowIt[] }; ENDCASE => BEGIN SELECT c FROM Ascii.SP => IF type # line AND type # any THEN { end _ c; EXIT }; Ascii.CR => IF type # any THEN { end _ c; EXIT }; Ascii.ESC, Ascii.DEL => { end _ c; EXIT }; ENDCASE => NULL; IF s.length < s.maxlength THEN BEGIN s[s.length] _ c; s.length _ s.length+1; WriteChar[IF type = pwd THEN dummy ELSE c]; END ELSE WriteChar[Ascii.BEL]; END; c _ ReadChar[]; ENDLOOP; END; Listen: PUBLIC PROC[ work: PROC[GlassDefs.Handle], socket: PupDefs.PupSocketID _ [0,0] ] = BEGIN BEGIN -- Note that there is an over-all assumption that the client calls -- the glass stream from only one process. The monitor locks are -- used only to synchronize between the "Reader" process and the -- client. readerPSB: PROCESS; -- we maintain a circular buffer of incoming characters, -- primarily to look ahead for DEL -- -- rPos = next index for reading from buffer -- -- wPos = next index for writing into buffer -- -- rPos = wPos iff buffer is empty -- -- (wPos+1)MOD bLength = rPos iff buffer is full -- -- buffer data has "markBit" on iff datum is a "Mark" byte -- charMask: WORD = 177B; markBit: WORD = 200B; bLength: CARDINAL = 100; buffer: PACKED ARRAY [0..bLength) OF CHARACTER; rPos: CARDINAL _ 0; wPos: CARDINAL _ 0; bChange: CONDITION; delCount: CARDINAL _ 0; charsWritten: BOOLEAN _ FALSE;--chars written but not sent-- ChangeWPos: ENTRY PROC[change: CARDINAL] = INLINE BEGIN ENABLE UNWIND => NULL; IF rPos = wPos THEN NOTIFY bChange; wPos _ wPos + change; IF wPos = bLength THEN wPos _ 0; END; WLimit: ENTRY PROC RETURNS[ limit: CARDINAL ] = INLINE BEGIN ENABLE UNWIND => NULL; WHILE (limit _ IF wPos >= rPos THEN IF rPos = 0 THEN bLength-1 ELSE bLength ELSE rPos-1 ) = wPos DO WAIT bChange ENDLOOP; END; AddDel: ENTRY PROC = INLINE BEGIN ENABLE UNWIND => NULL; delCount _ delCount + 1; END; GetByte: ENTRY PROC RETURNS[ c: UNSPECIFIED ] = BEGIN ENABLE UNWIND => NULL; WHILE rPos = wPos DO IF charsWritten THEN { charsWritten _ FALSE }; WAIT bChange; IF rPos = wPos THEN SIGNAL TimeOut[]; ENDLOOP; c _ buffer[rPos]; rPos _ rPos+1; IF rPos = bLength THEN rPos _ 0; NOTIFY bChange; -- in case buffer was full -- IF c = Ascii.DEL THEN delCount _ delCount-1; END; Reader: PROC = BEGIN ENABLE ABORTED => CONTINUE; DO -- always read one character -- used: CARDINAL = 1; [] _ WLimit[]; buffer[wPos] _ IODefs.ReadChar[]; FOR index: CARDINAL IN [wPos..wPos+used) DO IF (buffer[index]_InlineDefs.BITAND[buffer[index], charMask]) = Ascii.DEL THEN AddDel[]; ENDLOOP; ChangeWPos[used]; ENDLOOP; END; lineWidth: CARDINAL _ 0; pageHeight: CARDINAL _ 0; terminal: CARDINAL _ 0; charPos: CARDINAL _ 0; linePos: CARDINAL _ 0; ConsiderSST: PROC[thisSST: [0..128)] = BEGIN SELECT thisSST FROM 1 => -- data mark -- NULL; 2 => lineWidth _ GetByte[]; 3 => pageHeight _ GetByte[]; 4 => terminal _ GetByte[]; 5 => NULL; 6 => SIGNAL SynchReply[]; ENDCASE => NULL--ignore--; END; ReadChar: PROC RETURNS[c: CHARACTER] = BEGIN DO c _ GetByte[]; IF InlineDefs.BITAND[c, markBit] # 0 THEN ConsiderSST[InlineDefs.BITAND[c, charMask]] ELSE EXIT ENDLOOP; linePos _ 0; -- only count lines between input operations -- END; MyReadString: PROC[prompt: STRING, s: STRING, type: GlassDefs.StringType] RETURNS[end: CHARACTER] = { end _ ReadString[@obj, prompt, s, type] }; WriteChar: PROC[c: CHARACTER] = BEGIN IF delCount # 0 THEN RETURN; charsWritten_TRUE; ProcessDefs.Yield[]; IODefs.WriteChar[c]; END; WriteString: PROC[s: STRING] = BEGIN IF delCount # 0 THEN RETURN; charsWritten_TRUE; ProcessDefs.Yield[]; IODefs.WriteString[s]; END; WriteDecimal: PROC[n: CARDINAL] = BEGIN s: STRING = [6] -- -65536 --; StringDefs.AppendDecimal[s,n]; WriteString[s]; END; WriteLongDecimal: PROC[n: LONG CARDINAL] = BEGIN s: STRING = [11] -- -6553665536 --; StringDefs.AppendLongDecimal[s,n]; WriteString[s]; END; NoteSent: ENTRY PROC = INLINE { charsWritten _ FALSE }; SendNow: PROC = { NoteSent[] }; CharsLeft: PROC RETURNS[CARDINAL] = { RETURN[IF lineWidth > 0 THEN lineWidth-charPos ELSE LAST[CARDINAL] ] }; LinesLeft: PROC RETURNS[CARDINAL] = { RETURN[IF pageHeight > 0 THEN pageHeight-linePos ELSE LAST[CARDINAL] ] }; SetWidth: PROC[new: CARDINAL] = { lineWidth _ new }; SetHeight: PROC[new: CARDINAL] = { pageHeight _ new }; DelTyped: ENTRY PROC RETURNS[ BOOLEAN ] = { RETURN[ delCount # 0 ] }; Synch: PROC = { buffer[wPos] _ InlineDefs.BITOR[6, markBit]; ChangeWPos[1] }; Flush: PROC = { WHILE delCount > 0 DO [] _ ReadChar[] ENDLOOP }; obj: GlassDefs.HandleObject _ [ReadChar, MyReadString, WriteChar, WriteString, WriteDecimal, WriteLongDecimal, SendNow, CharsLeft, LinesLeft, SetWidth, SetHeight, DelTyped, Synch, Flush]; ProcessDefs.SetTimeout[@bChange, ProcessDefs.SecondsToTicks[150] ]; readerPSB _ FORK Reader[]; BEGIN ENABLE TimeOut => RESUME; work[@obj ! SynchReply => RESUME]; END--for cleanup--; ProcessDefs.Abort[readerPSB]; JOIN readerPSB; END; END; END.