<<>> <> <> <> <> DIRECTORY Basics, IO, Rope, Process, UXStrings, SimpleStreams; SimpleStreamsOnBridgeImpl: CEDAR MONITOR LOCKS hh USING hh: REF MyRec <<--monitors for input only>> IMPORTS IO, Process, Rope, UXStrings, SimpleStreams SHARES SimpleStreams = BEGIN SPPEmu: TYPE = POINTER; --TO SPPEmuRep <> <> <<--call this to get the necessary include files.>> <<"%=threads/UIO\n=threads/SPPEmu.">> <<};>> <<>> BridgeConnect: PROC [cmd: UXStrings.CString] RETURNS [SPPEmu] = TRUSTED MACHINE CODE { <<--Connect to Bridge.>> <<--The cmd is e.g. "ERTTY\r-h foo\r">> <<--Return XR_SPPEmu or NIL with errno set on failure.>> "XR_BridgeConnect" }; BridgeDisconnect: PROC [sppe: SPPEmu] = TRUSTED MACHINE CODE { <<--Disconnect Bridge session.>> <<--Errors are dealt with silently.>> "XR_BridgeDisconnect" }; SPPEWrite: PROC [sppe: SPPEmu, buf: POINTER, nBytes: INT, setEM: INT] RETURNS [bytesWritten: INT] = TRUSTED MACHINE CODE { <<--Write to SPPEmulator stream.>> <<--Writes nBytes of data from the buffer buf to the descriptor sppe.>> <<--If setEM==TRUE, the end of the logical record is set.>> <<--If successful the procedure returns the number of bytes >> <<--actually written, which will be `nBytes'; on failure it >> <<--returns -1 with an error code in errno.>> "XR_SPPEWrite" }; SPPERead: PROC [sppe: SPPEmu, buf: POINTER, nWanted: INT] RETURNS [bytesRead: INT] = TRUSTED MACHINE CODE { <<--Read from SPPEmulator stream.>> <<--Reads up to nWanted bytes of data into the buffer buf,>> <<--from the descriptor sppe, returning the number of bytes actually read.>> <<--The value of errno is always significant; usually it's 0, but it >> <<--may be XR_ESPPENDREC if the end of a logical record was encountered.>> <<--May return fewer bytes than requested, but won't return 0 bytes except >> <<--at the end of a logical record.>> "XR_SPPERead" }; buffSize: INT = 100; MyRec: TYPE = MONITORED RECORD [ sppe: SPPEmu ¬ NIL, alive: BOOL ¬ TRUE, <<--inputBuffer>> lastCh: CHAR ¬ 0c, cond: CONDITION, readerOn: BOOL ¬ FALSE, --monitored in, out: INT ¬ 0, buff: PACKED ARRAY [0..buffSize) OF CHAR, connectCmd: UXStrings.CString ]; BCreateSimpleStreams: SimpleStreams.CreateProcType = { hh: REF MyRec ¬ NEW[MyRec]; hh.connectCmd ¬ UXStrings.Create["ERTTY\r foo\r"]; hh.sppe ¬ BridgeConnect[hh.connectCmd]; IF hh.sppe=NIL THEN ERROR; out ¬ IO.CreateStream[outputStreamProcs, hh]; in ¬ IO.CreateStream[inputStreamProcs, hh]; IF ~Rope.IsEmpty[header] THEN {IO.PutRope[out, header]; IO.PutChar[out, '\n]}; }; inputStreamProcs: REF IO.StreamProcs ¬ IO.CreateStreamProcs[ variety: $input, class: $BridgeSimpleStreamsInput, charsAvail: MyCharsAvail, getChar: MyGetChar, close: MyCloseBoth ]; outputStreamProcs: REF IO.StreamProcs ¬ IO.CreateStreamProcs[ variety: $output, class: $BridgeSimpleStreamsOutput, unsafePutBlock: MyUnsafePutBlock, putChar: MyPutChar, eraseChar: MyEraseChar, close: MyCloseBoth ]; MyPutChar: PROC [self: IO.STREAM, char: CHAR] = TRUSTED { IF char#0C THEN { buff: PACKED ARRAY [0..BYTES[WORD]) OF CHAR; hh: REF MyRec = NARROW[self.streamData]; written: INT; buff[0] ¬ char; written ¬ SPPEWrite[hh.sppe, @buff, 1, 1]; IF written#1 THEN { MyCloseBoth[self, FALSE]; ERROR IO.Error[Failure, self]; }; }; }; MyEraseChar: PROC [self: IO.STREAM, char: CHAR] = { MyPutChar[self, '\b]; }; MyUnsafePutBlock: PROC [self: IO.STREAM, block: Basics.UnsafeBlock] = TRUSTED { hh: REF MyRec = NARROW[self.streamData]; WHILE block.count>0 DO written: INT ¬ SPPEWrite[hh.sppe, LOOPHOLE[LOOPHOLE[block.base, CARD]+block.startIndex, POINTER], block.count, 1]; IF written<=0 THEN { MyCloseBoth[self, FALSE]; ERROR IO.Error[Failure, self]; }; block.count ¬ block.count-written; block.startIndex ¬ block.startIndex+written; ENDLOOP; }; ReadBuffer: PROC [hh: REF MyRec] = TRUSTED { MsgBufferFilled: ENTRY UNSAFE PROC [hh: REF MyRec, n: INT] = { ENABLE UNWIND => NULL; IF n>0 THEN { hh.readerOn ¬ FALSE; hh.out ¬ buffSize; --order, so hh.in and hh.out can be compared unmonitored hh.in ¬ n; hh.out ¬ 0; BROADCAST hh.cond }; }; bytesRead: INT; WHILE (bytesRead ¬ SPPERead[hh.sppe, @hh.buff, buffSize])=0 DO Process.Pause[1]; ENDLOOP; IF bytesRead<0 THEN {hh.buff[0] ¬ 4c; bytesRead ¬ 1}; MsgBufferFilled[hh, bytesRead]; }; InternalTickleReader: INTERNAL PROC [hh: REF MyRec] = { IF ~hh.readerOn THEN { hh.readerOn ¬ TRUE; Process.Detach[FORK ReadBuffer[hh]] }; }; MyGetChar: PROC [self: IO.STREAM] RETURNS [ch: CHAR] = { kill: BOOL ¬ FALSE; EntryGetChar: ENTRY PROC [hh: REF MyRec] RETURNS [ch: CHAR] = { ENABLE UNWIND => NULL; IF hh.lastCh=4C THEN {kill¬TRUE; ch ¬ 4c; RETURN}; WHILE hh.out>=hh.in DO InternalTickleReader[hh]; WAIT hh.cond ENDLOOP; IF hh.out>=hh.in THEN ERROR; hh.lastCh ¬ ch ¬ hh.buff[hh.out]; hh.out¬hh.out+1; }; hh: REF MyRec = NARROW[self.streamData]; ch ¬ EntryGetChar[hh]; IF ch='\r THEN ch ¬ '\n; IF kill THEN {MyCloseBoth[self]; ERROR IO.Error[Failure, self]}; }; MyCharsAvail: PROC [self: IO.STREAM, wait: BOOL] RETURNS [n: INT] = { EntryCharsavail: ENTRY PROC [hh: REF MyRec, wait: BOOL] RETURNS [n: INT] = { ENABLE UNWIND => NULL; n ¬ hh.in-hh.out; IF n>0 THEN RETURN; InternalTickleReader[hh]; IF ~wait THEN RETURN [0]; DO n ¬ hh.in-hh.out; IF n>0 THEN RETURN [n]; InternalTickleReader[hh]; WAIT hh.cond ENDLOOP; }; hh: REF MyRec = NARROW[self.streamData]; IF ~hh.alive OR hh.lastCh=4C THEN RETURN [LAST[INT]]; n ¬ EntryCharsavail[hh, wait]; }; MyCloseBoth: PROC [self: IO.STREAM, abort: BOOL ¬ FALSE] = { SetDead: ENTRY PROC [hh: REF MyRec] RETURNS [was: BOOL¬FALSE] = { ENABLE UNWIND => NULL; IF hh#NIL THEN {was ¬ hh.alive; hh.alive ¬ FALSE}; }; hh: REF MyRec = NARROW[self.streamData]; wasAlive: BOOL ¬ SetDead[hh]; IF wasAlive THEN BridgeDisconnect[hh.sppe]; }; <> SimpleStreams.ImplementCreate[$bridge, BCreateSimpleStreams]; END.