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_CharStarFromRope" }; 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[]; }; 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 ) ); }; ROPE: TYPE ~ Rope.ROPE; NetworkStreamPair: TYPE ~ BridgeExec.NetworkStreamPair; Session: TYPE ~ BridgeExec.Session; 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; tipTable: TIPUser.TIPTable; rttyIcon: Icons.IconFlavor ¬ unInit; Destroy: PUBLIC BridgeExec.DestroyProc ~ { privateInstance: BridgeRTTY.PrivateInstance ¬ NARROW [instance]; ChangeState[privateInstance, winner]; }; 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 ~ { 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]; }; UserInput: PROC [instance: BridgeRTTY.PrivateInstance, in: IO.STREAM, nsp: NetworkStreamPair] ~ { 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 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]; }; 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] ~ { 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 { 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 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]; }; 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; }; }; 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; }; 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]; }; 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[]; }... ΄ 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 Procs for BridgeRTTY Types Magic stuff Global State BridgeExec Procs BridgeComm.CloseConnection[privateInstance.s]; now in BridgeExec BridgeExec Procs for TypeScripts PROC [viewer: Viewer, event: ViewerEvent, before: BOOL] RETURNS [abort: BOOL _ FALSE] Pipe Processes for TypeScripts 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. should use an error signal from the typescript (which does not work) kick the other guy in the pants! 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. [] _ XNSStreamEmulator.SendCloseReply[in]; should use an error signal from the typescript (which does not work) kick the other guy in the pants! Menu Procs TIP Procs Initialization ΚK•NewlineDelimiter –(cedarcode) style™code™Kšœ ΟeœC™NKšœ ™ Kšœ.™.K™3K™%K™-K™7K™/K™,K™.K™&K™)K™9K˜šΟk ˜ K˜Kšœ˜Kšœ ˜ Kšœ ˜ Kšœ˜Kšœ ˜ Kšœ˜Kšž˜Kšœ˜K˜K˜Kšžœ˜Kšœ˜Kšœ˜Kšœ ˜ K˜ Kšœ˜Kšœ ˜ Kšœ ˜ Kšœ ž˜Kšœ ˜ Kšœž˜ Kšœ ˜ Kšœ ˜ Kšœ˜K˜——šΠlnœžœž˜Kšžœ2žœ'žœw˜άKšžœ ˜šœ˜K˜K˜KšΟnœžœ žœ)˜IKš œžœ žœžœžœžœžœžœ˜jš œžœ žœ˜Kšœžœ˜'Kšœžœ˜$K˜K˜K˜K˜—š œž œ žœžœ˜1Kšœ2žœ˜8K˜K˜——K˜™K™Kš œž œžœ˜'K˜š  œžœžœžœF˜dKšžœžœžœ˜šžœ5ž˜?K˜6K˜5K˜1K˜3K˜3K˜5K˜5Kšœ5Οc˜HK˜4Kšžœžœžœžœ˜0—Kšœ˜—K™š  œžœžœ(žœžœžœ˜[K•StartOfExpansionW[session: BridgeExec.Session, instance: BridgeExec.Instance, clientData: REF ANY]šœVžœ˜[šžœžœžœžœ˜0Kšœ$˜$Kšœ%˜%Kšœž˜Kšœ˜—šžœžœžœžœ˜3Kšœ'˜'Kšœ(˜(Kšœž˜Kšœ˜—K–[ticks: PrincOps.Ticks]šœ˜Kšœ(žœ#˜MK˜—K™—™Kšž™Kšžœžœžœ˜Kšœžœ ˜7Kšœ žœ˜#K˜—šœ ™ K™KšΠbkœžœ ˜Kš’ œžœ‘˜.Kš’œžœ˜&Kš’œžœ ˜Kš’œžœ ˜Kš’œžœ˜K˜Kš œžœ˜'K˜—™ K™K˜K˜$K˜—šœ™K™š œžœ˜*Kšœ.žœ ˜@Kšœ%˜%Kšœ@™@Kšœ˜K˜——šœ ™ K™š   œž œ žœ žœžœ+˜’Kšœžœ˜Kšœžœ˜Kšœ žœ˜š œžœžœžœ˜>Kšœ]˜]Kšœ˜—K˜DK˜JKšœ’œL˜bKšœ žœ\˜j˜/Kšœ˜Kšœ˜Kšœ˜Kšœžœ‘&˜5Kšœ;˜;Kšœžœ˜Kšœ’œ˜5—KšœA˜AKšœ<˜šž˜šžœžœ˜Kšœ:žœ˜@šžœžœ˜Kšœ*™*Kšžœ ˜K˜—K˜—šžœž˜Kšœžœ ˜%Kšžœ˜—Kš œžœžœ"žœžœ˜IKšœžœ˜šžœž˜KšœžœI˜RKšœžœ.˜6Kš œžœžœžœžœ žœ˜AKšžœžœ˜—Kšœžœ˜Kšžœ˜—šž˜šœ ˜ Kšœ˜Kšžœ˜ š‘D™Dšžœ˜Kšœ žœ)žœ=˜sKšœ˜—šžœ˜Kšœ0˜0Kšœ0˜0Kšœ<˜