-- PupTool.mesa; Steve Temple on August 27, 1982 9:39 am
-- Adapted from PupHacks by Murray
-- Last edited March 25, 1983 1:39 pm by Schroeder
DIRECTORY
Buttons: TYPE USING [Button, ButtonProc, Create],
Containers: TYPE USING [ChildXBound, ChildYBound, Container, Create],
ConvertUnsafe: TYPE USING [AppendRope, ToRope],
DriverDefs: TYPE USING [Network],
IO,
Labels: TYPE USING [Create, Label, Set, SetDisplayStyle],
Menus: TYPE USING [AppendMenuEntry, CreateEntry, CreateMenu,
Menu, MenuProc],
Process: TYPE USING [Pause, SecondsToTicks],
PupDefs: TYPE USING [AppendPupAddress, DataWordsPerPupBuffer,
GetFreePupBuffer, GetPupAddress, GetPupContentsBytes,
MoveStringBodyToPupBuffer, ReturnFreePupBuffer,
PupBuffer, PupNameTrouble, PupPackageDestroy,
PupPackageMake, PupRouterBroadcastThis, PupSocket,
PupSocketDestroy, PupSocketMake, SecondsToTocks,
SetPupContentsBytes],
PupRouterDefs: TYPE USING [GetRoutingTable, RoutingTableEntry, RoutingTableObject],
PupTypes: TYPE USING [echoSoc, fillInPupAddress, fillInSocketID,
maxDataWordsPerGatewayPup, miscSrvSoc, PupAddress],
Rope: TYPE USING [Concat, FromChar, Length, ROPE, Substr],
Runtime: TYPE USING [GetBcdTime],
UserExec: TYPE USING [CommandProc, RegisterCommand],
VFonts: TYPE USING [CharWidth],
ViewerClasses: TYPE USING [Viewer, ViewerClassRec],
ViewerIO: TYPE USING [CreateViewerStreams],
ViewerOps: TYPE USING [CreateViewer, DestroyViewer, PaintViewer],
ViewerTools: TYPE USING [GetContents, MakeNewTextViewer, SetSelection];
PupTool: MONITOR
IMPORTS Buttons,
Containers,
ConvertUnsafe,
IO,
Labels,
Menus,
Process,
PupDefs,
PupRouterDefs,
Runtime,
Rope,
UserExec,
VFonts,
ViewerIO,
ViewerOps,
ViewerTools = BEGIN
entryHeight: CARDINAL = 15;
entryVSpace: CARDINAL = 8;
entryHSpace: CARDINAL = 10;
-- a ToolHandle is a REF to the data for a particular instance of the tool.
ToolHandle: TYPE = REF ToolHandleRec;
ToolHandleRec: TYPE = RECORD [
pleaseStop, echoInUse: BOOL ← FALSE, -- useful flags
viewer, root, msg: ViewerClasses.Viewer, -- viewer and parent
out: IO.STREAM, -- text stream
echoThrough: CONDITION]; -- echo process is done
--******************************************************************************
-- The following procedure creates an instance of the Pup Tool
-- Multiple instances of the tool can coexist
--******************************************************************************
MakeTool: PROC[] = BEGIN
handle: ToolHandle ← NEW[ToolHandleRec];
promptButton, textBox: ViewerClasses.Viewer;
menu: Menus.Menu ← Menus.CreateMenu[1];
height: CARDINAL ← 0;
charSize: INTEGER = VFonts.CharWidth['0];
initData: Rope.ROPE = "ME";
PupDefs.PupPackageMake[];
handle.root ← Containers.Create[[
name: "Pup Tool",
iconic: FALSE,
menu: menu,
column: left,
scrollable: FALSE
]];
Menus.AppendMenuEntry[menu: menu, line: 0,
entry: Menus.CreateEntry[clientData: handle, name: "Destroy", proc: QuitProc, fork: FALSE]];
Menus.AppendMenuEntry[menu: menu, line: 0,
entry: Menus.CreateEntry[clientData: handle, name: "EchoOn", proc: EchoProc, fork: TRUE]];
Menus.AppendMenuEntry[menu: menu, line: 0,
entry: Menus.CreateEntry[clientData: handle, name: "EchoOff", proc: StopProc, fork: FALSE]];
Menus.AppendMenuEntry[menu: menu, line: 0,
entry: Menus.CreateEntry[clientData: handle, name: "Route", proc: RouteProc, fork: FALSE]];
Menus.AppendMenuEntry[menu: menu, line: 0,
entry: Menus.CreateEntry[clientData: handle, name: "ToName", proc: ToNameProc, fork: FALSE]];
Menus.AppendMenuEntry[menu: menu, line: 0,
entry: Menus.CreateEntry[clientData: handle, name: "ToAddress", proc: ToAddressProc, fork: FALSE]];
height ← height + entryVSpace;
promptButton ← Buttons.Create[
info: [name: "Name or Address", wy: height, wh: entryHeight,
wx: 10*charSize, parent: handle.root],
proc: Prompt,
clientData: handle];
handle.viewer ← ViewerTools.MakeNewTextViewer[[
parent: handle.root,
wx: promptButton.wx + promptButton.ww + entryHSpace,
wy: height+2, -- (hack: add 2 to align text with button baseline)
ww: 50*charSize, -- 50 digits worth of width
wh: entryHeight,
data: initData,
scrollable: FALSE,
border: FALSE
]];
height ← height + entryHeight + entryVSpace;
handle.msg ← Labels.Create[[
parent: handle.root,
wx: 10*charSize,
wy: height,
ww: 80*charSize, -- 80 digits worth of width
wh: entryHeight,
border: FALSE
]];
height ← height + entryHeight + entryVSpace; -- interline spacing
textBox ← ViewerOps.CreateViewer [flavor: $Typescript,
info: [
parent: handle.root,
border: FALSE,
wy: height ]
];
Containers.ChildXBound[handle.root, textBox];
Containers.ChildYBound[handle.root, textBox];
ViewerOps.PaintViewer[handle.root, all];
[, handle.out] ← ViewerIO.CreateViewerStreams[viewer: textBox, name: NIL];
PrintHeaderLine[handle.out];
END;
--******************************************************************************
-- Procedures to do IO and manipulate PUP addresses and
-- messages as ROPEs follow
--******************************************************************************
Prompt: Buttons.ButtonProc = TRUSTED BEGIN
handle: ToolHandle ← NARROW[clientData]; -- get our data
ViewerTools.SetSelection[handle.viewer]; -- force the selection
END;
PrintHeaderLine: PROC[out: IO.STREAM] = BEGIN
me: PupTypes.PupAddress ← [,,[0,0]];
time: IO.GreenwichMeanTime ← Runtime.GetBcdTime[];
PupDefs.GetPupAddress[@me, "ME"];
IO.PutF[out, "PupTool of%g running on %g\n", IO.time[time], IO.rope[PupAddressToRope[me]]];
END;
ErrorMsg: PROC[l: Labels.Label, r: Rope.ROPE ← NIL] = BEGIN
IF Rope.Length[r] > 70 THEN r ← Rope.Substr[r, 0, 70];
IF Rope.Length[r] < 62 THEN r ← Rope.Concat["ERROR > ", r];
Labels.Set[l, r];
Labels.SetDisplayStyle[l, $WhiteOnBlack];
Process.Pause[Process.SecondsToTicks[1]];
Labels.SetDisplayStyle[l, $BlackOnWhite];
END;
ClearMsg: PROC[l: Labels.Label] = BEGIN
Labels.Set[l, NIL];
Labels.SetDisplayStyle[l, $BlackOnWhite];
END;
InRope: PROC[v: ViewerClasses.Viewer] RETURNS [r: Rope.ROPE ← NIL] = BEGIN
r ← ViewerTools.GetContents[v];
END;
PutSep: PROC[out: IO.STREAM] = BEGIN
IO.PutText[out, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"];
END;
NewLine: PROC[out: IO.STREAM] = BEGIN
IO.PutChar[out, '\n];
END;
ErrorPupToRope: PROC[b: PupDefs.PupBuffer] RETURNS[s: Rope.ROPE]=
BEGIN
source: PupTypes.PupAddress ← b.source;
IF b.pupType=error THEN
BEGIN
len: CARDINAL = PupDefs.GetPupContentsBytes[b];
s ← IO.PutFR["[Error Pup, code=%b, from: ", IO.card[LOOPHOLE[b.errorCode, CARDINAL]]];
s ← Rope.Concat[s, PupAddressToRope[source]];
s ← Rope.Concat[s, "] "];
FOR i: CARDINAL IN [0..len-2*(10+1+1)) DO
s ← Rope.Concat[s, Rope.FromChar[b.errorText[i]]]; ENDLOOP;
END
ELSE
s ← IO.PutFR["## Funny PupType = %b ##\n ", IO.card[LOOPHOLE[b.pupType, CARDINAL]]];
END;
PupAddressToRope: PROC[a: PupTypes.PupAddress] RETURNS[s: Rope.ROPE]=
BEGIN
buffer: STRING ← [40];
PupDefs.AppendPupAddress[buffer, a];
s ← ConvertUnsafe.ToRope[buffer];
END;
PupBodyToRope: PROC[b: PupDefs.PupBuffer] RETURNS[s: Rope.ROPE]=
BEGIN
s ← NIL;
FOR i: CARDINAL IN [0..PupDefs.GetPupContentsBytes[b]) DO
s ← Rope.Concat[s, Rope.FromChar[b.pupChars[i]]]; ENDLOOP;
END;
--******************************************************************************
-- The following procedures are invoked when the corresponding
-- menu button is clicked
--******************************************************************************
EchoProc: Menus.MenuProc = TRUSTED
BEGIN OPEN PupDefs, PupTypes;
h: ToolHandle = NARROW[clientData];
bytesPerBuffer: CARDINAL;
funny, late, recv, sent, wrong: LONG CARDINAL ← 0;
me, where: PupAddress ← [,,echoSoc];
mySoc: PupSocket;
packetNumber: CARDINAL ← 0;
routing: POINTER TO PupRouterDefs.RoutingTableObject;
r: Rope.ROPE;
fudge: STRING ← [40];
IF EchoIsInUse[h] THEN RETURN;
ClearMsg[h.msg];
r ← InRope[h.viewer];
IF Rope.Length[r] = 0 THEN { ErrorMsg[h.msg, "Name needed"]; GOTO GetOut};
ConvertUnsafe.AppendRope[fudge, r];
GetPupAddress[@where, fudge
! PupNameTrouble =>
{ ErrorMsg[h.msg, ConvertUnsafe.ToRope[e]]; GOTO GetOut }];
mySoc ← PupSocketMake[fillInSocketID, where, SecondsToTocks[2]];
me ← mySoc.getLocalAddress[];
PutSep[h.out];
IO.PutF[h.out, "Echo to <%g> via [%g] => [", IO.rope[r], IO.rope[PupAddressToRope[me]]];
routing ← @PupRouterDefs.GetRoutingTable[][where.net];
IF (routing.hop#0) AND (routing.network#NIL) THEN
IO.PutF[h.out, "%b#%b#] => [", IO.card[LOOPHOLE[routing.network.netNumber.b, CARDINAL]],
IO.card[LOOPHOLE[routing.route, CARDINAL]]];
IO.PutF[h.out, "%g]\n", IO.rope[PupAddressToRope[where]]];
bytesPerBuffer ←
2*MIN[DataWordsPerPupBuffer[], maxDataWordsPerGatewayPup];
UNTIL EchoShouldStop[h] DO
FOR len: CARDINAL IN [0..bytesPerBuffer] UNTIL EchoShouldStop[h] DO
b: PupBuffer ← GetFreePupBuffer[];
b.pupID.a ← b.pupID.b ← (packetNumber←packetNumber+1);
b.pupType ← echoMe;
SetPupContentsBytes[b,len];
FOR i: CARDINAL IN [0..len) DO b.pupBytes[i] ← i MOD 400B; ENDLOOP;
mySoc.put[b];
sent ← sent+1;
UNTIL (b←mySoc.get[])=NIL DO
SELECT TRUE FROM
(b.pupType#iAmEcho) =>
{ funny ← funny+1; ErrorMsg[h.msg, ErrorPupToRope[b]];
IO.PutChar[h.out, 'x]; GOTO Wrong };
((b.pupID.a#packetNumber)
OR (b.pupID.b#packetNumber)
OR (len#GetPupContentsBytes[b])) =>
{ IO.PutChar[h.out, '#]; late ← late+1 };
ENDCASE =>
BEGIN
FOR i: CARDINAL IN [0..len) DO
IF b.pupBytes[i]#(i MOD 400B) THEN
{ wrong ← wrong+1; IO.PutChar[h.out, '~]; GOTO Wrong };
ENDLOOP;
IO.PutChar[h.out, '!];
recv ← recv+1;
EXIT;
END;
ReturnFreePupBuffer[b];
REPEAT Wrong => NULL;
ENDLOOP;
IF b#NIL THEN ReturnFreePupBuffer[b] ELSE IO.PutChar[h.out, '?];
ENDLOOP;
NewLine[h.out];
ENDLOOP;
PupSocketDestroy[mySoc];
IO.PutF[h.out, "Out: %d, In: %d (%d%%)\n", IO.card[sent], IO.card[recv], IO.card[(recv*100)/sent]];
IF late#0 THEN IO.PutF[h.out, "Late: %d (%d%%)\n", IO.card[late], IO.card[(late*100)/sent]];
IF funny#0 THEN IO.PutF[h.out, "%d funny\n", IO.card[funny]];
IF wrong#0 THEN IO.PutF[h.out, "%d wrong data\n", IO.card[wrong]];
EchoUserStopped[h];
EXITS GetOut => {h: ToolHandle = NARROW[clientData]; EchoUserStopped[h]};
END;
RouteProc: Menus.MenuProc = TRUSTED BEGIN
pupRt: DESCRIPTOR FOR ARRAY OF PupRouterDefs.RoutingTableObject;
h: ToolHandle ← NARROW[clientData];
k: CARDINAL ← 0;
PutSep[h.out];
ClearMsg[h.msg];
IO.PutText[h.out, "Local PupRouting Table\n"];
IO.PutText[h.out, "| Net Via Hops | Net Via Hops | Net Via Hops |\n"];
IO.PutText[h.out, "|-------------------|-------------------|-------------------|\n"];
pupRt ← PupRouterDefs.GetRoutingTable[];
FOR i: CARDINAL IN [0..LENGTH[pupRt]) DO
r: PupRouterDefs.RoutingTableEntry=@pupRt[i];
network: DriverDefs.Network = r.network;
IF network=NIL THEN LOOP;
IF k=0 THEN IO.PutChar[h.out, '|];
IO.PutF[h.out, "%4b%4b#", IO.card[i], IO.card[LOOPHOLE[network.netNumber.b, CARDINAL]]];
IO.PutF[h.out, "%03b#%4b |", IO.card[IF r.hop#0 THEN r.route ELSE network.hostNumber], IO.card[r.hop]];
IF (k←k+1)=3 THEN { NewLine[h.out]; k←0 };
ENDLOOP;
IF k#0 THEN NewLine[h.out];
END;
ToNameProc: Menus.MenuProc = TRUSTED
BEGIN OPEN PupTypes, PupDefs;
h: ToolHandle = NARROW[clientData];
soc: PupSocket;
b: PupBuffer;
a: PupAddress ← [, , [0, 0]];
hit: BOOLEAN ← FALSE;
r: Rope.ROPE;
fudge: STRING ← [40];
r ← InRope[h.viewer];
ClearMsg[h.msg];
IF Rope.Length[r] = 0 THEN BEGIN ErrorMsg[h.msg, "Address needed"]; RETURN; END;
ConvertUnsafe.AppendRope [fudge, r];
GetPupAddress[@a, fudge ! PupNameTrouble =>
BEGIN
ErrorMsg[h.msg, ConvertUnsafe.ToRope[e]];
GOTO NoName;
END];
soc ← PupSocketMake[fillInSocketID, fillInPupAddress, SecondsToTocks[2]];
THROUGH [0..10) UNTIL hit DO
b ← GetFreePupBuffer[];
b.pupType ← addressLookup;
b.pupID ← [0, 0];
b.dest.socket ← PupTypes.miscSrvSoc;
b.source ← soc.getLocalAddress[];
b.address ← a;
SetPupContentsBytes[b, 2*SIZE[PupAddress]];
PupRouterBroadcastThis[b];
UNTIL hit OR (b ← soc.get[]) = NIL DO
SELECT b.pupType FROM
addressIs =>
BEGIN
hit ← TRUE;
PutSep[h.out];
IO.PutF[h.out, "Name of <%g> is: %g\n", IO.rope[r], IO.rope[PupBodyToRope[b]]];
END;
nameError =>
BEGIN
hit ← TRUE;
ErrorMsg[h.msg, PupBodyToRope[b]];
END;
ENDCASE => ErrorMsg[h.msg, ErrorPupToRope[b]];
ReturnFreePupBuffer[b];
ENDLOOP;
IF ~hit THEN ErrorMsg[h.msg, "No Response that try"];
ENDLOOP;
PupSocketDestroy[soc];
EXITS NoName => NULL;
END;
ToAddressProc: Menus.MenuProc = TRUSTED
BEGIN OPEN PupTypes, PupDefs;
h: ToolHandle = NARROW[clientData];
soc: PupSocket;
b: PupBuffer;
hit: BOOLEAN ← FALSE;
r: Rope.ROPE;
fudge: STRING ← [40];
r ← InRope[h.viewer];
ClearMsg[h.msg];
IF Rope.Length[r] = 0 THEN BEGIN ErrorMsg[h.msg, "Name needed"]; RETURN; END;
ConvertUnsafe.AppendRope [fudge, r];
soc ← PupSocketMake[fillInSocketID, fillInPupAddress, SecondsToTocks[2]];
THROUGH [0..10) UNTIL hit DO
b ← GetFreePupBuffer[];
b.pupType ← nameLookup;
b.pupID ← [0, 0];
b.dest.socket ← PupTypes.miscSrvSoc;
b.source ← soc.getLocalAddress[];
MoveStringBodyToPupBuffer[b, fudge];
PupRouterBroadcastThis[b];
UNTIL hit OR (b ← soc.get[]) = NIL DO
SELECT b.pupType FROM
nameIs =>
BEGIN
n: CARDINAL ← GetPupContentsBytes[b]/(2*SIZE[PupAddress]);
addresses: LONG POINTER TO ARRAY [0..0) OF PupAddress ←
LOOPHOLE[@b.pupBody];
hit ← TRUE;
PutSep[h.out];
IO.PutF[h.out, "Address of <%g> is: ", IO.rope[r]];
FOR i: CARDINAL IN [0..n) DO
IF i # 0 THEN IO.PutText[h.out, ", "];
IO.PutRope[h.out, PupAddressToRope[addresses[i]]];
ENDLOOP;
NewLine[h.out];
END;
nameError =>
BEGIN
hit ← TRUE;
ErrorMsg[h.msg, PupBodyToRope[b]];
END;
ENDCASE => ErrorMsg[h.msg, ErrorPupToRope[b]];
ReturnFreePupBuffer[b];
ENDLOOP;
IF ~hit THEN ErrorMsg[h.msg, "No Response that try"];
ENDLOOP;
PupSocketDestroy[soc];
END;
QuitProc: Menus.MenuProc = TRUSTED BEGIN
h: ToolHandle = NARROW[clientData];
StopEchoUser[h];
ViewerOps.DestroyViewer[h.root];
PupDefs.PupPackageDestroy[];
END;
StopProc: Menus.MenuProc = TRUSTED BEGIN
h: ToolHandle = NARROW[clientData];
StopEchoUser[h];
END;
StopEchoUser: ENTRY PROC [h: ToolHandle] =
BEGIN
h.pleaseStop ← TRUE;
UNTIL NOT h.echoInUse DO WAIT h.echoThrough ENDLOOP;
h.pleaseStop ← FALSE;
END;
EchoShouldStop: ENTRY PROC [h: ToolHandle] RETURNS[BOOLEAN] = INLINE
{RETURN[h.pleaseStop]};
EchoUserStopped: ENTRY PROC [h: ToolHandle] =
{h.echoInUse ← FALSE; NOTIFY h.echoThrough};
EchoIsInUse: ENTRY PROC [h: ToolHandle] RETURNS [BOOLEAN] =
{IF h.echoInUse THEN RETURN [TRUE]; h.echoInUse ← TRUE; RETURN[FALSE]};
--******************************************************************************
-- Now the procedure to create a new tool and
-- its registration with the UserExec
--******************************************************************************
DoIt: UserExec.CommandProc = TRUSTED BEGIN
MakeTool[]
END;
UserExec.RegisterCommand["PupTool", DoIt];
MakeTool[]
END.