SimpleStreamsOnBridgeImpl.mesa
Copyright Ó 1988, 1989, 1992 by Xerox Corporation. All rights reserved.
Christian Jacobi, September 2, 1988 2:18:47 pm PDT
Christian Jacobi, March 27, 1992 4:46 pm PST
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
No more necessary...
IncludeSPPEmu: PROC = TRUSTED MACHINE CODE {
--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𡤋ridgeConnect"
};
BridgeDisconnect: PROC [sppe: SPPEmu] = TRUSTED MACHINE CODE {
--Disconnect Bridge session.
--Errors are dealt with silently.
"XR𡤋ridgeDisconnect"
};
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𡤎SPPENDREC 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];
};
IncludeSPPEmu[];
SimpleStreams.ImplementCreate[$bridge, BCreateSimpleStreams];
END.