<<>> <> <> <> <> <> <> <> <> <> <> <> <> <> <> 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 { <<[] _ 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 <> 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[]; }...