VMEBug.mesa
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
connection is opened. It is JOINED whenever the connection is closed.
It also goes away if the VMEBug viewer is destroyed.
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 {
This PROC includes '+ ,to handle Pup names of the form dls+someOctalValue
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;
}];
iconic unless command line not empty
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;
create log file
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;
}];
plug in VMEBug TIP table.
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 -i then we are just initializing the host name, else really connect
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 {
closeEvent ← ViewerEvents.RegisterEventProc[proc: MyClose, event: close];
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 => {
last viewer destroyed !
Close connection and exit
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];
ViewerEvents.UnRegisterEventProc[proc: closeEvent, event: close];
};
}
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];
};
M a i n l i n e C o d e
Init[];
END... of VMEBug