ResolverTool.mesa
Hal Murray June 1, 1985 11:58:08 pm PDT
John Larson, July 19, 1987 5:07:52 pm PDT
DIRECTORY
Atom USING [GetPName],
BasicTime USING [GetClockPulses, Pulses, PulsesToMicroseconds],
Buttons USING [Button, ButtonProc, Create, Destroy, SetDisplayStyle],
Commander USING [CommandProc, Register],
Containers USING [ChildXBound, ChildYBound, Create],
IO USING [Flush, PutF, PutFR, PutRope, STREAM, Value],
IPNameUdp USING [AppendQuery, Class, DomainHeader, EndOfData, GetCard, GetCardinal, GetName, GetRope, GetTtl, GetIPAddress, GetTwoBytes, Type],
IPRouter USING [BestAddress],
Labels USING [Create],
Loader USING [BCDBuildTime],
Rope USING [ROPE],
Rules USING [Create],
TypeScript USING [ChangeLooks, Create],
ViewerClasses USING [Viewer],
ViewerIO USING [CreateViewerStreams],
ViewerOps USING [AddProp, ComputeColumn, CreateViewer, MoveViewer, OpenIcon, SetOpenHeight],
ViewerTools USING [GetContents, MakeNewTextViewer, SetContents, SetSelection],
IPDefs USING [Byte, DataBuffer, Datagram, DatagramRec, DByte, Address, InternetHeader, nullAddress],
IPName USING [AddressToName, AddressToRope, LoadCacheFromName, NameToAddress],
UDP USING [BodyRec, Create, default, Destroy, domain, Handle, minLength, Receive, Send];
ResolverTool: CEDAR MONITOR
IMPORTS
Atom, BasicTime, Buttons, Commander, Containers, IO, Labels, Loader, Rules, TypeScript, ViewerIO, ViewerOps, ViewerTools, IPName, IPNameUdp, IPRouter, UDP =
BEGIN
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
Viewer: TYPE = ViewerClasses.Viewer;
Viewer layout parameters
buttonHeight: INTEGER ← 0;
buttonWidth: INTEGER ← 0;
ClientData: TYPE = REF ClientDataRep;
ClientDataRep: TYPE = RECORD [
log: STREAMNIL,
in: STREAMNIL,
where: IPDefs.Address ← IPDefs.nullAddress,
target, query: Viewer ← NIL ];
sequenceNumber: CARDINAL ← 0;
Create: Commander.CommandProc = TRUSTED {
data: ClientData ← NEW[ClientDataRep ← []];
viewer, buttons, log: Viewer ← NIL;
viewer ← ViewerOps.CreateViewer [
flavor: $Container,
info: [name: "ResolverTool", column: right, iconic: TRUE, scrollable: FALSE]];
ViewerOps.AddProp[viewer, $Resolver, data];
{ -- Kludge to find Button size
temp: Buttons.Button = Buttons.Create[
info: [name: "Length:", parent: viewer, border: FALSE,
wx: 0, wy: 0],
proc: NIL, clientData: NIL, fork: FALSE, paint: FALSE];
buttonWidth ← temp.ww;
buttonHeight ← temp.wh;
Buttons.Destroy[temp]; };
log ← TypeScript.Create[
[name: "ResolverTool.log", wy: 27+4, parent: viewer, border: FALSE], FALSE];
[data.in, data.log] ← ViewerIO.CreateViewerStreams [
name: "ResolverTool.log", backingFile: "ResolverTool.log", viewer: log, editedStream: FALSE];
Containers.ChildXBound[viewer, log];
Containers.ChildYBound[viewer, log];
CreateButtons[data, viewer, log];
TypeScript.ChangeLooks[log, 'f];
IO.PutF[data.log, "Resolver Tool of %G.\n", [time[Loader.BCDBuildTime[Create]]]];
ViewerOps.OpenIcon[viewer]; };
CreateButtons: PROC[data: ClientData, parent, log: Viewer] = {
child: Viewer ← NIL;
kids: Viewer = Containers.Create[
info: [parent: parent, border: FALSE, scrollable: FALSE, wx: 0, wy: -9999, ww: 9999, wh: 0] ];
Containers.ChildXBound[parent, kids];
child ← MakeRule[kids, child];
child ← data.target ← MakeLabeledText[
parent: kids,
sibling: child,
name: "Target:",
data: "Target",
prev: data.target ];
child ← data.query ← MakeLabeledText[
parent: kids,
sibling: child,
name: "Query:",
data: "A.B.C.E.ARPA",
prev: data.query ];
child ← MakeRule[kids, child];
child ← MakeLabel[kids, child, "What:"];
child ← MakeButton[kids, child, data, "Address", Address];
child ← MakeButton[kids, child, data, "Server", Server];
child ← MakeButton[kids, child, data, "Star", Star];
child ← MakeButton[kids, child, data, "SOA", SOA];
child ← MakeButton[kids, child, data, "MX", MX];
child ← MakeButton[kids, child, data, "Inverse", Inverse];
child ← MakeRule[kids, child];
{
kidsY: INTEGER = 2;
kidsH: INTEGER = child.wy + child.wh + 2;
ViewerOps.MoveViewer[viewer: log, x: 0, y: kidsY + kidsH, w: log.ww, h: parent.ch - (kids.wy + kidsH), paint: FALSE];
ViewerOps.SetOpenHeight[parent, kidsY + kidsH + 12 * buttonHeight];
IF ~parent.iconic THEN ViewerOps.ComputeColumn[parent.column];
ViewerOps.MoveViewer[viewer: kids, x: kids.wx, y: kidsY, w: kids.ww, h: kidsH]; };
};
Address: Buttons.ButtonProc = TRUSTED {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
data: ClientData ← NARROW[clientData];
Query[data, a]; };
Server: Buttons.ButtonProc = TRUSTED {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
data: ClientData ← NARROW[clientData];
Query[data, ns]; };
Star: Buttons.ButtonProc = TRUSTED {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
data: ClientData ← NARROW[clientData];
Query[data, star]; };
SOA: Buttons.ButtonProc = TRUSTED {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
data: ClientData ← NARROW[clientData];
Query[data, soa]; };
MX: Buttons.ButtonProc = TRUSTED {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
data: ClientData ← NARROW[clientData];
Query[data, mx]; };
Query: PROC [data: ClientData, type: IPNameUdp.Type] = TRUSTED {
log: IO.STREAM ← data.log;
packetStart: BasicTime.Pulses;
target: ROPE = ViewerTools.GetContents[data.target];
query: ROPE ← ViewerTools.GetContents[data.query];
id: CARDINAL ← (sequenceNumber ← sequenceNumber + 1);
handle: UDP.Handle;
done: BOOLFALSE;
Report[log, "\nAsking ", target, " "];
IF ~FindPath[data, target] THEN RETURN;
handle ← UDP.Create[him: data.where, local: UDP.default, remote: UDP.domain];
IF handle = NIL THEN {IO.PutF[data.log, "Can't make handle.\n"]; RETURN[]; };
FOR i: CARDINAL IN [0..1) UNTIL done DO
dg: IPDefs.Datagram ← NEW [IPDefs.DatagramRec];
udp: LONG POINTER TO UDP.BodyRec ← LOOPHOLE[@dg.data];
domain: LONG POINTER TO IPNameUdp.DomainHeader ← LOOPHOLE[@udp.data];
domain^ ← [id: id];
udp.length ← UDP.minLength + IPNameUdp.DomainHeader.SIZE*2;
IPNameUdp.AppendQuery[udp, domain, query, type, in];
IO.PutRope[data.log, "Outgoing: \n"];
PrintDomainPacket[log, udp, domain];
UDP.Send[handle, dg, udp.length];
packetStart ← BasicTime.GetClockPulses[];
UNTIL done OR (dg ← UDP.Receive[handle, 20000]) = NIL DO
packetStop: BasicTime.Pulses ← BasicTime.GetClockPulses[];
microseconds: LONG CARDINAL ← BasicTime.PulsesToMicroseconds[packetStop-packetStart];
udp ← LOOPHOLE[@dg.data];
domain ← LOOPHOLE[@udp.data];
IF dg.inHdr.source # data.where THEN
IO.PutF[log, "Strange source address: expected %G, found %G = %G: \n",
[rope[IPName.AddressToRope[data.where]]],
[rope[IPName.AddressToRope[dg.inHdr.source]]],
[rope[IPName.AddressToName[dg.inHdr.source]]]];
IO.PutRope[log, "Reply: \n"];
PrintDomainPacket[log, udp, domain];
IO.PutF[log, "The response time was %G ms.\n", [integer[microseconds/1000]]];
dg ← NIL;
done ← TRUE;
ENDLOOP;
IF dg # NIL THEN dg ← NIL
ENDLOOP;
IO.PutRope[log, "\n"];
IO.Flush[log];
UDP.Destroy[handle]; };
Inverse: Buttons.ButtonProc = TRUSTED {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
data: ClientData ← NARROW[clientData];
log: IO.STREAM ← data.log;
packetStart: BasicTime.Pulses;
target: ROPE = ViewerTools.GetContents[data.target];
query: ROPE = ViewerTools.GetContents[data.query];
where: LIST OF IPDefs.Address ← IPName.NameToAddress[target];
aList: LIST OF IPDefs.Address ← IPName.NameToAddress[query];
address: IPDefs.Address;
id: CARDINAL ← (sequenceNumber ← sequenceNumber + 1);
handle: UDP.Handle;
done: BOOLFALSE;
IF where = NIL THEN {
IO.PutF[log, "\"Target\" needs to be a valid Name/Address.\n"]; RETURN[]; };
IF aList = NIL THEN {
IO.PutF[log, "\"Query\" needs to be a valid Name/Address.\n"]; RETURN[]; };
address ← aList.first;
Report[log, "\nAsking ", target, " "];
IF ~FindPath[data, target] THEN RETURN;
handle ← UDP.Create[him: data.where, local: UDP.default, remote: UDP.domain];
IF handle = NIL THEN {IO.PutF[log, "Can't make handle.\n"]; RETURN[]; };
packetStart ← BasicTime.GetClockPulses[];
FOR i: CARDINAL IN [0..1) UNTIL done DO
dg: IPDefs.Datagram ← NEW [IPDefs.DatagramRec];
udp: LONG POINTER TO UDP.BodyRec ← LOOPHOLE[@dg.data];
domain: LONG POINTER TO IPNameUdp.DomainHeader ← LOOPHOLE[@udp.data];
name: ROPEIO.PutFR["%G.%G.%G.%G.IN-ADDR.ARPA", [integer[address[3]]], [integer[address[2]]], [integer[address[1]]], [integer[address[0]]] ];
domain^ ← [id: id];
udp.length ← UDP.minLength + IPNameUdp.DomainHeader.SIZE*2;
IPNameUdp.AppendQuery[udp, domain, name, star, in];
IO.PutRope[log, "Outgoing: \n"];
PrintDomainPacket[log, udp, domain];
UDP.Send[handle, dg, udp.length];
UNTIL done OR (dg ← UDP.Receive[handle, 20000]) = NIL DO
packetStop: BasicTime.Pulses ← BasicTime.GetClockPulses[];
microseconds: LONG CARDINAL ← BasicTime.PulsesToMicroseconds[packetStop-packetStart];
udp ← LOOPHOLE[@dg.data];
domain ← LOOPHOLE[@udp.data];
IO.PutRope[log, "Reply:\n"];
IF dg.inHdr.source # data.where THEN
IO.PutF[log, "Strange source address: expected %G, found %G = %G: \n",
[rope[IPName.AddressToRope[data.where]]],
[rope[IPName.AddressToRope[dg.inHdr.source]]],
[rope[IPName.AddressToName[dg.inHdr.source]]]];
udp.length ← UDP.minLength + IPNameUdp.DomainHeader.SIZE*2;
PrintDomainPacket[log, udp, domain];
IO.PutF[log, "The response time was %G ms.\n", [integer[microseconds/1000]]];
dg ← NIL;
done ← TRUE;
ENDLOOP;
IF dg # NIL THEN dg ← NIL
ENDLOOP;
UDP.Destroy[handle];
IO.PutRope[log, "\n"];
IO.Flush[log]; };
PrintDomainPacket: PROC [
log: IO.STREAM,
udp: LONG POINTER TO UDP.BodyRec,
domain: LONG POINTER TO IPNameUdp.DomainHeader] = TRUSTED {
ENABLE {UNWIND => NULL; IPNameUdp.EndOfData => GOTO Quit};
length: INT ← udp.length;
udp.length ← UDP.minLength + IPNameUdp.DomainHeader.SIZE*2;
IO.PutF[log, "UDP Domain Packet: id=%G, ", [integer[domain.id]]];
SELECT domain.qr FROM
query => IO.PutRope[log, "qr: query, "];
response => IO.PutRope[log, "qr: response, "];
ENDCASE => ERROR;
SELECT domain.opcode FROM
query => IO.PutRope[log, "op: query"];
iquery => IO.PutRope[log, "op: iquery"];
cquerym => IO.PutRope[log, "op: cquerym"];
cqueryu => IO.PutRope[log, "op: cqueryu"];
ENDCASE => ERROR;
IO.PutF[log, ", length: %G bytes.\n", [integer[length]]];
IO.PutF[log, "aa: %G, tc: %G, rd: %G, ra: %G\n",
[boolean[domain.aa]], [boolean[domain.tc]], [boolean[domain.rd]], [boolean[domain.ra]]];
SELECT domain.rcode FROM
ok => IO.PutRope[log, "op: ok"];
format => IO.PutRope[log, "op: format"];
serverFailed => IO.PutRope[log, "op: serverFailed"];
nameNotFound => IO.PutRope[log, "op: nameNotFound"];
notImplemented => IO.PutRope[log, "op: notImplemented"];
refused => IO.PutRope[log, "op: refused"];
ENDCASE => ERROR;
IF domain.tc THEN IO.PutRope[log, " ** TRUNCATED **"];
IO.PutRope[log, "\n"];
IO.PutF[log, "qdCount: %G, anCount: %G, nsCount: %G, arCount: %G\n",
[integer[domain.qdCount]], [integer[domain.anCount]], [integer[domain.nsCount]], [integer[domain.arCount]]];
FOR i: INT IN [0..domain.qdCount) DO
PrintQuery[log, udp];
ENDLOOP;
FOR i: INT IN [0..domain.anCount) DO
PrintRR[log, udp];
ENDLOOP;
FOR i: INT IN [0..domain.nsCount) DO
PrintRR[log, udp];
ENDLOOP;
FOR i: INT IN [0..domain.arCount) DO
PrintRR[log, udp];
ENDLOOP;
IO.PutRope[log, "\n"];
udp.length ← length;
EXITS
Quit => {IO.PutRope[log, "** NO MORE DATA. RECORD COUNT INCORRECT **"]; RETURN};
};
PrintQuery: PROC [ log: IO.STREAM, udp: LONG POINTER TO UDP.BodyRec] = TRUSTED {
IO.PutF[log, "Query Name: \"%G\", ", [rope[IPNameUdp.GetName[udp]]]];
PrintType[log, "Type: %G, ", IPNameUdp.GetTwoBytes[udp]];
PrintClass[log, "Class: %G\n", IPNameUdp.GetTwoBytes[udp]]; };
PrintRR: PROC [
log: IO.STREAM,
udp: LONG POINTER TO UDP.BodyRec] = TRUSTED {
type: IPNameUdp.Type;
class: IPNameUdp.Class;
ttl: INT;
rDataLength: CARDINAL;
IO.PutF[log, "RR Name: \"%G\", ", [rope[IPNameUdp.GetName[udp]]]];
type ← IPNameUdp.GetTwoBytes[udp];
PrintType[log, "Type: %G, ", type];
class ← IPNameUdp.GetTwoBytes[udp];
PrintClass[log, "Class: %G", class];
IO.PutRope[log, ".\n"];
ttl ← IPNameUdp.GetTtl[udp];
IO.PutF[log, " TTL: %G, ", [integer[ttl]]];
rDataLength ← IPNameUdp.GetTwoBytes[udp];
IO.PutF[log, "RDataLength: %G", [integer[rDataLength]]];
SELECT type FROM
a => { -- This is it!
IF rDataLength = 4 THEN {
where: IPDefs.Address ← IPNameUdp.GetIPAddress[udp];
rope: ROPE ← IPName.AddressToRope[where];
IO.PutF[log, " Addr: %G\n", [rope[rope]]]; }
ELSE {
udp.length ← udp.length + rDataLength;
IO.PutRope[log, "Funny Length for type A.\n"]; }; };
ns =>
IO.PutF[log, " Server is \"%G\".\n", [rope[IPNameUdp.GetName[udp]]]];
md =>
IO.PutF[log, " Mail Host is \"%G\".\n", [rope[IPNameUdp.GetName[udp]]]];
mf =>
IO.PutF[log, " Forwarding Host is \"%G\".\n", [rope[IPNameUdp.GetName[udp]]]];
cName =>
IO.PutF[log, " Alias for \"%G\".\n", [rope[IPNameUdp.GetName[udp]]]];
soa => {
IO.PutRope[log, "\n"];
IO.PutF[log, " Primary server is \"%G\".\n", [rope[IPNameUdp.GetName[udp]]]];
IO.PutF[log, " Contact is \"%G\".\n", [rope[IPNameUdp.GetName[udp]]]];
IO.PutF[log, " Serial: %G", [cardinal[IPNameUdp.GetCard[udp]]]];
IO.PutF[log, ", Refresh: %G", [cardinal[IPNameUdp.GetCard[udp]]]];
IO.PutF[log, ", Retry: %G", [cardinal[IPNameUdp.GetCard[udp]]]];
IO.PutF[log, ", Expire: %G", [cardinal[IPNameUdp.GetCard[udp]]]];
IO.PutF[log, ", Min TTL: %G\n", [cardinal[IPNameUdp.GetCard[udp]]]]; };
mb =>
IO.PutF[log, " Mail Host \"%G\".\n", [rope[IPNameUdp.GetName[udp]]]];
mg =>
IO.PutF[log, " Member is \"%G\".\n", [rope[IPNameUdp.GetName[udp]]]];
mr =>
IO.PutF[log, " New Name is \"%G\".\n", [rope[IPNameUdp.GetName[udp]]]];
wks => {
where: IPDefs.Address ← IPNameUdp.GetIPAddress[udp];
rope: ROPE ← IPName.AddressToRope[where];
socketNumber: INT ← 0;
IO.PutF[log, " Addr: %G\n", [rope[rope]]];
IO.PutF[log, " Protocol: %G, Ports:", [integer[udp.data[udp.length]]]];
udp.length ← udp.length + 1;
FOR i: INT IN [5..rDataLength) DO
byte: IPDefs.Byte ← udp.data[udp.length];
udp.length ← udp.length + 1;
FOR i: INT IN [0..8) DO
IF byte >= 80H THEN {
byte ← byte - 80H;
IO.PutF[log, " %G", [integer[socketNumber]]]; };
byte ← byte * 2;
socketNumber ← socketNumber + 1;
ENDLOOP;
ENDLOOP;
IO.PutRope[log, "\n"]; };
ptr =>
IO.PutF[log, " PTR is \"%G\".\n", [rope[IPNameUdp.GetName[udp]]]];
hinfo => {
cpu: Rope.ROPE ← IPNameUdp.GetRope[udp];
os: Rope.ROPE ← IPNameUdp.GetRope[udp];
IO.PutF[log, " CPU: %G, OS: %G.\n", [rope[cpu]], [rope[os]]];};
mx => {
IO.PutF[log, " Preference Value: %G,", [cardinal[IPNameUdp.GetCardinal[udp]]]];
IO.PutF[log, " Host Name: \"%G\".\n", [rope[IPNameUdp.GetName[udp]]]]};
ENDCASE => {
udp.length ← udp.length + rDataLength;
IO.PutRope[log, ", Funny Type or Lazy Programmer.\n"]; }; };
PrintType: PROC [
log: IO.STREAM, arg: ROPE, type: IPNameUdp.Type] = TRUSTED {
text: ROPESELECT type FROM
a => "a",
ns => "ns",
md => "md",
mf => "mf",
cName => "cName",
soa => "soa",
mb => "mb",
mg => "mg",
mr => "mr",
null => "null",
wks => "wks",
ptr => "ptr",
hinfo => "hinfo",
minfo => "minfo",
mx => "mx",
axfbr => "axfbr",
mailb => "mailb",
maila => "maila",
star => "*",
ENDCASE => " *UnknownType";
IO.PutF[log, arg, [rope[text]]]; };
PrintClass: PROC [
log: IO.STREAM, arg: ROPE, class: IPNameUdp.Class] = TRUSTED {
text: ROPESELECT class FROM
in => "in",
cs => "cs",
star => "*",
ENDCASE => " *UnknownClass";
IO.PutF[log, arg, [rope[text]]]; };
FindPath: PROC [data: ClientData, target: ROPE] RETURNS [BOOLEAN] = TRUSTED {
log: IO.STREAM = data.log;
where: LIST OF IPDefs.Address;
IF IPName.LoadCacheFromName[target, TRUE, TRUE] = down THEN {
IO.PutF[data.log, "Can't load name Cache."]; RETURN[FALSE]; };
where ← IPName.NameToAddress[target];
IF where = NIL THEN {IO.PutF[log, "Name not found."]; RETURN[FALSE]; };
data.where ← IPRouter.BestAddress[where];
Report[log, "= ", IPName.AddressToName[data.where]];
Report[log, " = ", IPName.AddressToRope[data.where], ".\n"];
RETURN[TRUE]; };
Report: PROC [log: IO.STREAM, r1, r2, r3, r4: ROPENIL] = TRUSTED {
IF r1 # NIL THEN {IO.PutRope[log, r1]};
IF r2 # NIL THEN {IO.PutRope[log, r2]};
IF r3 # NIL THEN {IO.PutRope[log, r3]};
IF r4 # NIL THEN {IO.PutRope[log, r4]}; };
MakeRule: PROC [parent, sibling: Viewer] RETURNS [child: Viewer] = {
child ← Rules.Create[
info: [parent: parent, border: FALSE,
wy: IF sibling = NIL THEN 0 ELSE sibling.wy + sibling.wh + 2, wx: 0, ww: parent.ww, wh: 1],
paint: FALSE ];
Containers.ChildXBound[parent, child]; };
MakeButton: PROC [parent, sibling: Viewer, data: REF ANY, name: ROPE, proc: Buttons.ButtonProc] RETURNS[child: Viewer] = {
child ← Buttons.Create[
info: [name: name, parent: parent, border: TRUE,
wy: sibling.wy, wx: sibling.wx + buttonWidth - 1, ww: buttonWidth],
proc: proc,
clientData: data,
fork: TRUE,
paint: FALSE]; };
SelectorProc: TYPE = PROC [parent: Viewer, clientData: REF, value: ATOM];
Selector: TYPE = REF SelectorRec;
SelectorRec: TYPE = RECORD [
value: REF ATOM,
change: PROC [parent: Viewer, clientData: REF, value: ATOM],
clientData: REF,
buttons: LIST OF Buttons.Button,
values: LIST OF ATOM ];
MakeSelector: PROC
[name: ROPE, values: LIST OF ATOM, init: REF ATOMNIL, change: SelectorProc ← NIL, clientData: REFNIL, parent: Viewer, x, y: INTEGER]
RETURNS [child: Viewer] = {
selector: Selector ← NEW [SelectorRec ← [
value: IF init # NIL THEN init ELSE NEW [ATOM ← values.first],
change: change,
clientData: clientData,
buttons: NIL,
values: values ] ];
last: LIST OF Buttons.Button ← NIL;
child ← Labels.Create[info: [name: name, parent: parent, border: FALSE, wx: x, wy: y] ];
FOR a: LIST OF ATOM ← values, a.rest UNTIL a = NIL DO
child ← Buttons.Create[
info: [name: Atom.GetPName[a.first], parent: parent, border: TRUE, wx: child.wx + child.ww + 2, wy: child.wy],
proc: SelectorHelper, clientData: selector, fork: TRUE, paint: TRUE];
IF last = NIL THEN last ← selector.buttons ← LIST[child]
ELSE { last.rest ← LIST[child]; last ← last.rest };
IF a.first = selector.value^ THEN Buttons.SetDisplayStyle[child, $WhiteOnBlack];
ENDLOOP; };
SelectorHelper: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
self: Buttons.Button = NARROW[parent];
selector: Selector = NARROW[clientData];
buttons: LIST OF Buttons.Button ← selector.buttons;
FOR a: LIST OF ATOM ← selector.values, a.rest UNTIL a = NIL DO
IF self = buttons.first THEN {
selector.value^ ← a.first;
IF selector.change # NIL THEN selector.change[self.parent, selector.clientData, a.first];
Buttons.SetDisplayStyle[buttons.first, $WhiteOnBlack]; }
ELSE Buttons.SetDisplayStyle[buttons.first, $BlackOnWhite];
buttons ← buttons.rest;
ENDLOOP; };
BoolProc: TYPE = PROC [parent: Viewer, clientData: REF, value: BOOL];
Bool: TYPE = REF BoolRec;
BoolRec: TYPE = RECORD [
value: REF BOOL,
change: BoolProc,
clientData: REF,
button: Viewer ];
MakeBool: PROC
[name: ROPE, init: REF BOOL, change: BoolProc ← NIL, clientData: REFNIL, parent: Viewer, x, y: INTEGER]
RETURNS [child: Viewer] = {
bool: Bool ← NEW [BoolRec ← [
value: IF init # NIL THEN init ELSE NEW [BOOLTRUE],
change: change,
clientData: clientData,
button: NIL ] ];
child ← Buttons.Create[
info: [name: name, parent: parent, border: TRUE, wx: x, wy: y],
proc: BoolHelper, clientData: bool, fork: TRUE, paint: TRUE];
bool.button ← child;
IF bool.value^ THEN Buttons.SetDisplayStyle[child, $WhiteOnBlack]; };
BoolHelper: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
self: Buttons.Button = NARROW[parent];
bool: Bool = NARROW[clientData];
bool.value^ ← ~bool.value^;
IF bool.value^ THEN Buttons.SetDisplayStyle[bool.button, $WhiteOnBlack]
ELSE Buttons.SetDisplayStyle[bool.button, $BlackOnWhite];
IF bool.change # NIL THEN bool.change[self.parent, bool.clientData, bool.value^]; };
MakeLabel: PROC [parent, sibling: Viewer, name: ROPE] RETURNS [child: Viewer] = {
child ← Labels.Create[
info: [name: name, parent: parent, border: FALSE,
wy: sibling.wy + sibling.wh + (IF sibling.class.flavor = $Button THEN -1 ELSE 2),
wx: 2],
paint: FALSE ]; };
MakeLabeledText: PROC [
parent, sibling: Viewer, name, data: ROPE, prev: Viewer, newline: BOOLTRUE] RETURNS [child: Viewer] = {
x: INTEGER = IF newline THEN 2 ELSE sibling.wx + sibling.ww + 10;
y: INTEGER = IF newline THEN sibling.wy + sibling.wh + 1 ELSE sibling.wy;
child ← ViewerTools.MakeNewTextViewer[
info: [parent: parent, wh: buttonHeight, ww: 999, scrollable: TRUE,
data: IF prev = NIL THEN data ELSE ViewerTools.GetContents[prev],
border: FALSE,
wx: x + buttonWidth + 2, wy: y],
paint: FALSE ];
Containers.ChildXBound[parent, child];
[] ← Buttons.Create[
info: [name: name, parent: parent, wh: buttonHeight, border: FALSE, wx: x, wy: y],
proc: LabeledTextProc, clientData: child, fork: FALSE, paint: FALSE];
RETURN[child]; };
LabeledTextProc: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
text: Viewer = NARROW[clientData];
SELECT mouseButton FROM
red => ViewerTools.SetSelection[text, NIL];
yellow => NULL;
blue => { ViewerTools.SetContents[text, NIL]; ViewerTools.SetSelection[text, NIL] };
ENDCASE => ERROR; };
Commander.Register["ResolverTool", Create, "Probe Arp Internet Name Servers."];
END.