<> << Created by: Neil Gunther on March 7, 1985 12:25:12 pm PST Last edited by Neil Gunther, March 28, 1985 7:52:45 pm PST>> DIRECTORY Ascii, Basics USING [BITAND], Commander USING [CommandProc, Lookup, Register], CommandExtras USING [MakeUninterpreted], CommandTool USING [ArgumentVector, Failed, Parse], EditedStream USING [SetEcho], FS USING [Error, StreamOpen], Graphics USING [Context, DrawBox, SetPaintMode], GraphicsBasic, IO, IOClasses USING [CreateDribbleOutputStream], List USING [Length, Remove], Loader USING [BCDBuildTime], Menus USING [AppendMenuEntry, CreateEntry, FindEntry, MenuEntry, MenuProc, ReplaceMenuEntry], Process USING [Detach, MsecToTicks, Pause, SecondsToTicks], PupDefs USING [PupAddress, PupPackageMake, PupPackageDestroy], PupStream USING [ConsumeMark, GetPupAddress, PupByteStreamCreate, PupNameTrouble, SecondsToTocks, SendMark, StreamClosing, TimeOut], PupTypes USING [telnetSoc], Rope USING [Cat, Fetch, Find, Length, Match, ROPE, Substr], TiogaOps USING [GetCaret, GetRope, Location], TIPUser USING [InstantiateNewTIPTable, RegisterTIPPredicate, TIPPredicate, TIPTable], TypeScript USING [BackSpace, ChangeLooks, Create, InsertCharAtFrontOfBuffer, TS], UserCredentials USING [Get], ViewerClasses, ViewerEvents USING [EventProc, EventRegistration, RegisterEventProc, UnRegisterEventProc], ViewerIO USING [CreateViewerStreams], ViewerOps USING [AcquireContext, AddProp, DestroyViewer, FetchProp, PaintViewer, ReleaseContext, XYWH2Box], ViewerTools USING [GetSelectedViewer, GetSelectionContents, SetSelection]; VMEBug: CEDAR MONITOR LOCKS h.LOCK USING h: Handle IMPORTS Ascii, Basics, Commander, CommandExtras, CommandTool, EditedStream, FS, Graphics, IO, IOClasses, List, Loader, Menus, Process, PupDefs, PupStream, Rope, TiogaOps, TIPUser, TypeScript, UserCredentials, ViewerEvents, ViewerIO, ViewerOps, ViewerTools SHARES Menus, ViewerClasses, ViewerOps = BEGIN Handle: TYPE = REF VMEBugInstanceRecord; State: TYPE = {idle, starting, running, closing, destroy}; DisconnectChar: CHAR = 220C; AbortChar: CHAR = 221C; ConnectChar: CHAR = 222C; LoginChar: CHAR = 223C; RemoteCloseChar: CHAR = 224C; MarkByte: TYPE = [0..256); setLineWidth: MarkByte = 2; setPageLength: MarkByte = 3; timingMark: MarkByte = 5; timingMarkReply: MarkByte = 6; VMEBugInstanceRecord: TYPE = MONITORED RECORD [ ts: TypeScript.TS, -- the primary typescript state: State _ idle, lorc: CHAR _ 'c, logFileName: Rope.ROPE, logStream: IO.STREAM, keyFile: Rope.ROPE, argv: CommandTool.ArgumentVector, pleaseStop: BOOL _ FALSE, uToSStopped: BOOL _ FALSE, sToUStopped: BOOL _ FALSE, inDestroy: BOOL _ FALSE, serverToUserProcess: PROCESS, userToServerProcess: PROCESS, serverName: Rope.ROPE _ "LittleWonder", useOldHost: BOOL _ FALSE, keyStream: IO.STREAM, destroyOnClose: BOOL _ FALSE, synchronous: BOOL _ FALSE, in: IO.STREAM, origOut: IO.STREAM, out: IO.STREAM, tipTable: TIPUser.TIPTable, serverStream: IO.STREAM _ NIL, oldSplit: Menus.MenuEntry _ NIL ]; logFileNumber: INT _ 0; chatInstanceList: LIST OF REF ANY _ NIL; destroyEvent: ViewerEvents.EventRegistration _ NIL; closeEvent: ViewerEvents.EventRegistration _ NIL; chatTipTable: TIPUser.TIPTable; flushCount: NAT _ 10; ServerToUser: PROC [h: Handle] = { This procedure is FORKed by the UserToServer process via StartUp at the time a <> <> aborting: SIGNAL = CODE; c: CHAR; mySST: MarkByte; GetServerChar: PROC [h: Handle] RETURNS [c: CHAR] = { RETURN[LOOPHOLE[Basics.BITAND[LOOPHOLE[h.serverStream.GetChar[ ! PupStream.TimeOut => { IF h.pleaseStop OR h.state # running THEN SIGNAL aborting ELSE RESUME}; ], CARDINAL], 177B], CHAR]]; }; BugPrompt: PROC [h: Handle] RETURNS [isPrompt: BOOL _ TRUE] = { DO IF (c _ GetServerChar[h]) = 125C THEN { h.out.PutChar[c]; IF (c _ GetServerChar[h]) = 124C THEN { h.out.PutChar[c]; IF (c _ GetServerChar[h]) = 117C THEN { h.out.PutChar[c]; IF (c _ GetServerChar[h]) = 122C THEN { h.out.PutChar[c]; UNTIL (c _ GetServerChar[h]) = '> DO h.out.PutChar[c]; ENDLOOP; h.out.PutChar[c]; -- get trailing SP IF (c _ GetServerChar[h]) # Ascii.SP THEN { h.out.PutChar[c]; RETURN[isPrompt _ FALSE]; }; EXIT; } ELSE {isPrompt _ FALSE; EXIT} } ELSE {isPrompt _ FALSE; EXIT} } ELSE {isPrompt _ FALSE; EXIT} } ELSE {isPrompt _ FALSE; EXIT}; ENDLOOP; h.out.PutChar[c]; }; DO ENABLE { ABORTED => GOTO Cleanup; PupStream.StreamClosing => { IF NOT h.pleaseStop THEN TypeScript.InsertCharAtFrontOfBuffer[ts: h.ts, char: RemoteCloseChar]; GOTO Cleanup}; IO.Error => GOTO Cleanup }; IF h.serverStream.EndOf[] THEN { mySST _ PupStream.ConsumeMark[h.serverStream]; IF mySST = timingMark THEN PupStream.SendMark[h.serverStream, timingMarkReply]; }; IF h.pleaseStop OR h.state # running THEN GOTO Cleanup; c _ GetServerChar[h ! aborting => GOTO Cleanup]; SELECT c FROM '? => {h.out.PutChar[c]; GetLineScript[h]}; -- for assembly 'T => {h.out.PutChar[c]; IF BugPrompt[h] THEN GetLineScript[h]}; -- command Ascii.BEL , Ascii.ControlA, Ascii.BS => TypeScript.BackSpace[h.ts]; Ascii.TAB, Ascii.CR, IN[Ascii.SP..'>], IN['P..'S], IN['U..'~], IN['A..'O] => h.out.PutChar[c]; ENDCASE => NULL; ENDLOOP; EXITS Cleanup => h.sToUStopped _ TRUE; }; -- ServerToUser InitialSetUp: PROC [h: Handle] = TRUSTED { PupStream.SendMark[h.serverStream, setLineWidth]; h.serverStream.PutChar[0C]; IF h.lorc='l OR h.lorc='x THEN { name, password: Rope.ROPE; [name: name, password: password] _ UserCredentials.Get[]; h.serverStream.PutRope[" Login "]; h.serverStream.PutRope[name]; IF h.lorc='l AND Rope.Find[s1: name, s2: "."] = -1 THEN h.serverStream.PutRope[".PA"]; h.serverStream.PutRope[" "]; h.serverStream.PutRope[password]; h.serverStream.PutRope[" \n"]; h.serverStream.Flush[]; }; h.serverStream.PutRope["B"]; -- set baud rate h.serverStream.Flush[]; h.serverStream.PutRope["2400\n"]; h.serverStream.Flush[]; h.serverStream.PutRope["C"]; -- connect h.serverStream.PutRope["\n"]; h.serverStream.Flush[]; }; FinishStringWithErrorMsg: PROC [h: Handle, errorMsg: Rope.ROPE] = { IF errorMsg # NIL THEN h.out.PutF[": %s.\n", IO.rope[errorMsg]] ELSE h.out.PutF[".\n"]; }; <<>> GetLineScript: PROC [h: Handle] = TRUSTED { { Flush: PROC = TRUSTED { h.serverStream.Flush[]; Process.Pause[Process.MsecToTicks[100]]; -- propagate pups }; keyLine: Rope.ROPE; DO IF h.keyStream # NIL AND NOT h.keyStream.EndOf[] THEN { IF h.pleaseStop OR h.state # running THEN GOTO CloseKeyStream; keyLine _ h.keyStream.GetLineRope[]; -- strips CR IF Rope.Length[keyLine] = 0 THEN LOOP; IF Rope.Fetch[keyLine, 0] = '* THEN LOOP -- comment line ELSE { Process.Pause[Process.SecondsToTicks[1]]; -- hang on for a sec! h.serverStream.PutF["%s\n", IO.rope[keyLine]]; Flush[]; RETURN; } } ELSE GOTO CloseKeyStream; ENDLOOP; EXITS CloseKeyStream => { IF h.keyStream # NIL THEN{ h.keyStream.Close[]; h.keyStream _ NIL; }; }; };}; -- GetLineScript StartUp: PROC [h: Handle] = BEGIN ENABLE UNWIND => h.state _ idle; h.state _ starting; IF NOT h.useOldHost OR h.serverName.Length[] = 0 THEN h.serverName _ FindHostName[h]; h.useOldHost _ FALSE; h.pleaseStop _ FALSE; h.sToUStopped _ FALSE; h.uToSStopped _ FALSE; ViewerTools.SetSelection[h.ts, NIL]; TRUSTED { h.out.PutF["\nViewers VMEBug of %t.\n", IO.time[Loader.BCDBuildTime[FindHostName]]]; }; IF h.logStream # NIL THEN h.out.PutF["Log file: %g\n", IO.rope[h.logFileName]] ELSE h.out.PutF["No log file.\n"]; OpenConnection[h]; IF h.serverStream = NIL THEN { h.state _ idle; RETURN; }; SetName[h, Rope.Cat["VMEBug on: ", h.serverName]]; -- in viewer herald InitialSetUp[h]; -- does Login/ Baud rate/ Connect h.state _ running; h.serverToUserProcess _ FORK ServerToUser[h]; END; FindHostName: PROC [h: Handle] RETURNS [host: Rope.ROPE] = { caret: TiogaOps.Location; r: Rope.ROPE; i: INT; r _ ViewerTools.GetSelectionContents[]; IF r.Length[] > 1 THEN RETURN[r]; caret _ TiogaOps.GetCaret[]; r _ TiogaOps.GetRope[caret.node]; i _ caret.where; WHILE i > 0 DO char: CHARACTER = Rope.Fetch[r, i - 1]; IF -- char # '* AND -- NOT VMEBugTokenProc[char] = other THEN EXIT; i _ i-1; ENDLOOP; host _ Rope.Substr[base: r, start: i, len: caret.where - i]; }; VMEBugTokenProc: IO.BreakProc = TRUSTED { <> IF IO.TokenProc[char] = other THEN RETURN [other]; IF char = '+ THEN RETURN [other]; RETURN [sepr]; }; OpenConnection: PROC [h: Handle] = TRUSTED { addr: PupDefs.PupAddress; PupDefs.PupPackageMake[]; { h.out.PutF["Opening connection to %g ... ", IO.rope[h.serverName]]; SELECT TRUE FROM Rope.Match[pattern: "LittleWonder", object: h.serverName, case: FALSE], Rope.Match[pattern: "dls+100017", object: h.serverName, case: FALSE] => { addr _ PupStream.GetPupAddress[PupTypes.telnetSoc, "dls+100017" ! PupStream.PupNameTrouble => { h.out.PutF["PUP name error"]; FinishStringWithErrorMsg[h, e]; GOTO Return; }]; h.serverStream _ PupStream.PupByteStreamCreate [addr, PupStream.SecondsToTocks[1] ! PupStream.StreamClosing => { h.out.PutF["Can't connect"]; FinishStringWithErrorMsg[h, text]; GOTO Return; }]; h.out.PutF["open.\n"]; }; ENDCASE => h.out.PutF["\nUnknown dls address.\n"]; EXITS Return => NULL; }; }; CloseConnection: PROC [h: Handle, print: BOOL] = TRUSTED { h.pleaseStop _ TRUE; IF h.serverStream # NIL THEN { IF print THEN h.out.PutF["\nClosing connection to %s", IO.rope[h.serverName] ! IO.Error => CONTINUE]; h.serverStream.Close[]; h.serverStream _ NIL; -- could cause pointer faults! IF print THEN h.out.PutF[" ... Closed\n" ! IO.Error => CONTINUE]; PupDefs.PupPackageDestroy[]; }; IF h.keyStream # NIL THEN { h.keyStream.Close[]; h.keyStream _ NIL; }; IF h.state = running THEN {JOIN h.serverToUserProcess}; IF h.logStream # NIL THEN h.logStream.Flush[]; h.state _ idle; SetName[h, "VMEBug"]; }; SetName: PROC [h: Handle, r: Rope.ROPE] = { -- in Viewer herald InternalSetName: PROC [v: ViewerClasses.Viewer] = { v.name _ r; ViewerOps.PaintViewer[viewer: v, hint: caption]; }; EnumerateSplits[h.ts, InternalSetName ! ANY => CONTINUE]; }; VMEBugMain: Commander.CommandProc = TRUSTED { -- registered by Init h: Handle _ NEW[VMEBugInstanceRecord _ []]; execOut: IO.STREAM _ cmd.out; switchChar: CHAR; i: NAT _ 2; h.argv _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; CONTINUE; }]; IF h.argv = NIL THEN RETURN; h.lorc _ 'l; -- default GV login WHILE i < h.argv.argc DO IF h.argv[i].Length[] > 1 THEN SELECT h.argv[i].Fetch[0] FROM '- => { switchChar _ h.argv[i].Fetch[1]; SELECT switchChar FROM 'd => h.destroyOnClose _ TRUE; 'k => { IF i+1 < h.argv.argc THEN { h.keyStream _ IO.RIS[h.argv[i+1]]; i _ i + 1; }; }; 's => h.synchronous _ TRUE; ENDCASE => h.lorc _ switchChar; }; '> => h.logFileName _ Rope.Substr[base: h.argv[i], start: 1]; '< => h.keyFile _ Rope.Substr[base: h.argv[i], start: 1]; ENDCASE => execOut.PutF["VMEBug: unknown command: %s.\n", IO.rope[h.argv[i]]]; i _ i + 1; ENDLOOP; IF h.logFileName.Length[] = 0 THEN { h.logFileName _ IO.PutFR["VMEBug%d.log", IO.int[logFileNumber]]; logFileNumber _ logFileNumber + 1; }; IF h.keyFile.Length[] > 0 THEN h.keyStream _ FS.StreamOpen[fileName: h.keyFile ! FS.Error => IF error.group = user THEN { execOut.PutF["VMEBug: Cannot open %s\n", IO.rope[h.keyFile]]; CONTINUE; }]; <> h.ts _ TypeScript.Create[info: [name: "VMEBug", iconic: h.argv.argc <= 1 OR h.lorc = 'i], paint: TRUE]; TypeScript.ChangeLooks[h.ts, 'f]; h.ts.file _ h.logFileName; h.ts.icon _ typescript; <> h.logStream _ FS.StreamOpen[fileName: h.logFileName, accessOptions: $create ! FS.Error => IF error.group = user THEN { execOut.PutF["VMEBug: Cannot open %s\n", IO.rope[h.logFileName]]; CONTINUE; }]; <> chatTipTable.link _ h.ts.tipTable; chatTipTable.opaque _ FALSE; h.tipTable _ h.ts.tipTable _ chatTipTable; [in: h.in, out: h.origOut] _ ViewerIO.CreateViewerStreams[name: "VMEBug.log", viewer: h.ts, editedStream: FALSE]; h.out _ h.origOut; IF h.logStream # NIL THEN h.out _ IOClasses.CreateDribbleOutputStream[output1: h.origOut, output2: h.logStream]; EditedStream.SetEcho[h.in, NIL]; IF h.argv.argc > 1 THEN { h.serverName _ h.argv[1]; h.useOldHost _ TRUE; <> IF h.lorc = 'i THEN h.lorc _ 'c ELSE TypeScript.InsertCharAtFrontOfBuffer[ts: h.ts, char: IF h.lorc = 'c THEN ConnectChar ELSE LoginChar]; }; IF List.Length[chatInstanceList] = 0 THEN { <> destroyEvent _ ViewerEvents.RegisterEventProc[proc: MyDestroy, event: destroy] }; chatInstanceList _ CONS[h, chatInstanceList]; Menus.AppendMenuEntry[menu: h.ts.menu, entry: Menus.CreateEntry[name: "Disconnect", proc: MyDisconnect, clientData: h, fork: TRUE, documentation: "Close Ethernet connection"]]; Menus.AppendMenuEntry[menu: h.ts.menu, entry: Menus.CreateEntry[name: "Login", proc: MyLogin, clientData: h, documentation: "Open Ethernet Connection to selected host and send Login sequence"]]; Menus.AppendMenuEntry[menu: h.ts.menu, entry: Menus.CreateEntry[name: "Connect", proc: MyConnect, clientData: h, documentation: "Open Ethernet Connection to selected host"]]; Menus.AppendMenuEntry[menu: h.ts.menu, entry: Menus.CreateEntry[name: "BreakKey", proc: MyBreakKey, clientData: h, documentation: "Transmit Ascii.NULL (DLS interprets as RS-232 Line Break)"]]; Menus.AppendMenuEntry[menu: h.ts.menu, entry: Menus.CreateEntry[name: "FlushLog", proc: MyFlushLog, clientData: h, documentation: "Flush log file to disk."]]; h.oldSplit _ Menus.FindEntry[menu: h.ts.menu, entryName: "Split"]; Menus.ReplaceMenuEntry[menu: h.ts.menu, oldEntry: h.oldSplit, newEntry: Menus.CreateEntry[name: "Split", proc: MySplit, fork: TRUE, clientData: h, documentation: "Split window"]]; ViewerOps.AddProp[h.ts, $VMEBugToolData, h]; ViewerOps.PaintViewer[viewer: h.ts, hint: all]; IF h.synchronous THEN UserToServer[h] ELSE { h.userToServerProcess _ FORK UserToServer[h]; Process.Detach[h.userToServerProcess]; }; }; -- VMEBugMain UserToServer: PROC [h: Handle] = TRUSTED {{ << Watches viewer buttons & keyboard>> char: CHAR; count: NAT _ 0; DO ENABLE { ABORTED => ERROR; -- ever happen? IO.Error => { <> <> CloseConnection[h, FALSE]; GOTO Die; }; PupStream.StreamClosing => { IF h.serverStream # NIL THEN { h.out.PutF["\nConnection being closed by %s", IO.rope[h.serverName]]; FinishStringWithErrorMsg[h, text]; CloseConnection[h, FALSE]; IF h.destroyOnClose THEN { ViewerOps.DestroyViewer[h.ts]; GOTO Die; }; }; CONTINUE; }; }; char _ h.in.GetChar[]; IF h.inDestroy THEN { CloseConnection[h, FALSE]; GOTO Die; }; SELECT char FROM AbortChar => {IF h.state = running THEN CloseConnection[h, TRUE]}; DisconnectChar => {IF h.state = running THEN CloseConnection[h, TRUE]}; RemoteCloseChar => {ERROR PupStream.StreamClosing[why: remoteClose, text: NIL]}; ConnectChar => {IF h.state = idle THEN { h.lorc _ 'c; StartUp[h]; count _ 0}; }; LoginChar => { IF h.state = idle THEN { h.lorc _ 'l; StartUp[h]; count _ 0}; }; ENDCASE => { char _ Ascii.Upper[char]; SELECT h.state FROM running => { h.serverStream.PutChar[char]; IF count >= flushCount OR h.in.CharsAvail[] = 0 THEN { h.serverStream.Flush[]; count _ 0} ELSE count _ count + 1; }; ENDCASE => h.out.PutChar[char]; }; ENDLOOP; EXITS Die => {IF h.logStream # NIL THEN h.logStream.Close[]}; }; }; -- end of UserToServer MyDestroy: ViewerEvents.EventProc = { h: Handle _ NARROW[ViewerOps.FetchProp[viewer, $VMEBugToolData]]; IF h = NIL THEN RETURN; IF NumSplit[viewer] = 1 THEN { -- last one h.inDestroy _ TRUE; chatInstanceList _ List.Remove[h, chatInstanceList]; IF List.Length[chatInstanceList] = 0 THEN { ViewerEvents.UnRegisterEventProc[proc: destroyEvent, event: destroy]; <> }; } ELSE IF NumSplit[viewer] > 1 THEN { IF viewer = h.ts THEN { Another: PROC [v: ViewerClasses.Viewer] = { IF v # viewer THEN h.ts _ v; }; EnumerateSplits[viewer, Another]; }; }; }; MyClose: ViewerEvents.EventProc = { h: Handle _ NARROW[ViewerOps.FetchProp[viewer, $VMEBugToolData]]; IF h = NIL THEN RETURN; TypeScript.InsertCharAtFrontOfBuffer[ts: h.ts, char: DisconnectChar]; }; MyConnect: Menus.MenuProc = { viewer: TypeScript.TS _ NARROW[parent]; h: Handle _ NARROW[clientData]; h.ts _ viewer; -- "primary" copy h.useOldHost _ mouseButton # red; TypeScript.InsertCharAtFrontOfBuffer[ts: h.ts, char: ConnectChar]; }; MyLogin: Menus.MenuProc = { viewer: TypeScript.TS _ NARROW[parent]; h: Handle _ NARROW[clientData]; h.ts _ viewer; -- "primary" copy h.useOldHost _ mouseButton # red; TypeScript.InsertCharAtFrontOfBuffer[ts: h.ts, char: LoginChar]; }; MyDisconnect: Menus.MenuProc = { h: Handle _ NARROW[clientData]; TypeScript.InsertCharAtFrontOfBuffer[ts: h.ts, char: DisconnectChar]; }; MyBreakKey: Menus.MenuProc = TRUSTED { h: Handle _ NARROW[clientData]; IF h.state # running THEN RETURN; IF h.serverStream # NIL THEN { h.serverStream.PutChar['\000]; h.serverStream.Flush[]; }; }; MyFlushLog: Menus.MenuProc = { h: Handle _ NARROW[clientData]; IF h.logStream # NIL THEN h.logStream.Flush[]; }; MySplit: Menus.MenuProc = { h: Handle _ NARROW[clientData]; CheckVMEBugProperties: PROC [v: ViewerClasses.Viewer] = { IF ViewerOps.FetchProp[v, $VMEBugToolData] = NIL THEN ViewerOps.AddProp[v, $VMEBugToolData, h]; v.tipTable _ h.tipTable; }; h.oldSplit.proc[parent: parent, clientData: h.oldSplit.clientData, mouseButton: mouseButton, shift: shift, control: control]; EnumerateSplits[NARROW[parent, ViewerClasses.Viewer], CheckVMEBugProperties]; }; ConnectionOpen: TIPUser.TIPPredicate = { h: Handle; viewer: ViewerClasses.Viewer _ ViewerTools.GetSelectedViewer[]; IF viewer=NIL THEN RETURN [FALSE]; -- no primary selection h _ NARROW[ViewerOps.FetchProp[viewer, $VMEBugToolData]]; IF h=NIL THEN RETURN [FALSE]; -- not a chat-like tool RETURN [h.state = running]; -- connection open? }; EnumerateSplits: PROC [v: ViewerClasses.Viewer, p: PROC [v: ViewerClasses.Viewer]] = { v2: ViewerClasses.Viewer _ v; IF v = NIL THEN RETURN; DO p[v2]; IF v2.link = NIL OR v2.link = v THEN RETURN; v2 _ v2.link; ENDLOOP; }; NumSplit: PROC [v: ViewerClasses.Viewer] RETURNS [count: INT _ 0] = { Counter: PROC [v2: ViewerClasses.Viewer] = { count _ count + 1; }; EnumerateSplits[v, Counter]; }; Init: PROC = { Commander.Register[key: "VMEBug", proc: VMEBugMain, doc: "Pup User Telnet, see VMEBugDoc.tioga"]; CommandExtras.MakeUninterpreted[Commander.Lookup["VMEBug"]]; chatTipTable _ TIPUser.InstantiateNewTIPTable["VMEBug.TIP"]; TIPUser.RegisterTIPPredicate[$ConnectionOpen, ConnectionOpen]; }; <> Init[]; END... of VMEBug