BridgeRTTYImpl.mesa
Copyright Ó 1987, 1988, 1989, 1992 by Xerox Corporation. All rights reserved.
Demers, May 16, 1990 1:08 pm PDT
Bill Jackson (bj) April 6, 1987 6:17:24 pm PDT
Christian Le Cocq September 29, 1987 4:57:07 pm PDT
Pavel, October 6, 1987 5:42:12 pm PDT
Peter B. Kessler May 26, 1989 10:53:43 am PDT
Eduardo Pelegri-Llopart October 7, 1988 10:28:15 am PDT
Russ Atkinson (RRA) May 27, 1988 7:15:03 pm PDT
Carl Hauser, August 30, 1988 11:30:43 am PDT
Tim Diebert: September 13, 1989 8:51:34 am PDT
Willie-s, November 9, 1992 1:18 pm PST
Michael Plass, March 10, 1992 4:35 pm PST
Last tweaked by Mike Spreitzer April 14, 1992 1:09 pm PDT
DIRECTORY
Ascii,
Atom,
BridgeExec,
BridgeRTTY,
Convert,
EditedStream,
Icons,
IO,
Menus,
MessageWindow,
NetworkStream,
PFS,
Process,
Rope,
TiogaOps,
TIPLinking,
TIPUser,
TypeScript,
UserProfile,
ViewerClasses,
ViewerEvents,
ViewerIO,
ViewerOps,
ViewerTools
;
BridgeRTTYImpl: CEDAR MONITOR
IMPORTS Atom, BridgeExec, Convert, EditedStream, Icons, IO, Menus, MessageWindow, NetworkStream, PFS, Process, Rope, TiogaOps, TIPLinking, TIPUser, TypeScript, UserProfile, ViewerEvents, ViewerIO, ViewerOps, ViewerTools
EXPORTS BridgeRTTY
~ {
<<
XRPrintF: PROC [fmt, msg: CARD32] ~ TRUSTED MACHINE CODE { "XR←PrintF" };
XRCharStarFromROPE: PROC [r: Rope.ROPE] RETURNS [CARD32] ~ TRUSTED MACHINE CODE { "XR𡤌harStarFromRope" };
DBMsg: PROC [r: Rope.ROPE] ~ {
fmt: CARD32 ¬ XRCharStarFromROPE["%s"];
msg: CARD32 ¬ XRCharStarFromROPE[r];
XRPrintF[fmt, msg];
};
>>
ExcuseMe: PROCEDURE [excuse: ROPE] RETURNS [] ~ {
MessageWindow.Append[message: excuse, clearFirst: TRUE];
MessageWindow.Blink[];
};
Procs for BridgeRTTY
UnknownTransition: PUBLIC ERROR ~ CODE;
ChangeState: PUBLIC ENTRY PROC [instance: BridgeRTTY.PrivateInstance, new: BridgeRTTY.CaveState] ~ {
ENABLE { UNWIND => NULL };
SELECT BridgeRTTY.CaveStateCross [instance.caveState, new] FROM
[dead, starting] => { instance.caveState ¬ starting };
[starting, open] => { instance.caveState ¬ opening };
[opening, open] => { instance.caveState ¬ open };
[open, closed] => { instance.caveState ¬ closing };
[open, winner] => { instance.caveState ¬ closing };
[closing, closed] => { instance.caveState ¬ closed };
[closing, winner] => { instance.caveState ¬ closed };
[closed, closed] => { instance.caveState ¬ winner }; -- kicked by Detroy
[closed, winner] => { instance.caveState ¬ winner };
ENDCASE =>  RETURN WITH ERROR UnknownTransition;
};
ShutDown: PUBLIC PROC [instance: BridgeRTTY.PrivateInstance]
RETURNS [yes: BOOL ¬ TRUE] ~ {
BridgeExec.DestroyInstance[session: instance.session, instance: instance, clientData: NIL];
IF ( instance.consumeUser # NIL ) THEN TRUSTED {
Process.Abort[instance.consumeUser];
Process.Detach[instance.consumeUser];
instance.consumeUser ¬ NIL
};
IF ( instance.consumeService # NIL ) THEN TRUSTED {
Process.Abort[instance.consumeService];
Process.Detach[instance.consumeService];
instance.consumeService ¬ NIL
};
Process.PauseMsec[ms: 500];
yes ¬ ( ( instance.caveState = closed ) OR ( instance.caveState = winner ) );
};
Types
ROPE: TYPE ~ Rope.ROPE;
NetworkStreamPair: TYPE ~ BridgeExec.NetworkStreamPair;
Session: TYPE ~ BridgeExec.Session;
Magic stuff
RTTY: ROPE ¬ "RTTY";
RTTYICONS: ROPE ¬ "Rtty.icons"; -- the rest --
RTTYLOG: ROPE ~ "-vux:/tmp/Rtty.Log$";
RTTYOPEN: ATOM ~ $RttyOpen;
RTTYPROP: ATOM ~ $RttyProp;
RTTYTIP: ROPE ~ "Rtty.tip";
InstanceProperty: ATOM ~ $RTTYInstance;
Global State
tipTable: TIPUser.TIPTable;
rttyIcon: Icons.IconFlavor ¬ unInit;
BridgeExec Procs
Destroy: PUBLIC BridgeExec.DestroyProc ~ {
privateInstance: BridgeRTTY.PrivateInstance ¬ NARROW [instance];
ChangeState[privateInstance, winner];
BridgeComm.CloseConnection[privateInstance.s]; now in BridgeExec
};
BridgeExec Procs for TypeScripts
RttyCreate: PUBLIC PROC [nsp: NetworkStreamPair, args: ROPE, session: Session, clientData: REF] RETURNS [instance: BridgeRTTY.PrivateInstance] ~ {
transportClass: ATOM;
transportName: Rope.ROPE;
banner: Rope.ROPE;
Append: PROC [name: ROPE, proc: Menus.MenuProc, doc: ROPE] ~ {
Menus.AppendMenuEntry[instance.typescript.menu, Menus.CreateEntry[name, proc, instance, doc]]
};
transportClass ¬ NetworkStream.GetStreamInfo[nsp.in].protocolFamily;
transportName ¬ Rope.Cat[" (", Convert.RopeFromAtom[transportClass], ")"];
banner ¬ Rope.Cat[RTTY, ": ", BridgeExec.SessionNameFromSession[session: session], transportName];
instance ¬ NEW [BridgeRTTY.PrivateInstanceObject ¬ [args~args, nsp~nsp, session~session, caveState~dead]];
instance.typescript ¬ TypeScript.Create[info: [
tipTable: tipTable,
name: banner,
icon: rttyIcon,
iconic: FALSE, -- open the viewer at the beginning --
label: BridgeExec.SessionNameFromSession[session: session],
guardDestroy: FALSE,
props: Atom.PutPropOnList[NIL, RTTYPROP, instance]]];
Append["Disconnect!", MyDisconnect, "Close Ethernet connection"];
Append["FlushLog!", MyFlushLog, "Flush Typescript to disk"];
Append["Interrupt!", MyInterrupt, "Try to expedite an interrupt"];
[] ¬ ViewerEvents.RegisterEventProc[proc: ViewerEventDestroyProc,
event: ViewerEvents.ViewerEvent.destroy, filter: instance.typescript, before: TRUE];
ViewerOps.AddProp[viewer: instance.typescript, prop: InstanceProperty, val: instance];
IF UserProfile.Boolean["Bridge.RTTY.BackingFile", TRUE] THEN
[instance.in, instance.out] ¬ ViewerIO.CreateViewerStreams[RTTY, instance.typescript, RTTYLOG, FALSE]
ELSE [instance.in, instance.out] ¬ ViewerIO.CreateViewerStreams[RTTY, instance.typescript, NIL, FALSE];
EditedStream.SetEcho[instance.in, NIL];
TiogaOps.SetNodeStyle["ascii", TiogaOps.ViewerDoc[instance.typescript]];
TypeScript.ChangeLooks[instance.typescript, 'i];
TypeScript.ChangeLooks[instance.typescript, 'b];
TypeScript.ChangeLooks[instance.typescript, 't];
TypeScript.PutRope[instance.typescript, "Bridge RTTY\n"];
TypeScript.ChangeLooks[instance.typescript, 'T];
TypeScript.ChangeLooks[instance.typescript, 'B];
TypeScript.ChangeLooks[instance.typescript, 'I];
TypeScript.PutRope[instance.typescript, "\n"];
TypeScript.Flush[instance.typescript];
ChangeState[instance, starting];
instance.consumeUser ¬ FORK UserInput[instance, instance.in, instance.nsp];
instance.consumeService ¬ FORK ServiceOutput[instance, instance.nsp, instance.out];
instance.typescript.guardDestroy ¬ TRUE;
ViewerOps.PaintViewer[viewer: instance.typescript, hint: caption];
};
ViewerEventDestroyProc: PUBLIC ViewerEvents.EventProc ~ {
PROC [viewer: Viewer, event: ViewerEvent, before: BOOL] RETURNS [abort: BOOLFALSE]
ok: BOOL;
instance: BridgeRTTY.PrivateInstance ¬ NARROW[ViewerOps.FetchProp[viewer: viewer, prop: InstanceProperty]];
IF event # destroy OR before # TRUE THEN ERROR;
ok ¬ ShutDown[instance];
RETURN [abort: NOT ok];
};
Pipe Processes for TypeScripts
UserInput: PROC [instance: BridgeRTTY.PrivateInstance, in: IO.STREAM, nsp: NetworkStreamPair] ~ {
Accept keystrokes from the user and dump them out onto the network. We don't flush if there are lots of characters coming (like from shift-select) so that we gets lots of them into the buffer. The flush will happen just before we wait for the next character to be typed.
ENABLE {
ABORTED => { GOTO Finished };
IO.Error => { GOTO Finished };
NetworkStream.Error => { GOTO Finished };
};
ch: CHAR;
out: IO.STREAM ¬ nsp.out;
ChangeState[instance, open];
DO
IF ( IO.CharsAvail[in] = 0 ) THEN {
NetworkStream.SendEndOfMessage[out];
NetworkStream.SendSoon[out, 0];
};
SELECT instance.caveState FROM
closing, closed => { GOTO Finished };
ENDCASE;
ch ¬ IO.GetChar[in];
SELECT ch FROM
Ascii.TAB, Ascii.LF, Ascii.CR, IN [Ascii.SP..0176C] => IO.PutChar[out, ch];
ENDCASE => IO.PutChar[out, ch]; -- NULL?
ENDLOOP;
EXITS
Finished => {
ChangeState[instance, closed];
IF instance.typescript.destroyed
should use an error signal from the typescript (which does not work)
THEN {
ExcuseMe[IO.PutFR1["Bridge RTTY local close - %g", IO.rope[BridgeExec.SessionNameFromSession[instance.session]]]];
}
ELSE {
TypeScript.ChangeLooks[instance.typescript, 'b];
TypeScript.ChangeLooks[instance.typescript, 't];
TypeScript.PutRope[instance.typescript, "\n{local close}"];
TypeScript.ChangeLooks[instance.typescript, 'T];
TypeScript.ChangeLooks[instance.typescript, 'B];
TypeScript.Flush[instance.typescript];
};
IF (instance.consumeService # NIL) THEN TRUSTED {
Process.Abort[instance.consumeService];
};
kick the other guy in the pants!
BridgeExec.DestroyInstance[session: instance.session, instance: instance, clientData: NIL];
SELECT instance.caveState FROM
closed, winner => {
instance.typescript.guardDestroy ¬ FALSE;
ViewerOps.PaintViewer[viewer: instance.typescript, hint: caption];
};
ENDCASE;
};
};
flushInterval: INT ¬ 250; -- milliseconds
OutputFlusher: PROC [instance: BridgeRTTY.PrivateInstance, out: IO.STREAM] ~ {
WHILE NOT instance.stopFlushing DO
Process.PauseMsec[flushInterval];
IF instance.outChanged THEN {
IO.Flush[out];
instance.outChanged ¬ FALSE;
};
ENDLOOP;
};
ServiceOutput: PROC [instance: BridgeRTTY.PrivateInstance, nsp: NetworkStreamPair, out: IO.STREAM] ~ {
Accept bytes from the service and dump them out onto the typescript. Remember that EndOfStream doesn't mean what we want for XNSStreamEmulators, so we just skip over EOM's with NetworkStream.GetStreamState.
ENABLE {
ABORTED => { GOTO Finished };
IO.Error => { GOTO Finished };
NetworkStream.Error => { GOTO Finished };
};
ch: CHAR;
in: IO.STREAM ¬ nsp.in;
ssType: NetworkStream.SubStreamType ¬ 0;
atEndOfStream: BOOL ¬ FALSE;
ChangeState[instance, open];
IF UserProfile.Boolean[key: "Bridge.RTTY.GrabInputFocus", default: TRUE] THEN {
ViewerTools.SetSelection[instance.typescript, NIL]; -- grab the input focus --
};
TRUSTED { Process.Detach[FORK OutputFlusher[instance, out]] };
DO
IF atEndOfStream THEN {
[subStreamType~ssType] ¬ NetworkStream.GetStreamState[in, TRUE];
IF ( ssType # 0 ) THEN {
[] ← XNSStreamEmulator.SendCloseReply[in];
GOTO Finished;
};
};
SELECT instance.caveState FROM
closing, closed => { GOTO Finished };
ENDCASE;
ch ¬ IO.GetChar[ in
! IO.EndOfStream => { atEndOfStream ¬ TRUE; LOOP } ];
atEndOfStream ¬ FALSE;
SELECT ch FROM
Ascii.BEL => ViewerOps.BlinkViewer[viewer: instance.typescript, milliseconds: 50];
Ascii.BS => TypeScript.BackSpace[instance.typescript];
Ascii.TAB, Ascii.LF, IN [Ascii.SP..0176C] => IO.PutChar[out, ch];
ENDCASE => NULL;
instance.outChanged ¬ TRUE;
ENDLOOP;
EXITS
Finished => {
ChangeState[instance, closed];
IF instance.typescript.destroyed
should use an error signal from the typescript (which does not work)
THEN {
ExcuseMe[IO.PutFR1["Bridge RTTY remote close - %g", IO.rope[BridgeExec.SessionNameFromSession[instance.session]]]];
}
ELSE {
TypeScript.ChangeLooks[instance.typescript, 'b];
TypeScript.ChangeLooks[instance.typescript, 't];
TypeScript.PutRope[instance.typescript, "\n{remote close}"];
TypeScript.ChangeLooks[instance.typescript, 'T];
TypeScript.ChangeLooks[instance.typescript, 'B];
TypeScript.Flush[instance.typescript];
};
instance.stopFlushing ¬ TRUE;
IF (instance.consumeUser # NIL) THEN TRUSTED {
Process.Abort[instance.consumeUser];
};
kick the other guy in the pants!
BridgeExec.DestroyInstance[session: instance.session, instance: instance, clientData: NIL];
SELECT instance.caveState FROM
closed, winner => {
instance.typescript.guardDestroy ¬ FALSE;
ViewerOps.PaintViewer[viewer: instance.typescript, hint: caption];
};
ENDCASE;
};
};
Menu Procs
MyDisconnect: Menus.MenuProc ~ {
instance: BridgeRTTY.PrivateInstance ¬ NARROW[clientData];
ok: BOOL ¬ ShutDown[instance];
IF (NOT ok) THEN {
ExcuseMe["Bridge RTTY - inconsistent shutdown"];
};
};
MyFlushLog: Menus.MenuProc ~ {
instance: BridgeRTTY.PrivateInstance ¬ NARROW[clientData];
};
MyInterrupt: Menus.MenuProc ~ TRUSTED {
instance: BridgeRTTY.PrivateInstance ¬ NARROW[clientData];
NULL;
};
TIP Procs
RttyOpen: PROC RETURNS [yes: BOOL] ~ {
h: BridgeRTTY.PrivateInstance;
viewer: ViewerClasses.Viewer;
IF (viewer ¬ ViewerTools.GetSelectedViewer[]) = NIL THEN RETURN[FALSE];
IF (h ¬ NARROW [ViewerOps.FetchProp[viewer, RTTYPROP]]) = NIL THEN RETURN[FALSE];
RETURN[TRUE];
};
Initialization
Init: PROC ~ {
temp: TIPUser.TIPTable ¬ ViewerOps.FetchViewerClass[$Typescript].tipTable;
tipTable ¬ TIPUser.InstantiateNewTIPTable[ RTTYTIP
! PFS.Error, TIPUser.InvalidTable => CONTINUE ];
IF tipTable = NIL
THEN {
tipTable ¬ temp;
}
ELSE {
[] ← TIPLinking.Append[tipTable, temp];
TIPUser.RegisterTIPPredicate[RTTYOPEN, RttyOpen];
};
rttyIcon ¬ Icons.NewIconFromFile[file: RTTYICONS, n: 0
! PFS.Error => CONTINUE];
BridgeExec.Register[RTTY, RttyCreate, NIL, Destroy];
};
Init[];
}...