DIRECTORY BasicTime USING [GMT, Now, Period], Buttons USING [Button, ButtonProc, Create, Destroy, SetDisplayStyle], Commander USING [CommandProc, Register], Containers USING [ChildXBound, ChildYBound, Create], IO USING [Flush, PutF, PutRope, STREAM, Value], Labels USING [Create], Loader USING [BCDBuildTime], Rope USING [Cat, ROPE, Length], 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 [Address], IPName USING [AddressToName, AddressToRope, LoadCacheFromAddress, LoadCacheFromName, NameState, NameToAddress, NormalizeName], IPNameCache, IPNameSupport USING [AddressSummaryLine, AliasSummaryLine, BogusAddressSummaryLine, BogusNameSummaryLine, DownAddressSummaryLine, DownNameSummaryLine, DownServerSummaryLine, MXSummaryLine, NameSummaryLine, ZoneSummaryLine]; IPNameTool: CEDAR MONITOR IMPORTS BasicTime, Buttons, Commander, Containers, IO, Labels, Loader, Rope, Rules, TypeScript, ViewerIO, ViewerOps, ViewerTools, IPName, IPNameCache, IPNameSupport = 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, query: Viewer, arg: Viewer]; sequenceNumber: CARDINAL _ 0; NameEntry: TYPE = IPNameCache.NameEntry; AliasEntry: TYPE = IPNameCache.AliasEntry; MXEntry: TYPE = IPNameCache.MXEntry; BogusNameEntry: TYPE = IPNameCache.BogusNameEntry; DownNameEntry: TYPE = IPNameCache.DownNameEntry; AddressEntry: TYPE = IPNameCache.AddressEntry; BogusAddressEntry: TYPE = IPNameCache.BogusAddressEntry; DownAddressEntry: TYPE = IPNameCache.DownAddressEntry; DownServerEntry: TYPE = IPNameCache.DownServerEntry; HistogramEntry: TYPE = IPNameCache.HistogramEntry; ZoneEntry: TYPE = IPNameCache.ZoneEntry; Create: Commander.CommandProc = { CreateOne[]; }; CreateOne: PROC = { data: ClientData _ NEW[ClientDataRep _ []]; viewer, buttons, log: Viewer _ NIL; viewer _ ViewerOps.CreateViewer [ flavor: $Container, info: [name: "IPNameTool", column: left, iconic: TRUE, scrollable: FALSE]]; ViewerOps.AddProp[viewer, $IPNameTool, data]; { -- Kludge to find Button size temp: Buttons.Button = Buttons.Create[ info: [name: "Aliases:", 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: "IPNameTool.log", wy: 27+4, parent: viewer, border: FALSE], FALSE]; [data.in, data.log] _ ViewerIO.CreateViewerStreams [ name: "IPNameTool.log", backingFile: "IPNameTool.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, "IPName Tool of %G.\n", [time[Loader.BCDBuildTime[Create]]]]; IF FALSE THEN 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.query _ MakeLabeledText[ parent: kids, sibling: child, name: "Query:", data: "Xerox.COM", prev: data.query]; child _ data.arg _ MakeLabeledText[ parent: kids, sibling: child, name: "Arg:", data: "[10.2.0.32]", prev: data.arg]; child _ MakeRule[kids, child]; child _ MakeLabel[kids, child, "Poke:"]; child _ MakeButton[kids, child, data, "N=>A", N2A]; child _ MakeButton[kids, child, data, "N=>MX", N2MX]; child _ MakeButton[kids, child, data, "N=>N", N2N]; child _ MakeButton[kids, child, data, "A=>N", A2N]; child _ MakeButton[kids, child, data, "Quick", Quick]; child _ MakeLabel[kids, child, "Info:"]; child _ MakeButton[kids, child, data, "Slot", Slot]; child _ MakeButton[kids, child, data, "Names", Names]; child _ MakeButton[kids, child, data, "Addrs", Addrs]; child _ MakeButton[kids, child, data, "Aliases", Aliases]; child _ MakeButton[kids, child, data, "MXs", MXs]; child _ MakeButton[kids, child, data, "Bogus", Bogus]; child _ MakeButton[kids, child, data, "Down", Down]; child _ MakeButton[kids, child, data, "Zones", Zones]; child _ MakeButton[kids, child, data, "Hist", Hist]; child _ MakeButton[kids, child, data, "Zap", Zap]; child _ MakeButton[kids, child, data, "Add", Add]; 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]; }; }; Zap: Buttons.ButtonProc = { data: ClientData _ NARROW[clientData]; log: IO.STREAM _ data.log; IPNameCache.ZapWholeCache[]; IO.PutRope[log, "Zapped name and server caches.\n\n"]; }; N2A: Buttons.ButtonProc = { data: ClientData _ NARROW[clientData]; log: IO.STREAM _ data.log; query: ROPE _ ViewerTools.GetContents[data.query]; addresses: LIST OF IPDefs.Address; state: IPName.NameState _ IPName.LoadCacheFromName[query, TRUE, TRUE]; IF state = bogus OR state = down THEN {IO.PutRope[log, Rope.Cat[query, " => "]]; RETURN}; addresses _ IPName.NameToAddress[query]; IO.PutRope[log, query]; PrintListOfAddresses[log, " => ", addresses]; }; N2N: Buttons.ButtonProc = { data: ClientData _ NARROW[clientData]; log: IO.STREAM _ data.log; query: ROPE _ ViewerTools.GetContents[data.query]; name: ROPE; state: IPName.NameState _ IPName.LoadCacheFromName[query, TRUE, FALSE]; IF state = bogus OR state = down THEN {IO.PutRope[log, Rope.Cat[query, " => "]]; RETURN}; name _ IPName.NormalizeName[query]; IO.PutF[log, "%G => %G.\n", [rope[query]], [rope[name]]]; }; N2MX: Buttons.ButtonProc = { data: ClientData _ NARROW[clientData]; log: IO.STREAM _ data.log; query: ROPE _ ViewerTools.GetContents[data.query]; state: IPName.NameState _ IPName.LoadCacheFromName[query, TRUE, FALSE]; Log: PROC [rope: ROPE] ~ { IO.PutRope[log, rope]; IO.Flush[log]}; IF state = mxOk THEN { mx: MXEntry _ IPNameCache.MXLookup[query]; IF mx # NIL THEN IPNameSupport.MXSummaryLine[mx, FALSE, TRUE, Log]; }; }; A2N: Buttons.ButtonProc = { data: ClientData _ NARROW[clientData]; log: IO.STREAM _ data.log; query: ROPE _ ViewerTools.GetContents[data.query]; where: LIST OF IPDefs.Address _ IPName.NameToAddress[query]; name: Rope.ROPE; IF where = NIL THEN { IO.PutF[log, "\"Query\" needs to be a valid Address.\n"]; RETURN[]; }; name _ IPName.AddressToName[where.first]; IO.PutF[log, "%G => %G.\n", [rope[IPName.AddressToRope[where.first]]], [rope[name]]]; }; Quick: Buttons.ButtonProc = { ENABLE UNWIND => NULL; data: ClientData _ NARROW[clientData]; log: IO.STREAM _ data.log; names: LIST OF NameEntry = IPNameCache.GetNames[]; mxList: LIST OF MXEntry = IPNameCache.GetMXList[]; aliases: LIST OF AliasEntry = IPNameCache.GetAliases[]; bogusNames: LIST OF BogusNameEntry = IPNameCache.GetBogusNames[]; downNames: LIST OF DownNameEntry = IPNameCache.GetDownNames[]; addresses: LIST OF AddressEntry = IPNameCache.GetAddresses[]; bogusAddresses: LIST OF BogusAddressEntry = IPNameCache.GetBogusAddresses[]; downAddresses: LIST OF DownAddressEntry = IPNameCache.GetDownAddresses[]; downServers: LIST OF DownServerEntry = IPNameCache.GetDownServers[]; zones: LIST OF ZoneEntry = IPNameCache.GetZones[]; Log: PROC [rope: ROPE] ~ { IO.PutRope[log, rope]; IO.Flush[log]}; IO.PutRope[log, "\nCache Summary:\n"]; IF names = NIL THEN IO.PutRope[log, "Name Cache is empty.\n"] ELSE IO.PutRope[log, "Names:\n"]; FOR list: LIST OF NameEntry _ names, list.rest UNTIL list = NIL DO IPNameSupport.NameSummaryLine[list.first, FALSE, TRUE, Log]; ENDLOOP; IF aliases = NIL THEN IO.PutRope[log, "Alias Cache is empty.\n"] ELSE IO.PutRope[log, "Aliases:\n"]; FOR list: LIST OF AliasEntry _ aliases, list.rest UNTIL list = NIL DO IPNameSupport.AliasSummaryLine[list.first, FALSE, TRUE, Log]; ENDLOOP; IF mxList = NIL THEN IO.PutRope[log, "MX Cache is empty.\n"] ELSE IO.PutRope[log, "MXs:\n"]; FOR list: LIST OF MXEntry _ mxList, list.rest UNTIL list = NIL DO IPNameSupport.MXSummaryLine[list.first, FALSE, TRUE, Log]; ENDLOOP; IF bogusNames = NIL THEN IO.PutRope[log, "Bogus Name Cache is empty.\n"] ELSE IO.PutRope[log, "Bogus Names:\n"]; FOR list: LIST OF BogusNameEntry _ bogusNames, list.rest UNTIL list = NIL DO IPNameSupport.BogusNameSummaryLine[list.first, FALSE, TRUE, Log]; ENDLOOP; IF downNames # NIL THEN IO.PutRope[log, "Down Names:\n"]; FOR list: LIST OF DownNameEntry _ downNames, list.rest UNTIL list = NIL DO IPNameSupport.DownNameSummaryLine[list.first, FALSE, TRUE, Log]; ENDLOOP; IF addresses = NIL THEN IO.PutRope[log, "Address Cache is empty.\n"] ELSE IO.PutRope[log, "Addresses:\n"]; FOR list: LIST OF AddressEntry _ addresses, list.rest UNTIL list = NIL DO IPNameSupport.AddressSummaryLine[list.first, FALSE, TRUE, Log]; ENDLOOP; IF bogusAddresses = NIL THEN IO.PutRope[log, "Bogus Address Cache is empty.\n"] ELSE IO.PutRope[log, "Bogus Addresses:\n"]; FOR list: LIST OF BogusAddressEntry _ bogusAddresses, list.rest UNTIL list = NIL DO IPNameSupport.BogusAddressSummaryLine[list.first, FALSE, TRUE, Log]; ENDLOOP; IF downAddresses # NIL THEN IO.PutRope[log, "Down Addresses:\n"]; FOR list: LIST OF DownAddressEntry _ downAddresses, list.rest UNTIL list = NIL DO IPNameSupport.DownAddressSummaryLine[list.first, FALSE, TRUE, Log]; ENDLOOP; IF downServers # NIL THEN IO.PutRope[log, "Down Servers:\n"]; FOR list: LIST OF DownServerEntry _ downServers, list.rest UNTIL list = NIL DO IPNameSupport.DownServerSummaryLine[list.first, FALSE, TRUE, Log]; ENDLOOP; IF zones = NIL THEN IO.PutRope[log, "Zone Cache is empty.\n"] ELSE IO.PutRope[log, "Zones:\n"]; FOR list: LIST OF ZoneEntry _ zones, list.rest UNTIL list = NIL DO IPNameSupport.ZoneSummaryLine[list.first, FALSE, TRUE, Log]; ENDLOOP; IO.PutRope[log, "\n\n"]; IO.Flush[log]; }; Add: Buttons.ButtonProc = { ENABLE UNWIND => NULL; data: ClientData _ NARROW[clientData]; query: ROPE _ ViewerTools.GetContents[data.query]; log: IO.STREAM _ data.log; arg: ROPE _ ViewerTools.GetContents[data.arg]; addresses: LIST OF IPDefs.Address; IF query.Length[] = 0 OR arg.Length[] = 0 THEN RETURN; IPNameCache.AddAddressToName[entry: IPNameCache.UpdateName[name: query, source: [10,2,0,32], ttl: 3333, authoritative: FALSE], address: IPName.NameToAddress[name: arg].first]; addresses _ IPName.NameToAddress[query]; IO.PutRope[log, query]; PrintListOfAddresses[log, " => ", addresses]; }; Slot: Buttons.ButtonProc = { ENABLE UNWIND => NULL; data: ClientData _ NARROW[clientData]; log: IO.STREAM _ data.log; found: BOOLEAN _ FALSE; query: ROPE _ ViewerTools.GetContents[data.query]; this: NameEntry _ IPNameCache.NameLookup[query]; alias: AliasEntry _ IPNameCache.AliasLookup[query]; mx: MXEntry _ IPNameCache.MXLookup[query]; bogus: BogusNameEntry _ IPNameCache.BogusNameLookup[query]; down: DownNameEntry _ IPNameCache.DownNameLookup[query]; zone: ZoneEntry _ IPNameCache.ZoneLookup[query]; IO.PutRope[log, "\n"]; IF this # NIL THEN { now: BasicTime.GMT _ BasicTime.Now[]; source: ROPE _ IPName.AddressToName[this.source]; expires: INT _ BasicTime.Period[from: now, to: this.expires]; found _ TRUE; IO.PutF[log, "Name Loaded: %G, From: %G", [time[this.loaded]], [rope[source]]]; IF ~this.authoritative THEN IO.PutF[log, ", NOT Authoritative **"]; IO.PutRope[log, ".\n"]; IO.PutF[log, " Referenced: %G, Expires: %G, TTL: %G.\n", [time[this.referenced]], [integer[expires]],[integer[this.ttl]]]; IO.PutF[log, " NameCacheLoad: %G, NameToAddress: %G, NormalizeName: %G, Rejuvinates: %G.\n", [integer[this.nameCacheLoad]], [integer[this.nameToAddress]], [integer[this.normalizeName]], [integer[this.rejuvinations]]]; IO.PutF[log, " Name: %G.\n", [rope[this.name]]]; PrintListOfAddresses[log, " Addresses: ", this.addresses]; IO.PutRope[log, "\n"];}; IF alias # NIL THEN { now: BasicTime.GMT _ BasicTime.Now[]; source: ROPE _ IPName.AddressToName[alias.source]; expires: INT _ BasicTime.Period[from: now, to: alias.expires]; found _ TRUE; IO.PutF[log, "Alias Loaded: %G, From: %G", [time[alias.loaded]], [rope[source]]]; IF ~alias.authoritative THEN IO.PutF[log, ", NOT Authoritative **"]; IO.PutRope[log, ".\n"]; IO.PutF[log, " Referenced: %G, Expires: %G, TTL: %G.\n", [time[alias.referenced]], [integer[expires]], [integer[alias.ttl]]]; IO.PutF[log, " AliasToName: %G, Rejuvinates: %G.\n", [integer[alias.aliasToName]], [integer[alias.rejuvinations]]]; IO.PutF[log, " Alias: %G.\n", [rope[alias.alias]]]; IO.PutF[log, " Name: %G.\n", [rope[alias.name]]]; IO.PutRope[log, "\n"];}; IF mx # NIL THEN { now: BasicTime.GMT _ BasicTime.Now[]; source: ROPE _ IPName.AddressToName[mx.source]; expires: INT _ BasicTime.Period[from: now, to: mx.expires]; found _ TRUE; IO.PutF[log, "MX Loaded: %G, From: %G", [time[mx.loaded]], [rope[source]]]; IF ~mx.authoritative THEN IO.PutF[log, ", NOT Authoritative **"]; IO.PutRope[log, ".\n"]; IO.PutF[log, " Referenced: %G, Expires: %G, TTL: %G.\n", [time[mx.referenced]], [integer[expires]], [integer[mx.ttl]]]; IO.PutF[log, " NameToMX: %G, Rejuvinates: %G.\n", [integer[mx.nameToMX]], [integer[mx.rejuvinations]]]; IO.PutF[log, " Name: %G.\n", [rope[mx.name]]]; FOR list: LIST OF IPNameCache.MXHostInfo _ mx.mxHosts, list.rest UNTIL list = NIL DO IO.PutF[log, " MX Host: %G", [rope[list.first.host]]]; IO.PutF[log,", Preference: %G", [integer[list.first.preference]]]; IO.PutRope[log, "\n"]; ENDLOOP; IO.PutRope[log, "\n"]; }; IF bogus # NIL THEN { now: BasicTime.GMT _ BasicTime.Now[]; source: ROPE _ IPName.AddressToName[bogus.source]; expires: INT _ BasicTime.Period[from: now, to: bogus.expires]; found _ TRUE; IO.PutF[log, "Bogus Name Loaded: %G, From: %G", [time[bogus.loaded]], [rope[source]]]; IF ~bogus.authoritative THEN IO.PutF[log, ", NOT Authoritative **"]; IO.PutRope[log, ".\n"]; IO.PutF[log, " Referenced: %G, Expires: %G, TTL: %G.\n", [time[bogus.referenced]], [integer[expires]], [integer[bogus.ttl]]]; IO.PutF[log, " NameToBogus: %G, Rejuvinates: %G.\n", [integer[bogus.nameToBogus]], [integer[bogus.rejuvinations]]]; IO.PutF[log, " Bogus Name: %G.\n", [rope[bogus.bogus]]]; IO.PutRope[log, "\n"];}; IF down # NIL THEN { now: BasicTime.GMT _ BasicTime.Now[]; expires: INT _ BasicTime.Period[from: now, to: down.expires]; found _ TRUE; IO.PutF[log, "Down Name Loaded: %G", [time[down.loaded]]]; IO.PutRope[log, ".\n"]; IO.PutF[log, " Referenced: %G, Expires: %G, TTL: %G.\n", [time[down.referenced]], [integer[expires]], [integer[down.ttl]]]; IO.PutF[log, " NameToDown: %G.\n", [integer[down.nameToDown]]]; IO.PutF[log, " Down Name: %G.\n", [rope[down.name]]]; IO.PutRope[log, "\n"];}; IF zone # NIL THEN { now: BasicTime.GMT _ BasicTime.Now[]; source: ROPE _ IPName.AddressToName[zone.source]; expires: INT _ BasicTime.Period[from: now, to: zone.expires]; found _ TRUE; IO.PutF[log, "Zone Loaded: %G, From: %G", [time[zone.loaded]], [rope[source]]]; IF ~zone.authoritative THEN IO.PutF[log, ", NOT Authoritative **"]; IO.PutRope[log, ".\n"]; IO.PutF[log, " Referenced: %G, Expires: %G, TTL: %G.\n", [time[zone.referenced]], [integer[expires]], [integer[zone.ttl]]]; IO.PutF[log, " ZoneLookup: %G, Rejuvinates: %G.\n", [integer[zone.zoneLookup]], [integer[zone.rejuvinations]]]; IO.PutF[log, " Zone: %G.\n", [rope[zone.zone]]]; PrintListOfNames[log, " Servers: ", zone.servers]; IO.PutRope[log, "\n"];}; IF ~found THEN IO.PutRope[log, "Entry not found.\n"]; IO.PutRope[log, "\n"]; IO.Flush[log]; }; Addrs: Buttons.ButtonProc = { ENABLE UNWIND => NULL; data: ClientData _ NARROW[clientData]; log: IO.STREAM _ data.log; addresses: LIST OF AddressEntry = IPNameCache.GetAddresses[]; now: BasicTime.GMT _ BasicTime.Now[]; n: INT _ 0; FOR list: LIST OF AddressEntry _ addresses, list.rest UNTIL list = NIL DO this: AddressEntry _ list.first; source: ROPE _ IPName.AddressToName[this.source]; expires: INT _ BasicTime.Period[from: now, to: this.expires]; n _ n + 1; IO.PutF[log, "Address slot %G.\n", [integer[n]]]; IO.PutF[log, " Loaded: %G, From: %G", [time[this.loaded]], [rope[source]]]; IF ~this.authoritative THEN IO.PutF[log, ", NOT Authoritative **"]; IO.PutRope[log, ".\n"]; IO.PutF[log, " Referenced: %G, Expires: %G, TTL: %G.\n", [time[this.referenced]], [integer[expires]], [integer[this.ttl]]]; IO.PutF[log, " AddressCacheLoad: %G, AddressToName: %G, Rejuvinated: %G.\n", [integer[this.addressCacheLoad]], [integer[this.addressToName]], [integer[this.rejuvinations]]]; IO.PutF[log, " Address: %G.\n", [rope[IPName.AddressToRope[this.address]]]]; PrintListOfRopes[log, " Names: ", this.names]; IO.PutRope[log, "\n"]; ENDLOOP; IO.Flush[log]; }; Aliases: Buttons.ButtonProc = { ENABLE UNWIND => NULL; data: ClientData _ NARROW[clientData]; log: IO.STREAM _ data.log; aliases: LIST OF AliasEntry = IPNameCache.GetAliases[]; now: BasicTime.GMT _ BasicTime.Now[]; n: INT _ 0; FOR list: LIST OF AliasEntry _ aliases, list.rest UNTIL list = NIL DO this: AliasEntry _ list.first; source: ROPE _ IPName.AddressToName[this.source]; expires: INT _ BasicTime.Period[from: now, to: this.expires]; n _ n + 1; IO.PutF[log, "Alias slot %G.\n", [integer[n]]]; IO.PutF[log, " Loaded: %G, From: %G", [time[this.loaded]], [rope[source]]]; IF ~this.authoritative THEN IO.PutF[log, ", NOT Authoritative **"]; IO.PutRope[log, ".\n"]; IO.PutF[log, " Referenced: %G, Expires: %G, TTL: %G.\n", [time[this.referenced]], [integer[expires]], [integer[this.ttl]]]; IO.PutF[log, " AddressToName: %G, Rejuvinated: %G.\n", [integer[this.aliasToName]], [integer[this.rejuvinations]]]; IO.PutF[log, " Alias: %G.\n", [rope[this.alias]]]; IO.PutF[log, " Name: %G.\n", [rope[this.name]]]; IO.PutRope[log, "\n"]; ENDLOOP; IO.Flush[log]; }; MXs: Buttons.ButtonProc = { ENABLE UNWIND => NULL; data: ClientData _ NARROW[clientData]; log: IO.STREAM _ data.log; mxList: LIST OF MXEntry = IPNameCache.GetMXList[]; now: BasicTime.GMT _ BasicTime.Now[]; n: INT _ 0; FOR list: LIST OF MXEntry _ mxList, list.rest UNTIL list = NIL DO mx: MXEntry _ list.first; source: ROPE _ IPName.AddressToName[mx.source]; expires: INT _ BasicTime.Period[from: now, to: mx.expires]; n _ n + 1; IO.PutF[log, "MX slot %G.\n", [integer[n]]]; IO.PutF[log, "Loaded: %G, From: %G", [time[mx.loaded]], [rope[source]]]; IF ~mx.authoritative THEN IO.PutF[log, ", NOT Authoritative **"]; IO.PutRope[log, ".\n"]; IO.PutF[log, " Referenced: %G, Expires: %G, TTL: %G.\n", [time[mx.referenced]], [integer[expires]], [integer[mx.ttl]]]; IO.PutF[log, " NameToMX: %G, Rejuvinates: %G.\n", [integer[mx.nameToMX]], [integer[mx.rejuvinations]]]; IO.PutF[log, " Name: %G.\n", [rope[mx.name]]]; FOR list: LIST OF IPNameCache.MXHostInfo _ mx.mxHosts, list.rest UNTIL list = NIL DO IO.PutF[log, " MX Host: %G", [rope[list.first.host]]]; IO.PutF[log, ", Preference: %G", [integer[list.first.preference]]]; IO.PutRope[log, "\n"]; ENDLOOP; IO.PutRope[log, "\n"]; ENDLOOP; IO.Flush[log]; }; Bogus: Buttons.ButtonProc = { ENABLE UNWIND => NULL; data: ClientData _ NARROW[clientData]; log: IO.STREAM _ data.log; bogusAddresses: LIST OF BogusAddressEntry = IPNameCache.GetBogusAddresses[]; bogusNames: LIST OF BogusNameEntry = IPNameCache.GetBogusNames[]; now: BasicTime.GMT _ BasicTime.Now[]; n: INT _ 0; FOR list: LIST OF BogusNameEntry _ bogusNames, list.rest UNTIL list = NIL DO this: BogusNameEntry _ list.first; source: ROPE _ IPName.AddressToName[this.source]; expires: INT _ BasicTime.Period[from: now, to: this.expires]; n _ n + 1; IO.PutF[log, "Bogus slot %G.\n", [integer[n]]]; IO.PutF[log, " Loaded: %G, From: %G", [time[this.loaded]], [rope[source]]]; IF ~this.authoritative THEN IO.PutF[log, ", NOT Authoritative **"]; IO.PutRope[log, ".\n"]; IO.PutF[log, " Referenced: %G, Expires: %G, TTL: %G.\n", [time[this.referenced]], [integer[expires]], [integer[this.ttl]]]; IO.PutF[log, " N=>?: %G, R: %G.\n", [integer[this.nameToBogus]], [integer[this.rejuvinations]]]; IO.PutF[log, " Name: %G.\n", [rope[this.bogus]]]; IO.PutRope[log, "\n"]; ENDLOOP; FOR list: LIST OF BogusAddressEntry _ bogusAddresses, list.rest UNTIL list = NIL DO this: BogusAddressEntry _ list.first; source: ROPE _ IPName.AddressToName[this.source]; expires: INT _ BasicTime.Period[from: now, to: this.expires]; n _ n + 1; IO.PutF[log, "Bogus slot %G.\n", [integer[n]]]; IO.PutF[log, " Loaded: %G, From: %G", [time[this.loaded]], [rope[source]]]; IF ~this.authoritative THEN IO.PutF[log, ", NOT Authoritative **"]; IO.PutRope[log, ".\n"]; IO.PutF[log, " Referenced: %G, Expires: %G, TTL: %G.\n", [time[this.referenced]], [integer[expires]], [integer[this.ttl]]]; IO.PutF[log, " BogusNameLookup: %G, Rejuvinated: %G.\n", [integer[this.addressToBogus]], [integer[this.rejuvinations]]]; IO.PutF[log, " Address: %G.\n", [rope[IPName.AddressToRope[this.bogus]]]]; IO.PutRope[log, "\n"]; ENDLOOP; IO.Flush[log]; }; Down: Buttons.ButtonProc = { ENABLE UNWIND => NULL; data: ClientData _ NARROW[clientData]; log: IO.STREAM _ data.log; downAddresses: LIST OF DownAddressEntry = IPNameCache.GetDownAddresses[]; downNames: LIST OF DownNameEntry = IPNameCache.GetDownNames[]; downServers: LIST OF DownServerEntry = IPNameCache.GetDownServers[]; now: BasicTime.GMT _ BasicTime.Now[]; n: INT _ 0; FOR list: LIST OF DownNameEntry _ downNames, list.rest UNTIL list = NIL DO this: DownNameEntry _ list.first; expires: INT _ BasicTime.Period[from: now, to: this.expires]; n _ n + 1; IO.PutF[log, "Down name slot %G.\n", [integer[n]]]; IO.PutF[log, " Loaded: %G", [time[this.loaded]]]; IO.PutRope[log, ".\n"]; IO.PutF[log, " Referenced: %G, Expires: %G, TTL: %G.\n", [time[this.referenced]], [integer[expires]], [integer[this.ttl]]]; IO.PutF[log, " N=>?: %G.\n", [integer[this.nameToDown]]]; IO.PutF[log, " Name: %G.\n", [rope[this.name]]]; IO.PutRope[log, "\n\n"]; ENDLOOP; n _ 0; FOR list: LIST OF DownAddressEntry _ downAddresses, list.rest UNTIL list = NIL DO this: DownAddressEntry _ list.first; expires: INT _ BasicTime.Period[from: now, to: this.expires]; n _ n + 1; IO.PutF[log, "Down address slot %G.\n", [integer[n]]]; IO.PutF[log, " Loaded: %G", [time[this.loaded]]]; IO.PutRope[log, ".\n"]; IO.PutF[log, " Referenced: %G, Expires: %G, TTL: %G.\n", [time[this.referenced]], [integer[expires]], [integer[this.ttl]]]; IO.PutF[log, " DownNameLookup: %G.\n", [integer[this.addressToDown]]]; IO.PutF[log, " Address: %G.\n", [rope[IPName.AddressToRope[this.address]]]]; IO.PutRope[log, "\n\n"]; ENDLOOP; n _ 0; FOR list: LIST OF DownServerEntry _ downServers, list.rest UNTIL list = NIL DO this: DownServerEntry _ list.first; expires: INT _ BasicTime.Period[from: now, to: this.expires]; n _ n + 1; IO.PutF[log, "Down server slot %G.\n", [integer[n]]]; IO.PutF[log, " Loaded: %G", [time[this.loaded]]]; IO.PutRope[log, ".\n"]; IO.PutF[log, " Referenced: %G, Expires: %G, TTL: %G.\n", [time[this.referenced]], [integer[expires]], [integer[this.ttl]]]; IO.PutF[log, " DownServerLookup: %G.\n", [integer[this.serverToDown]]]; IO.PutF[log, " Address: %G.\n", [rope[IPName.AddressToRope[this.address]]]]; IO.PutRope[log, "\n"]; ENDLOOP; IO.Flush[log]; }; Names: Buttons.ButtonProc = { ENABLE UNWIND => NULL; data: ClientData _ NARROW[clientData]; log: IO.STREAM _ data.log; names: LIST OF NameEntry = IPNameCache.GetNames[]; now: BasicTime.GMT _ BasicTime.Now[]; n: INT _ 0; FOR list: LIST OF NameEntry _ names, list.rest UNTIL list = NIL DO this: NameEntry _ list.first; source: ROPE _ IPName.AddressToName[this.source]; expires: INT _ BasicTime.Period[from: now, to: this.expires]; n _ n + 1; IO.PutF[log, "Name slot %G.\n", [integer[n]]]; IO.PutF[log, " Loaded: %G, From: %G", [time[this.loaded]], [rope[source]]]; IF ~this.authoritative THEN IO.PutF[log, ", NOT Authoritative **"]; IO.PutRope[log, ".\n"]; IO.PutF[log, " Referenced: %G, Expires: %G, TTL: %G.\n", [time[this.referenced]], [integer[expires]], [integer[this.ttl]]]; IO.PutF[log, " NameCacheLoad: %G, NameToAddress: %G, NameToName: %G, Rejuvinated: %G.\n", [integer[this.nameCacheLoad]], [integer[this.nameToAddress]], [integer[this.normalizeName]], [integer[this.rejuvinations]]]; IO.PutF[log, " Name: %G.\n", [rope[this.name]]]; PrintListOfAddresses[log, " Addresses: ", this.addresses]; IO.PutRope[log, "\n"]; ENDLOOP; IO.Flush[log]; }; Zones: Buttons.ButtonProc = { ENABLE UNWIND => NULL; data: ClientData _ NARROW[clientData]; log: IO.STREAM _ data.log; servers: LIST OF ZoneEntry = IPNameCache.GetZones[]; now: BasicTime.GMT _ BasicTime.Now[]; n: INT _ 0; FOR list: LIST OF ZoneEntry _ servers, list.rest UNTIL list = NIL DO this: ZoneEntry _ list.first; source: ROPE _ IPName.AddressToName[this.source]; expires: INT _ BasicTime.Period[from: now, to: this.expires]; n _ n + 1; IO.PutF[log, "Zone slot %G.\n", [integer[n]]]; IO.PutF[log, " Loaded: %G, From: %G", [time[this.loaded]], [rope[source]]]; IF ~this.authoritative THEN IO.PutF[log, ", NOT Authoritative **"]; IO.PutF[log, ".\n"]; IO.PutF[data.log, " Referenced: %G, Expires: %G, TTL: %G.\n", [time[this.referenced]], [integer[expires]], [integer[this.ttl]]]; IO.PutF[log, " ZoneLookup: %G, Rejuvinated: %G.\n", [integer[this.zoneLookup]], [integer[this.rejuvinations]]]; IO.PutF[log, " Zone: %G\n", [rope[this.zone]]]; PrintListOfRopes[log, " Servers: ", this.servers]; IO.PutRope[log, "\n"]; ENDLOOP; IO.Flush[log]; }; Hist: Buttons.ButtonProc = { ENABLE UNWIND => NULL; data: ClientData _ NARROW[clientData]; log: IO.STREAM _ data.log; histograms: LIST OF HistogramEntry = IPNameCache.GetHistograms[]; FOR list: LIST OF HistogramEntry _ histograms, list.rest UNTIL list = NIL DO [] _ IPName.LoadCacheFromAddress[list.first.server, TRUE]; ENDLOOP; IO.PutRope[log, "\n"]; IO.PutF[log, "Names: %G, ", [integer[ListLength[IPNameCache.GetNames[]]]]]; IO.PutF[log, "Aliases: %G, ", [integer[ListLength[IPNameCache.GetAliases[]]]]]; IO.PutF[log, "Bogus Names: %G\n", [integer[ListLength[IPNameCache.GetBogusNames[]]]]]; IO.PutF[log, "Addresses: %G, ", [integer[ListLength[IPNameCache.GetAddresses[]]]]]; IO.PutF[log, "Bogus Addresses: %G\n", [integer[ListLength[IPNameCache.GetBogusAddresses[]]]]]; IO.PutF[log, "Zones: %G.\n", [integer[ListLength[IPNameCache.GetZones[]]]]]; FOR list: LIST OF HistogramEntry _ histograms, list.rest UNTIL list = NIL DO running: INT _ 0; this: HistogramEntry _ list.first; serverRope: ROPE = IPName.AddressToRope[this.server]; serverName: ROPE = IPName.AddressToName[this.server]; cacheHits: INT _ TotalCacheHits[this]; probes: INT _ TotalProbes[this]; querys: INT _ probes + cacheHits; IO.PutF[log, " Server: %G = %G, Miss rate: %1.2F%%.\n", [rope[serverRope]], [rope[serverName]], [real[100.0*probes/querys]] ]; IF this.lost # 0 THEN IO.PutF[log, " Packets Lost: %G, %1.2F%%.\n", [integer[this.lost]], [real[100.0*this.lost/probes]] ]; IO.PutF[log, " Cache Hits: %G.\n", [integer[cacheHits]] ]; FOR type: IPNameCache.CacheType IN IPNameCache.CacheType DO counter: INT _ this.cacheHits[type]; counterPerCent: REAL; IF counter = 0 THEN LOOP; counterPerCent _ 100.0*counter/cacheHits; IO.PutF[log, "%7G %7.2F%% %G\n", [integer[counter]], [real[counterPerCent]], [rope[cacheHitText[type]]] ]; ENDLOOP; IO.PutF[log, " Probes: %G.\n", [integer[probes]] ]; FOR type: IPNameCache.ProbeType IN IPNameCache.ProbeType DO counter: INT _ this.probes[type]; counterPerCent: REAL; IF counter = 0 THEN LOOP; counterPerCent _ 100.0*counter/probes; IO.PutF[log, "%7G %7.2F%% %G\n", [integer[counter]], [real[counterPerCent]], [rope[probeText[type]]] ]; ENDLOOP; IO.PutRope[log, " Response time:\n"]; FOR i: INT IN [0..IPNameCache.histogramSlots) DO counter: INT _ this.counters[i]; milliseconds: INT _ i*IPNameCache.histogramSlotSize; counterPerCent, runningPerCent: REAL; IF counter = 0 THEN LOOP; running _ running + counter; counterPerCent _ 100.0*counter/probes; runningPerCent _ 100.0*running/probes; IO.PutF[log, "%7G %7.2F %7.2F %7G\n", [integer[counter]], [real[counterPerCent]], [real[runningPerCent]], [integer[milliseconds]] ]; ENDLOOP; IO.PutRope[log, "\n"]; ENDLOOP; IO.PutRope[log, "\n"]; IO.Flush[log]; }; ListLength: PROCEDURE [arg: REF ANY] RETURNS [length: INT _ 0] = TRUSTED { FOR list: LIST OF REF ANY _ LOOPHOLE[arg], list.rest UNTIL list = NIL DO length _ length + 1; ENDLOOP; }; cacheHitText: ARRAY IPNameCache.CacheType OF ROPE = [ name: "Names", alias: "Aliases", bogusName: "Bogus Names", address: "Addresses", bogusAddress: "Bogus Addresses", zone: "Zones"]; TotalCacheHits: PROCEDURE [entry: HistogramEntry] RETURNS [total: INT _ 0] = { FOR type: IPNameCache.CacheType IN IPNameCache.CacheType DO total _ total + entry.cacheHits[type]; ENDLOOP; }; probeText: ARRAY IPNameCache.ProbeType OF ROPE = [ name: "Names", nameRejuvination: "Name Rejuvenation", aliasRejuvination: "Alias Rejuvenation", bogusNameRejuvination: "Bogus Name Rejuvenation", address: "Address", addressRejuvination: "Address Rejuvenation", bogusAddressRejuvination: "Bogus Address Rejuvenation", newZone: "New Zone", zoneRejuvination: "Zone Rejuvenation", newServer: "New Server"]; TotalProbes: PROCEDURE [entry: HistogramEntry] RETURNS [total: INT _ 0] = { FOR type: IPNameCache.ProbeType IN IPNameCache.ProbeType DO total _ total + entry.probes[type]; ENDLOOP; }; PrintListOfRopes: PROC [log: IO.STREAM, header: ROPE, ropes: LIST OF ROPE] = { IO.PutRope[log, header]; FOR list: LIST OF ROPE _ ropes, list.rest UNTIL list = NIL DO IF list # ropes THEN IO.PutRope[log, ", "]; IO.PutF[log, "\"%G\"", [rope[list.first]]]; ENDLOOP; IO.PutRope[log, "\n"]; }; PrintListOfAddresses: PROC [log: IO.STREAM, header: ROPE, addresses: LIST OF IPDefs.Address] = { IO.PutRope[log, header]; FOR list: LIST OF IPDefs.Address _ addresses, list.rest UNTIL list = NIL DO IF list # addresses THEN IO.PutRope[log, ", "]; IO.PutRope[log, IPName.AddressToRope[list.first]]; ENDLOOP; IO.PutRope[log, "\n"]; }; PrintListOfNames: PROC [log: IO.STREAM, header: ROPE, names: LIST OF ROPE] = { IO.PutRope[log, header]; FOR list: LIST OF ROPE _ names, list.rest UNTIL list = NIL DO IF list # names THEN IO.PutRope[log, ", "]; IO.PutRope[log, list.first]; ENDLOOP; IO.PutRope[log, "\n"]; }; 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]; }; 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 [parent, sibling: Viewer, clientData: REF _ NIL, name: ROPE, init: REF BOOL, change: BoolProc _ NIL, x, y: INTEGER _ 0] RETURNS [child: Viewer] = { bool: Bool _ NEW [BoolRec _ [ value: IF init # NIL THEN init ELSE NEW [BOOL _ TRUE], change: change, clientData: clientData, button: NIL ] ]; IF y = 0 THEN y _ sibling.wy + sibling.wh; IF x = 0 THEN x _ 2; 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; }; CreateOne[]; Commander.Register["IPNameTool", Create, "MailGateway name cache control."]; END. IPNameTool.mesa Hal Murray June 24, 1985 9:10:31 pm PDT John Larson, July 12, 1987 2:43:06 am PDT Sharon Johnson, April 10, 1987 10:02:06 am 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 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 Κ#΅˜šœ™J™'Icode™)K™.—J˜šΟk ˜ Jšœ œœ˜#Jšœœ8˜EJšœ œ˜(Jšœ œ$˜4Jšœœœ ˜/Jšœœ ˜Jšœœ˜Jšœœœ ˜Jšœœ ˜Jšœ œ˜'Jšœœ ˜Jšœ œ˜%Jšœ œM˜\Jšœ œ=˜NJšœœ ˜Jšœœr˜~Jšœ ˜ šœœΜ˜ίJ˜——šΟn œœ˜š˜Jšœ+œq˜ž—Jš˜J˜Jšœœœ˜Jšœœœœ˜šœœ˜$J˜—™Jšœœ˜Jšœ œ˜J˜—Jšœ œœ˜%šœœœ˜Jšœœœ˜Jšœœœ˜Jšœ˜Jšœ ˜ J˜—šœœ˜J˜—Jšœ œ˜(Jšœ œ˜*Jšœ œ˜$Jšœœ˜2Jšœœ˜0Jšœœ˜.Jšœœ!˜8Jšœœ ˜6Jšœœ˜4Jšœœ˜2šœ œ˜(J˜—JšΠbnœ+˜1J˜šŸ œœ˜Jšœœ˜+Jšœœ˜#J˜šœ!˜!J˜Jšœ1œœ˜L—Jšœ-˜-J˜šœΟc˜˜&šœ1œ˜7J˜—Jš œœœœ œ˜7—J˜J˜Jšœ˜—šœ˜Jšœ;œœ˜J—šœ4˜4JšœRœ˜YJ˜—Jšœ$˜$Jšœ$˜$Jšœ!˜!Jšœ ˜ JšœM˜OJšœœœ ˜-J˜—šž œœ+˜>Jšœœ˜šœ!˜!Jšœœœ'˜^—J˜%J˜šœ%˜%J˜ J˜J˜Jšœ˜Jšœ˜J˜—šœ#˜#J˜ J˜J˜ Jšœ˜Jšœ˜J˜—J˜J˜(Jšœ3˜3Jšœ5˜5Jšœ3˜3Jšœ3˜3Jšœ6˜6J˜J˜(Jšœ4˜4Jšœ6˜6Jšœ6˜6Jšœ:˜:Jšœ2˜2Jšœ6˜6Jšœ4˜4Jšœ6˜6Jšœ4˜4Jšœ2˜2Jšœ2˜2J˜J˜šœ˜Jšœœ˜Jšœœ˜)Jšœnœ˜uJ˜CJšœœ(˜>JšœR˜R—J˜J˜—šŸœ˜JšœL™LJšœœ ˜&Jšœœœ ˜Jšœ˜Jšœ7˜9J˜—šŸœ˜JšœL™LJšœœ ˜&Jšœœœ ˜Jšœœ'˜2Jšœ œœ˜"Jšœ:œœ˜FJš œœœœ(œ˜YJšœ(˜(Jšœ˜Jšœ0˜0J˜—šŸœ˜JšœL™LJšœœ ˜&Jšœœœ ˜Jšœœ'˜2Jšœœ˜ Jšœ:œœ˜GJ˜Jš œœœœ(œ˜YJšœ#˜#Jšœ:˜Jšœ œœ+˜=Jšœœœ5˜LJšœœœ3˜IJšœ œœ0˜DJšœœœ$˜2J˜šžœœœ˜Kšœ˜Kšœ ˜K˜—Jšœ$˜&Jšœ œœœ'˜=Jšœœ˜!š œœœœœ˜BJšœ*œœ˜Jšœœ˜ JšœO˜QJšœœœ%˜DJšœ˜Jšœ|˜~šœ ˜ Jšœ)˜)Jšœ˜Jšœ ˜ —Jšœ2˜4Jšœ0˜2Jšœ˜J˜—šœœœ˜Jšœœ˜%Jšœœ#˜/Jšœ œ/˜;Jšœœ˜ JšœI˜KJšœœœ%˜AJšœ˜Jšœv˜xšœ ˜ Jšœ&˜&Jšœ˜Jšœ˜—Jšœ-˜/š œœœ0œœ˜TJšœ6˜8Jšœ@˜BJšœ˜Jšœ˜—Jšœ˜J˜J˜—šœ œœ˜Jšœœ˜%Jšœœ&˜2Jšœ œ2˜>Jšœœ˜ JšœT˜VJšœœœ%˜DJšœ˜Jšœ|˜~šœ ˜ Jšœ)˜)Jšœ˜Jšœ ˜ —Jšœ7˜9Jšœ˜J˜—šœœœ˜Jšœœ˜%Jšœ œ1˜=Jšœœ˜ Jšœ8˜:Jšœ˜Jšœz˜|šœ ˜ Jšœ˜Jšœ˜—Jšœ4˜6Jšœ˜J˜—J˜šœœœ˜Jšœœ˜%Jšœœ%˜1Jšœ œ1˜=Jšœœ˜ JšœM˜OJšœœœ%˜CJšœ˜Jšœz˜|šœ ˜ Jšœ(˜(Jšœ˜Jšœ˜—Jšœ/˜1Jšœ4˜4Jšœ˜J˜—Jšœœœ$˜5Jšœ˜Jšœ˜J˜J˜—šŸœ˜JšœL™LJšœœœ˜Jšœœ ˜&Jšœœœ ˜Jšœ œœ+˜=Jšœœ˜%Jšœœ˜ š œœœ%œœ˜IJšœ ˜ Jšœœ%˜1Jšœ œ1˜=J˜ Jšœ/˜1JšœJ˜LJšœœœ%˜CJšœ˜Jšœz˜|šœ ˜ Jšœ@˜@Jšœ!˜!Jšœ˜Jšœ˜—JšœK˜MJšœ/˜/Jšœ˜Jšœ˜—Jšœ˜J˜—šŸœ˜JšœL™LJšœœœ˜Jšœœ ˜&Jšœœœ ˜Jšœ œœ'˜7Jšœœ˜%Jšœœ˜ š œœœ!œœ˜EJšœ˜Jšœœ%˜1Jšœ œ1˜=J˜ Jšœ-˜/JšœJ˜LJšœœœ%˜CJšœ˜Jšœz˜|šœ ˜ Jšœ*˜*Jšœ˜Jšœ˜—Jšœ1˜3Jšœ/˜1Jšœ˜Jšœ˜—Jšœ˜J˜—šžœ˜JšœL™LJšœœœ˜Jšœœ ˜&Jšœœœ ˜Jšœœœ#˜2Jšœœ˜%J˜Jšœœ˜ š œœœœœ˜AJšœ˜Jšœœ#˜/Jšœ œ/˜;J˜ Jšœ*˜,JšœF˜HJšœœœ%˜AJšœ˜Jšœv˜xšœ ˜ Jšœ&˜&Jšœ˜Jšœ˜—Jšœ-˜/š œœœ0œœ˜TJšœ6˜8JšœA˜CJšœ˜Jšœ˜—Jšœ˜Jšœ˜—Jšœ˜J˜J˜—šŸœ˜JšœL™LJšœœœ˜Jšœœ ˜&Jšœœœ ˜Jšœœœ5˜LJšœ œœ.˜AJšœœ˜%Jšœœ˜ š œœœ(œœ˜LJšœ"˜"Jšœœ%˜1Jšœ œ1˜=J˜ Jšœ-˜/JšœJ˜LJšœœœ%˜CJšœ˜Jšœz˜|šœ ˜ Jšœ˜Jšœ˜Jšœ˜—Jšœ0˜2Jšœ˜Jšœ˜—š œœœ/œœ˜SJšœ%˜%Jšœœ%˜1Jšœ œ1˜=J˜ Jšœ-˜/JšœJ˜LJšœœœ%˜CJšœ˜Jšœz˜|šœ ˜ Jšœ,˜,Jšœ˜Jšœ˜—JšœI˜KJšœ˜Jšœ˜—Jšœ˜J˜—šœŸœ˜JšœL™LJšœœœ˜Jšœœ ˜&Jšœœœ ˜Jšœœœ3˜IJšœ œœ,˜>Jšœ œœ0˜DJ˜Jšœœ˜%Jšœœ˜ š œœœ&œœ˜JJšœ!˜!Jšœ œ1˜=J˜ Jšœ1˜3Jšœ0˜2Jšœ˜Jšœz˜|Jšœ8˜:Jšœ/˜1Jšœ˜Jšœ˜J˜J˜—š œœœ-œœ˜QJšœ$˜$Jšœ œ1˜=J˜ Jšœ4˜6Jšœ0˜2Jšœ˜Jšœz˜|JšœE˜GJšœK˜MJšœ˜Jšœ˜J˜—J˜š œœœ*œœ˜NJšœ#˜#Jšœ œ1˜=J˜ Jšœ3˜5Jšœ0˜2Jšœ˜Jšœz˜|JšœF˜HJšœK˜MJšœ˜Jšœ˜J˜J˜J˜J˜—šœ˜J˜——šŸœ˜JšœL™LJšœœœ˜Jšœœ ˜&Jšœœœ ˜Jšœœœ$˜2Jšœœ˜%Jšœœ˜ š œœœœœ˜BJšœ˜Jšœœ%˜1Jšœ œ1˜=J˜ Jšœ,˜.JšœJ˜LJšœœœ%˜CJšœ˜Jšœz˜|šœ ˜ JšœM˜MJšœ˜Jšœ˜Jšœ˜Jšœ˜—Jšœ/˜1Jšœ;˜;Jšœ˜Jšœ˜—Jšœ˜J˜—šŸœ˜JšœL™LJšœœœ˜Jšœœ ˜&Jšœœœ ˜Jšœ œœ$˜4Jšœœ˜%Jšœœ˜ š œœœ œœ˜DJšœ˜Jšœœ%˜1Jšœ œ1˜=J˜ Jšœ,˜.JšœJ˜LJšœœœ%˜CJšœ˜Jšœ˜šœ ˜ Jšœ'˜'Jšœ˜Jšœ˜—Jšœ.˜0Jšœ3˜3Jšœ˜Jšœ˜—Jšœ˜J˜—šŸœ˜JšœL™LJšœœœ˜Jšœœ ˜&Jšœœœ ˜Jšœ œœ.˜Aš œœœ(œœ˜LJšœ4œ˜:Jšœ˜—Jšœ˜JšœI˜KJšœM˜OJšœT˜VJšœQ˜SJšœ\˜^JšœJ˜Lš œœœ(œœ˜LJšœ œ˜Jšœ"˜"Jšœ œ%˜5Jšœ œ%˜5Jšœ œ˜&Jšœœ˜ Jšœœ˜!Jšœ}˜Jšœœœf˜~Jšœ;˜=šœœ˜;Jšœ œ˜$Jšœœ˜Jšœ œœ˜Jšœ)˜)Jšœh˜jJšœ˜—Jšœ4˜6šœœ˜;Jšœ œ˜!Jšœœ˜Jšœ œœ˜Jšœ&˜&Jšœe˜gJšœ˜—Jšœ&˜(šœœœ!˜0Jšœ œ˜ Jšœœ#˜4Jšœ œ˜%Jšœ œœ˜Jšœ˜Jšœ&˜&Jšœ&˜&Jšœ‚˜„Jšœ˜—Jšœ˜Jšœ˜—Jšœ˜Jšœ˜J˜—šž œ œœœœ œœ˜Jšœœœœœœœœ˜HJšœ˜Jšœ˜ J˜——šœœœœ˜5Jšœ˜—šžœ œœ œ ˜Ošœœ˜;Jšœ&˜&Jšœ˜ —J˜—šœ œœœ˜2Jšœ˜Jšœ&˜&Jšœ(˜(Jšœ1˜1Jšœ˜Jšœ,˜,Jšœ7˜7Jšœ˜Jšœ&˜&Jšœ˜—šž œ œœ œ ˜Lšœœ˜;Jšœ#˜#Jšœ˜ —J˜—šžœœœœ œ œœœ˜NJšœ˜š œœœœœœ˜=Jšœœœ˜+Jšœ)˜+Jšœ˜—Jšœ˜J˜—šžœœœœ œ œœ˜`Jšœ˜š œœœ'œœ˜KJšœœœ˜/Jšœ0˜2Jšœ˜—Jšœ˜J˜—šžœœœœ œ œœœ˜NJšœ˜š œœœœœœ˜=Jšœœœ˜+Jšœ˜Jšœ˜—Jšœ˜J˜J˜—šžœœœ˜D˜šœœ˜%Jš œœ œœœ;˜[—Jšœœ˜—Jšœ)˜)J˜—š ž œœ!œœœœ˜z˜šœ+œ˜0J˜C—J˜ J˜Jšœœ˜ Jšœœ˜J˜——Jš œ œœœ œ˜EJšœœœ ˜šœ œœ˜Jšœœœ˜Jšœ˜Jšœ œ˜Jšœ˜J˜—šžœ˜Jšœ&œœœœœœœ˜wJšœ˜šœ œ ˜Jšœœœœœœœœ˜6J˜Jšœ˜Jšœœ˜—Jšœœ˜*Jšœœ˜šœ˜Jšœ+œ˜?Jšœ*œ œ˜=—Jšœ˜Jšœ œ2˜EJ˜—šŸ œ˜"JšœL™LJšœœ ˜&Jšœ œ ˜ Jšœ˜Jšœ œ4˜GJšœ5˜9Jšœœœ<˜UJ˜—šž œœ!œœ˜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˜Jšœ ˜ šœL˜LJ˜—Jšœ˜J˜J˜J˜—J˜—…—ƒj­9