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 =
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.