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; buttonHeight: INTEGER _ 0; buttonWidth: INTEGER _ 0; ClientData: TYPE = REF ClientDataRep; ClientDataRep: TYPE = RECORD [ log: STREAM _ NIL, in: STREAM _ NIL, 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 { data: ClientData _ NARROW[clientData]; Query[data, a]; }; Server: Buttons.ButtonProc = TRUSTED { data: ClientData _ NARROW[clientData]; Query[data, ns]; }; Star: Buttons.ButtonProc = TRUSTED { data: ClientData _ NARROW[clientData]; Query[data, star]; }; SOA: Buttons.ButtonProc = TRUSTED { data: ClientData _ NARROW[clientData]; Query[data, soa]; }; MX: Buttons.ButtonProc = TRUSTED { 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: BOOL _ FALSE; 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 { 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: BOOL _ FALSE; 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: ROPE _ IO.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: ROPE _ SELECT 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: ROPE _ SELECT 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: ROPE _ NIL] = 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 ATOM _ NIL, change: SelectorProc _ NIL, clientData: REF _ NIL, 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 = { 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: REF _ NIL, parent: Viewer, x, y: INTEGER] RETURNS [child: Viewer] = { bool: Bool _ NEW [BoolRec _ [ value: IF init # NIL THEN init ELSE NEW [BOOL _ TRUE], 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 = { 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: BOOL _ TRUE] 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 = { 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. :ResolverTool.mesa Hal Murray June 1, 1985 11:58:08 pm PDT John Larson, July 19, 1987 5:07:52 pm PDT Viewer layout parameters parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL Κ5˜šœ™Icode™'K™)—J˜šΟk ˜ Jšœœ ˜Jšœ œ0˜?Jšœœ8˜EJšœ œ˜(Jšœ œ$˜4Jšœœœ ˜6Jšœ œ˜Jšœ œ˜Jšœœ ˜Jšœœ˜Jšœœœ˜Jšœœ ˜Jšœ œ˜'Jšœœ ˜Jšœ œ˜%Jšœ œM˜\Jšœ œ=˜NJšœœX˜dJšœœB˜NšœœO˜XJ˜——šΟn œœ˜š˜Jšœ1œdœ˜œ—Jš˜J˜Jšœœœ˜Jšœœœœ˜Jšœœ˜%™Jšœœ˜Jšœ œ˜—J˜Jšœ œœ˜%šœœœ˜Jšœœœ˜Jšœœœ˜Jšœ+˜+Jšœœ˜—J˜Jšœœ˜J˜šΠbnœœ˜)Jšœœ˜+Jšœœ˜#šœ!˜!J˜Jšœ4œœ˜O—Jšœ+˜+J˜šœΟc˜˜&šœ0œ˜6J˜—Jš œœœœ œ˜7—J˜J˜Jšœ˜—šœ˜Jšœ=œœ˜L—šœ4˜4JšœVœ˜]—Jšœ$˜$Jšœ$˜$Jšœ!˜!Jšœ ˜ JšœO˜QJšœ˜J˜—šž œœ+˜>Jšœœ˜šœ!˜!Jšœœœ'˜^—J˜%J˜šœ&˜&J˜ J˜J˜J˜Jšœ˜—šœ%˜%J˜ J˜J˜Jšœ˜Jšœ˜—J˜J˜(Jšœ:˜:Jšœ8˜8Jšœ4˜4Jšœ-œ˜2J˜Jšœ,œ˜0Jšœ:˜:J˜šœ˜Jšœœ˜Jšœœ˜)Kšœnœ˜uJ˜CJšœœ(˜>JšœR˜R—J˜J˜—šŸœœ˜'JšœL™LJšœœ ˜&Jšœ˜J˜—šŸœœ˜&JšœL™LJšœœ ˜&Jšœ˜J˜—šŸœœ˜$JšœL™LJšœœ ˜&Jšœ˜—šΠbkœœ˜#JšœL™LJšœœ ˜&Jšœ˜—š‘œœ˜"JšœL™LJšœœ ˜&Jšœ˜J˜—šŸœœ,œ˜@Jšœœœ ˜Jšœ˜Jšœœ(˜4Jšœœ'˜2Jšœœ)˜5Jšœœ˜Jšœœœ˜Jšœ&˜&Jšœœœ˜'Jšœ œ œœ ˜MJš œ œœœ)œ˜Mš œœœœ˜'Jšœœ˜/Icode2š œœœœœ œ ˜6Lš œœœœœ ˜ELšœ˜Lšœ œ$œ˜;Lšœ4˜4Jšœ$˜&Lšœ$˜$Lšœ˜!Jšœ)˜)L˜š œœœœ˜8Jšœ:˜:Jšœœœ:˜ULšœœ ˜Lšœ œ ˜šœ˜$JšœD˜FJšœ*˜*Jšœ.˜.Jšœ0˜0—Jšœ˜Lšœ$˜$JšœK˜MJšœœ˜ Jšœœ˜ Jšœ˜—Jšœœœ˜Jšœ˜—Jšœ˜Jšœ ˜Jšœ˜J˜—šŸœœ˜'JšœL™LJšœœ ˜&Jšœœœ ˜Jšœ˜Jšœœ(˜4Jšœœ'˜2Jšœœœ/˜=Jšœœœ.˜œ˜L—šœ œœ˜Jšœ=œ˜K—Jšœ˜Jšœ&˜&Jšœœœ˜'Jšœ œ œœ ˜MJš œ œœœ$œ˜HJšœ)˜)š œœœœ˜'Jšœœ˜/Lš œœœœœ œ ˜6Lš œœœœœ ˜EJšœœœ€˜L˜Lšœ˜Lšœ œ$œ˜;L˜Jšœ3˜3J™Jšœ˜!Lšœ$˜$Lšœ˜!š œœœœ˜8Jšœ:˜:Jšœœœ:˜ULšœœ ˜Lšœ œ ˜Jšœ˜šœ˜$JšœD˜FJšœ*˜*Jšœ.˜.Jšœ0˜0—Lšœ œ$œ˜;Lšœ$˜$JšœK˜MJšœœ˜ Jšœœ˜ Jšœ˜—Jšœœœ˜Jšœ˜—Jšœ˜Jšœ˜Jšœ˜J˜—šžœœ˜Jšœœœ˜Jš œœœœœ ˜!Jš œœœœœ˜˜>J˜—šžœœ˜Jšœœœ˜Jš œœœœœ œ˜-Jšœ˜Jšœ˜Jšœœ˜ Jšœ œ˜Jšœ@˜BJšœ"˜"Jšœ#˜#Jšœ#˜#Jšœ$˜$Jšœ˜Jšœ˜Jšœ)˜+Jšœ)˜)Jšœ6˜8šœ˜šœ ˜šœœ˜Jšœ4˜4Jšœœ˜)Jšœ+˜-—šœ˜Jšœ&˜&Jšœ2˜4——šœ˜JšœD˜F—šœ˜JšœG˜I—šœ˜JšœM˜O—šœ˜JšœD˜F—šœ˜Jšœ˜JšœN˜PJšœG˜IJšœA˜CJšœ@˜BJšœ>˜@Jšœ?˜AJšœE˜G—šœ˜JšœD˜F—šœ˜JšœD˜F—šœ˜JšœF˜H—šœ˜Jšœ4˜4Jšœœ˜)Jšœœ˜Jšœ)˜+JšœF˜HJšœ˜šœœœ˜!Jšœ)˜)Jšœ˜šœœœ˜šœ œ˜J˜Jšœ.˜0—J˜J˜ Jšœ˜—Jšœ˜—Jšœ˜—šœ˜JšœA˜C—šœ ˜ Jšœ œ˜)Jšœ œ˜'Jšœ>˜@—˜JšœN˜PJšœF˜H—šœ˜ Jšœ&˜&Jšœ:˜<——J˜—šž œœ˜Jš œœœœœ˜<šœœœ˜Jšœ ˜ Jšœ ˜ Jšœ ˜ J˜ Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ ˜ Jšœ ˜ Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ˜Jšœ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ˜—Jšœ!˜#J˜—šž œœ˜Jš œœœœœ˜>šœœœ˜Jšœ ˜ Jšœ ˜ Jšœ ˜ Jšœ˜—Jšœ!˜#J˜—š žœœœœœœ˜MJšœœœ ˜Jšœœœ˜šœ"œœ œ˜=Jšœ+œœ˜>—Jšœ%˜%Jš œ œœœœœ˜GJšœ)˜)Jšœ4˜4Jšœ<˜J˜J˜Jšœ œ˜ J˜—Jšœœœœ˜#JšœAœ˜Xš œœœœœœ˜5šœ˜Kšœ=œ-˜nJšœ2œ œ˜E—Jšœœœœ˜8Jšœœ˜3Jšœœ/˜PJšœ˜ ——J˜šŸœ˜&JšœL™LJšœœ ˜&Jšœœ ˜(Jšœ œœ#˜3š œœœœœœ˜>šœœ˜J˜Jšœœœ<˜YJšœ8˜8—Jšœ7˜;J˜Jšœ˜ ——J˜Jš œ œœœ œ˜EJšœœœ ˜šœ œœ˜Jšœœœ˜Jšœ˜Jšœ œ˜Jšœ˜—J˜šžœ˜Kšœœœœœœœœ˜jKšœ˜šœ œ ˜Jšœœœœœœœœ˜6J˜Jšœ˜Jšœœ˜—šœ˜Kšœ+œ˜?Jšœ*œ œ˜=—Jšœ˜Jšœ œ2˜E—J˜šŸ œ˜"JšœL™LJšœœ ˜&Jšœ œ ˜ Jšœ˜Jšœ œ4˜GJšœ5˜9Jšœœœ<˜U—šž œœ!œœ˜Q˜šœ+œ˜1Jšœœ œœ˜QJ˜—Jšœœ˜——J˜šžœœ˜Jš œ%œœœœ˜jJš œœœ œœ˜AJš œœœ œœ ˜I˜&šœ>œ˜CJš œœœœœ˜AJšœœ˜J˜ —Jšœœ˜—Jšœ&˜&˜Jšœ=œ˜RJšœ0œ œ˜E—Jšœ ˜J˜—šŸœ˜'JšœL™LJšœœ ˜"šœ ˜Jšœ&œ˜+Jšœ œ˜Jšœ(œ"œ˜TJšœœ˜——J˜šœO˜OJ˜—Jšœ˜J˜J˜J˜—J˜—…—O\iΛ