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: BOOLFALSE,
uToSStopped: BOOLFALSE,
sToUStopped: BOOLFALSE,
inDestroy: BOOLFALSE,
serverToUserProcess: PROCESS,
userToServerProcess: PROCESS,
serverName: Rope.ROPE ← "LittleWonder",
useOldHost: BOOLFALSE,
keyStream: IO.STREAM,
destroyOnClose: BOOLFALSE,
synchronous: BOOLFALSE,
in: IO.STREAM,
origOut: IO.STREAM,
out: IO.STREAM,
tipTable: TIPUser.TIPTable,
serverStream: IO.STREAMNIL,
oldSplit: Menus.MenuEntry ← NIL
];
logFileNumber: INT ← 0;
chatInstanceList: LIST OF REF ANYNIL;
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: BOOLTRUE] = {
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.TSNARROW[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.TSNARROW[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