PupTool.mesa;
By Steve Temple on August 27, 1982 9:39 am
Adapted from PupHacks by Hal Murray
Last edited by Steve Temple, November 11, 1982 2:41 pm
Revised for Cedar 5.2 by Neil Gunther, February 27, 1985 3:40:54 pm PST
DIRECTORY
BasicTime: TYPE USING [Now],
Buttons: TYPE USING [Button, ButtonProc, Create],
Commander USING [CommandProc, Register],
Containers: TYPE USING [ChildXBound, ChildYBound, Container, Create],
Convert USING [RopeFromTime],
ConvertUnsafe: TYPE USING [AppendRope],
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 [DataWordsPerPupBuffer,
GetFreePupBuffer, GetPupAddress, GetPupContentsBytes,
MoveRopeToPupBuffer, ReturnFreePupBuffer, PupAddressToRope,
PupBuffer, PupNameTrouble, PupPackageDestroy,
PupPackageMake, PupRouterBroadcastThis, PupSocket,
PupSocketDestroy, PupSocketMake, SecondsToTocks,
SetPupContentsBytes],
PupRouterDefs: TYPE USING [GetRoutingTable, GetRoutingTableEntry,
RoutingTableEntry, RoutingTable],
PupTypes: TYPE USING [echoSoc, fillInPupAddress, fillInSocketID,
maxDataWordsPerGatewayPup, miscSrvSoc, PupAddress, PupSocketID],
Rope: TYPE USING [Concat, FromChar, Length, ROPE, Substr],
TypeScript: TYPE USING [Create],
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
BasicTime, Buttons, Commander, Containers, Convert, ConvertUnsafe, IO, Labels, Menus,
Process, PupDefs,
PupRouterDefs, Rope, TypeScript, 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
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 ← TypeScript.Create [[
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;
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
meID: PupTypes.PupSocketID ← [0,0];
me: PupTypes.PupAddress ← [,,[0,0]];
-- time: IO.GreenwichMeanTime ← Runtime.GetBcdTime[];
me ← PupDefs.GetPupAddress[meID, "ME"];
IO.PutF[out, "PupTool of %s running on %s\n",
IO.rope[Convert.RopeFromTime[from: BasicTime.Now[]]],
IO.rope[PupDefs.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, PupDefs.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;
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;
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;
mySocID: PupSocketID ← [0,0];
packetNumber: CARDINAL ← 0;
routing: PupRouterDefs.RoutingTableEntry;
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];
where ← GetPupAddress[mySocID, r ! PupNameTrouble =>
{ErrorMsg[h.msg, 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.GetRoutingTableEntry[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: PupRouterDefs.RoutingTable;
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..pupRt.length] 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𡤀};
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;
addrID: PupSocketID ← [0, 0];
addr: PupAddress ← [, , [0, 0]];
hit: BOOLEAN ← FALSE;
r: Rope.ROPE;
r ← InRope[h.viewer];
ClearMsg[h.msg];
IF Rope.Length[r] = 0 THEN BEGIN ErrorMsg[h.msg, "Address needed"]; RETURN; END;
addr ← GetPupAddress[addrID, r ! PupNameTrouble =>
BEGIN
ErrorMsg[h.msg, 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 ← addr;
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;
r ← InRope[h.viewer];
ClearMsg[h.msg];
IF Rope.Length[r] = 0 THEN BEGIN ErrorMsg[h.msg, "Name needed"]; RETURN; END;
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[];
PupDefs.MoveRopeToPupBuffer[b, r];
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]};
DoIt: Commander.CommandProc = TRUSTED BEGIN
MakeTool[]
END;
Commander.Register[key: "PupTool", proc: DoIt, doc: "Simple network interogation program"];
MakeTool[]
END.