DIRECTORY Ascii USING [CR, SP], Basics USING [BITAND, bytesPerWord, LowHalf], BasicTime USING [FromPupTime, GetClockPulses, GMT, Now, nullGMT, OutOfRange, Pulses, PulsesToMicroseconds], Buttons USING [Button, ButtonProc, Create, ReLabel, SetDisplayStyle], CommBuffer USING [Encapsulation], CommDriver USING [CreateInterceptor, DestroyInterceptor, GetNetworkChain, Interceptor, Network, RecvInterceptor, RecvType], CommDriverType USING [Encapsulation], Containers USING [ChildXBound, ChildYBound, Create], Convert USING [AppendTime], Endian USING [CardFromF, FWORD, HWORD], FS USING [Copy, Error, StreamOpen], Imager USING [black, Box, Context, MaskBox, Rectangle, SetColor, SetFont, SetXY, ShowText, white], ImagerBackdoor USING [GetBounds, GetCP], ImagerFont USING [Font, FontBoundingBox, RopeWidth], IO USING [Close, Put, PutChar, PutRope, STREAM], Labels USING [Create], Menus USING [MouseButton], PrincOpsUtils USING [LongCopy], Process USING [Abort, Detach, EnableAborts, Priority, priorityBackground, priorityNormal, SetPriority], Pup USING [Address, nullSocket, Socket], PupBuffer USING [Buffer, bytesPerRoutingInfoResponse, FileLookupReply, maxDataBytes, TimeResponse], PupName USING [AddressToRope, Error, NameLookup], PupSocket USING [GetMyAddress], PupType USING [bytesOfPupOverhead, CardFromSocket, ErrorCode, HeaderWithoutChecksum], PupWKS USING [bspSink, copyDisk, echo, eftp, fileLookup, ftp, gatewayInfo, gvLily, gvMSClientInput, gvMSForward, gvMSPoll, gvMSRetrieve, gvRSEnquiry, gvRSPoll, leaf, misc, rpc, telnet, teleSwat], Rope USING [Cat, Length, ROPE, Text], RPCPkt USING [Header], TeledebugProtocol USING [CoreStoreRequest, DiskAddressSetRequest], VFonts USING [EstablishFont], ViewerClasses USING [DestroyProc, PaintProc, Viewer, ViewerClass, ViewerClassRec], ViewerOps USING [CreateViewer, FindViewer, OpenIcon, PaintViewer, RegisterViewerClass, RestoreViewer], ViewerTools USING [GetContents, MakeNewTextViewer, SetContents, SetSelection], VM USING [AddressForPageNumber, Allocate, Free, Interval, PagesForWords]; PupWatch: MONITOR IMPORTS Basics, BasicTime, Buttons, CommDriver, Containers, Convert, Endian, FS, Imager, ImagerBackdoor, ImagerFont, IO, Labels, PrincOpsUtils, Process, Rope, PupName, PupSocket, PupType, VFonts, ViewerOps, ViewerTools, VM EXPORTS CommBuffer = { Encapsulation: PUBLIC TYPE = CommDriverType.Encapsulation; ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; MSec: PROC [rawClock: BasicTime.Pulses] RETURNS [LONG CARDINAL] = INLINE { RETURN[BasicTime.PulsesToMicroseconds[rawClock]/1000]; }; InputAction: TYPE = RECORD [SELECT act: * FROM fast => NULL, newHost => [name: ROPE], pauseContinue => NULL, replay => NULL, slow => NULL, start => NULL, stop => NULL, writeLog => [mouseButton: Menus.MouseButton, shift, control: BOOL], pktSize => [big: BOOL], ENDCASE]; bufferSize: INT; copySize: NAT; maxPupLength: NAT; bigPupLength: NAT = PupBuffer.maxDataBytes; smallPupLength: NAT = 100; showIdAndSockets: BOOL _ TRUE; showText: INT; -- Filters for WriteLog showBytes: BOOL; showWords: BOOL; Buffer: TYPE = LONG POINTER TO BufferData; BufferData: TYPE = MACHINE DEPENDENT RECORD [ encap: CommBuffer.Encapsulation, header: PupType.HeaderWithoutChecksum, pupBody: SELECT OVERLAID * FROM null => [], big => [bigChars: PACKED ARRAY [0..bigPupLength) OF CHAR], small => [smallChars: PACKED ARRAY [0..smallPupLength) OF CHAR], pupChars => [pupChars: PACKED ARRAY [0..0) OF CHAR], pupBytes => [pupBytes: PACKED ARRAY [0..0) OF [0..377B]], pupWords => [pupWords: ARRAY [0..0) OF CARDINAL], pupString => [pupString: StringBody], rfc => [address: Pup.Address], ack => [maximumBytesPerPup, numberOfPupsAhead, numberOfBytesAhead: CARDINAL], abort => [abortCode: CARDINAL, abortText: PACKED ARRAY [0..0) OF CHAR], error => [ error: RECORD [ header: PupType.HeaderWithoutChecksum, code: PupType.ErrorCode, options: Endian.HWORD, errorText: PACKED ARRAY [0..0) OF CHAR] ], addresses => [addresses: ARRAY [0..0) OF Pup.Address], fileLookupReply => [fileLookupReply: PupBuffer.FileLookupReply], time => [time: PupBuffer.TimeResponse], ENDCASE]; ContentsBytes: PROC [b: Buffer] RETURNS [CARDINAL] = INLINE { RETURN[b.header.byteLength - PupType.bytesOfPupOverhead]; }; nBuffers: CARDINAL = 1000; BufferIndex: TYPE = [0..nBuffers); bufferSpace: VM.Interval; buffers: LONG POINTER TO --ARRAY BufferIndex OF-- BufferData _ NIL; times: REF ARRAY BufferIndex OF BasicTime.Pulses _ NIL; lost: PACKED ARRAY BufferIndex OF BOOL _ ALL[FALSE]; logged: PACKED ARRAY BufferIndex OF BOOL _ ALL[FALSE]; wBuffer: BufferIndex _ 0; -- buffer being written by ethernet driver rBuffer: BufferIndex _ 0; -- buffer Looker wants to read fullBuffers: [0..nBuffers] _ 0; bufferChange: CONDITION; lookerWaiting: BOOL _ FALSE; allUsed: BOOL _ FALSE; -- whether all buffers have been written lookerProcess: PROCESS; AllocBuffers: ENTRY PROC [big: BOOL] = { IF buffers # NIL THEN FreeBuffers[]; bufferSize _ IF big THEN SIZE[big BufferData] ELSE SIZE[small BufferData]; copySize _ CARDINAL[bufferSize - SIZE[null BufferData]]; bufferSpace _ VM.Allocate[VM.PagesForWords[nBuffers * bufferSize]]; buffers _ VM.AddressForPageNumber[bufferSpace.page]; maxPupLength _ IF big THEN bigPupLength ELSE smallPupLength; times _ NEW[ARRAY BufferIndex OF BasicTime.Pulses]; InnerFlush[]; }; FreeBuffers: PROC = { VM.Free[bufferSpace]; times _ NIL; }; GetBuffer: ENTRY PROC RETURNS [BufferIndex] = { ENABLE UNWIND => lookerWaiting _ FALSE; WHILE fullBuffers = 0 DO lookerWaiting _ TRUE; WAIT bufferChange ENDLOOP; lookerWaiting _ FALSE; RETURN[rBuffer] }; ReturnBuffer: ENTRY PROC = { rBuffer _ IF rBuffer = LAST[BufferIndex] THEN 0 ELSE SUCC[rBuffer]; fullBuffers _ fullBuffers-1; NOTIFY bufferChange; }; ReplayBuffers: ENTRY PROC = { rBuffer _ IF allUsed THEN IF wBuffer = LAST[BufferIndex] THEN 0 ELSE SUCC[wBuffer] ELSE 0; fullBuffers _ (wBuffer+nBuffers-rBuffer) MOD nBuffers; IF lookerWaiting THEN NOTIFY bufferChange; }; FlushBuffers: ENTRY PROC = { InnerFlush[] }; InnerFlush: INTERNAL PROC = { rBuffer _ wBuffer _ 0; fullBuffers _ 0; allUsed _ FALSE; lost _ ALL[FALSE]; logged _ ALL[FALSE]; waitingForBuffers _ FALSE; NOTIFY bufferChange; }; waitingForBuffers: BOOL _ FALSE; NextBuffer: INTERNAL PROC = { IF fullBuffers >= nBuffers/2 OR ( waitingForBuffers AND fullBuffers+50 >= nBuffers/2 --make sure we have 50 free--) THEN { waitingForBuffers _ TRUE; lost[wBuffer] _ TRUE; RETURN }; waitingForBuffers _ FALSE; IF wBuffer = LAST[BufferIndex] THEN { allUsed _ TRUE; wBuffer _ 0 } ELSE wBuffer _ SUCC[wBuffer]; fullBuffers _ fullBuffers+1; IF lookerWaiting THEN NOTIFY bufferChange; logged[wBuffer] _ lost[wBuffer] _ FALSE; }; wanted: Pup.Address; myNet: CARDINAL _ 0; big: REF BOOL _ NEW[BOOL _ FALSE]; background: REF BOOL _ NEW[BOOL _ TRUE]; broadcast: REF BOOL _ NEW[BOOL _ FALSE]; route: REF BOOL _ NEW[BOOL _ TRUE]; misc: REF BOOL _ NEW[BOOL _ TRUE]; rpc: REF BOOL _ NEW[BOOL _ TRUE]; echo: REF BOOL _ NEW[BOOL _ TRUE]; error: REF BOOL _ NEW[BOOL _ TRUE]; raee: REF BOOL _ NEW[BOOL _ TRUE]; -- rfc, abort, end, endRep aData: REF BOOL _ NEW[BOOL _ TRUE]; -- data and aData, "data" clashes in Recv mark: REF BOOL _ NEW[BOOL _ TRUE]; -- mark and aMark ack: REF BOOL _ NEW[BOOL _ TRUE]; eftp: REF BOOL _ NEW[BOOL _ TRUE]; routing: REF BOOL _ NEW[BOOL _ TRUE]; Filter: PROC [who: Pup.Address] RETURNS [reject: BOOL] = { IF (who.net # wanted.net AND wanted.net # 0) AND (who.net # 0 OR wanted.net # myNet) THEN RETURN[TRUE]; IF (who.host # wanted.host AND wanted.host # 0) AND (who.host # 0 OR ~broadcast^) THEN RETURN[TRUE]; IF (who.socket # wanted.socket) AND (wanted.socket # Pup.nullSocket) THEN RETURN[TRUE]; RETURN[FALSE]; }; Recv: ENTRY CommDriver.RecvInterceptor = TRUSTED { myBuffer: BufferIndex = wBuffer; b: PupBuffer.Buffer = LOOPHOLE[buffer]; new: Buffer = LOOPHOLE[buffers + myBuffer * bufferSize]; IF Filter[b.dest] AND Filter[b.source] THEN RETURN; IF (b.dest.socket = PupWKS.gatewayInfo OR b.source.socket = PupWKS.gatewayInfo) AND ~route^ THEN RETURN; IF (b.dest.socket = PupWKS.misc OR b.source.socket = PupWKS.misc) AND ~misc^ THEN RETURN; IF (b.dest.socket = PupWKS.rpc OR b.source.socket = PupWKS.rpc) AND ~rpc^ THEN RETURN; IF (b.type = echoMe OR b.type = iAmEcho) AND ~echo^ THEN RETURN; IF b.type = error AND ~error^ THEN RETURN; IF (b.type = rfc OR b.type = abort OR b.type = end OR b.type = endRep) AND ~raee^ THEN RETURN; IF (b.type = data OR b.type = aData) AND ~aData^ THEN RETURN; IF (b.type = mark OR b.type = aMark) AND ~mark^ THEN RETURN; IF b.type = ack AND ~ack^ THEN RETURN; IF (b.type = eData OR b.type = eAck OR b.type = eEnd OR b.type = eAbort) AND ~eftp^ THEN RETURN; times[myBuffer] _ BasicTime.GetClockPulses[]; new.encap _ buffer.ovh.encap; PrincOpsUtils.LongCopy[ from: @buffer.data, to: @new.header, nwords: MIN[(b.byteLength+1)/2, copySize]]; NextBuffer[]; }; interceptor: CommDriver.Interceptor; network: CommDriver.Network _ CommDriver.GetNetworkChain[]; TakeEthernet: ENTRY PROC = { recvMask: PACKED ARRAY CommDriver.RecvType OF BOOL _ ALL[FALSE]; recvMask[pup] _ TRUE; interceptor _ CommDriver.CreateInterceptor[ network: network, sendMask: ALL[FALSE], sendProc: NIL, recvMask: recvMask, recvProc: Recv, data: NIL, promiscuous: TRUE]; }; GiveEthernet: ENTRY PROC = { CommDriver.DestroyInterceptor[interceptor]; }; inputActive: BOOL _ FALSE; lookerActive: BOOL _ FALSE; pauseWanted: BOOL _ FALSE; inputChange: CONDITION; lookerChange: CONDITION; wantedPriority: Process.Priority _ Process.priorityBackground; lookerPriority: Process.Priority; ActivateLooker: ENTRY PROC RETURNS [BOOL] = { ENABLE UNWIND => NULL; IF lookerPriority # wantedPriority THEN Process.SetPriority[lookerPriority _ wantedPriority]; IF inputActive OR pauseWanted THEN { WHILE inputActive OR pauseWanted DO WAIT inputChange ENDLOOP; RETURN[FALSE] } ELSE { lookerActive _ TRUE; RETURN[TRUE]; }; }; DeactivateLooker: ENTRY PROC = { lookerActive _ FALSE; IF inputActive THEN NOTIFY lookerChange; }; ActivateInput: ENTRY PROC = { ENABLE UNWIND => NULL; WHILE inputActive DO WAIT inputChange ENDLOOP; inputActive _ TRUE; WHILE lookerActive DO WAIT lookerChange ENDLOOP; }; DeactivateInput: ENTRY PROC = { inputActive _ FALSE; BROADCAST inputChange; }; mode: { display, disk} _ display; tabFrom: REAL; tabTo: REAL; tabAddr: REAL; tabPkt: REAL; tabData: REAL; diskFrom: CARDINAL; diskPkt: CARDINAL; InitTabs: PROC = { tabFrom _ GetLength["7777: "]; tabTo _ tabFrom + (GetLength["from"]-GetLength["to"]); tabAddr _ tabFrom + GetLength["from "]; tabPkt _ tabAddr + GetLength["377#777#777 "]; tabData _ tabPkt + GetLength["[aData,to:177777,L:1777] "]; diskFrom _ Rope.Length["9999: "]; diskPkt _ diskFrom + Rope.Length["from 377#377#777 "]; }; WriteChar: PROC [CHARACTER] _ DisplayChar; WriteMultiple: PROC [LONG DESCRIPTOR FOR PACKED ARRAY OF CHARACTER] _ DisplayMultiple; WriteDigit: PROC [card: CARDINAL] = INLINE { WriteChar['0+card]; }; WriteHexDigit: PROC [card: CARDINAL] = INLINE { SELECT card FROM IN [0..9] => WriteChar['0+card]; IN [10..15] => WriteChar['A+card-10]; ENDCASE => ERROR; }; WriteHexByte: PROC [card: CARDINAL] = INLINE { WriteHexDigit[card/16]; WriteHexDigit[card MOD 16]; }; WriteString: PROC [s: LONG STRING] = { WriteMultiple[DESCRIPTOR[@(s.text), s.length]]; }; WriteText: PROC [r: Rope.Text] = { s: LONG STRING = LOOPHOLE[r]; WriteMultiple[DESCRIPTOR[@(s.text), s.length]]; }; WriteTextOctal: PROC [r: Rope.Text, n: CARDINAL] = { s: LONG STRING = LOOPHOLE[r]; WriteMultiple[DESCRIPTOR[@(s.text), s.length]]; WriteOctal[n]; }; WritePup: PROC [b: Buffer] = { WriteMultiple[DESCRIPTOR[@(b.pupChars), MIN[ContentsBytes[b],maxPupLength]]]; }; WriteTail: PROC [b: Buffer, wordOffset: NAT] = { bytes: NAT _ MIN[ContentsBytes[b], maxPupLength]; byteOffset: NAT = wordOffset*Basics.bytesPerWord; bytesToPrint: INT = bytes-byteOffset; IF bytes < byteOffset THEN { WriteString["..."]; RETURN; }; WriteMultiple[DESCRIPTOR[@b.pupChars+wordOffset, bytesToPrint]]; IF bytes < bytesToPrint THEN WriteString["..."]; }; WritePupHex: PROC [b: Buffer] = { length: NAT _ ContentsBytes[b]; bytes: NAT _ MIN[length, maxPupLength]; WriteTextOctal["L:", length]; FOR i: NAT IN [0..bytes) DO IF (i MOD 2 = 0) THEN WriteString[","]; WriteHexByte[b.pupBytes[i]]; ENDLOOP; }; WriteOctal: PROC [n: CARDINAL] = { SELECT n FROM < 10B => GO TO Final; < 100B => { WriteDigit[(n / 10B)]; GO TO Final; }; < 1000B => { WriteDigit[(n / 100B)]; WriteDigit[(n / 10B) MOD 10B]; GO TO Final; }; >= 10000B => { IF n >= 100000B THEN WriteChar['1]; WriteDigit[(n / 10000B) MOD 10B]; }; ENDCASE; WriteDigit[(n / 1000B) MOD 10B]; WriteDigit[(n / 100B) MOD 10B]; WriteDigit[(n / 10B) MOD 10B]; GO TO Final; EXITS Final => WriteDigit[(n MOD 10B)]; }; WriteLongOctal: PROC [n: LONG CARDINAL] = { SELECT n FROM <= LAST[CARDINAL] => WriteOctal[n]; ENDCASE => { radix: CARDINAL = 8; radixPower: LONG CARDINAL _ 1; lwb: LONG CARDINAL _ n/radix; WHILE radixPower <= lwb DO radixPower _ radixPower*radix ENDLOOP; WHILE radixPower > 0 DO x: CARDINAL = n/radixPower; WriteDigit[x]; n _ n - x*radixPower; radixPower _ radixPower/radix; ENDLOOP; }; }; WriteId: PROC [id: Endian.FWORD] = { WriteLongOctal[Endian.CardFromF[id]]; }; WriteSocket: PROC [socket: Pup.Socket] = { WriteLongOctal[PupType.CardFromSocket[socket]]; }; WriteDecimal: PROC [n: CARDINAL] = { tenPower: CARDINAL _ 1; n10: CARDINAL _ n/10; WHILE tenPower <= n10 DO tenPower _ tenPower*10 ENDLOOP; WHILE tenPower > 0 DO x: CARDINAL = n/tenPower; WriteDigit[x]; n _ n - x*tenPower; tenPower _ tenPower/10; ENDLOOP; }; WriteLongDecimal: PROC [n: LONG CARDINAL] = { tenPower: LONG CARDINAL _ 1; n10: LONG CARDINAL _ n/10; WHILE tenPower <= n10 DO tenPower _ tenPower*10 ENDLOOP; WHILE tenPower > 0 DO x: CARDINAL = n/tenPower; WriteDigit[x]; n _ n - x*tenPower; tenPower _ tenPower/10; ENDLOOP; }; WriteAddr: PROC [addr: Pup.Address] = { lowSocket: WORD _ Basics.LowHalf[PupType.CardFromSocket[addr.socket]]; WriteOctal[addr.net]; WriteChar['#]; WriteOctal[addr.host]; WriteChar['#]; WriteOctal[Basics.BITAND[lowSocket,777B]]; }; WriteFullAddr: PROC [addr: Pup.Address] = { WriteOctal[addr.net]; WriteChar['#]; WriteOctal[addr.host]; WriteChar['#]; WriteLongOctal[PupType.CardFromSocket[addr.socket]]; }; WriteTo: PROC [id: Endian.FWORD, length: CARDINAL] = { n: LONG CARDINAL = Endian.CardFromF[id] + length; WriteTextOctal["to:", Basics.LowHalf[n]]; }; time: REF TEXT _ NEW[TEXT[20]]; WriteTime: PROC [gmt: BasicTime.GMT] = { time.length _ 0; time _ Convert.AppendTime[time, gmt, years, seconds]; WriteText[LOOPHOLE[time]]; }; prevMS: LONG CARDINAL _ 0; Watch: PROC [b: Buffer, time: BasicTime.Pulses] = { WriteOneAddress: PROC [b: Buffer] = { expected: CARDINAL = SIZE[Pup.Address, Basics.bytesPerWord]; bytes: CARDINAL = ContentsBytes[b]; IF bytes # expected THEN { WriteTextOctal["L:", bytes]; WriteText[","]; }; WriteFullAddr[b.address] }; WriteBootFileNumber: PROC [b: Buffer] = { bytes: CARDINAL = ContentsBytes[b]; IF bytes # 0 THEN { WriteTextOctal["L:", bytes]; WriteText[","]; }; WriteId[b.header.id]; }; DefaultBodyPrintout: PROC [b: Buffer] = { WriteTextOctal["L:", ContentsBytes[b]]; }; { misc: BOOL = b.header.source.socket = PupWKS.misc OR b.header.dest.socket = PupWKS.misc; lookupFile: BOOL = b.header.source.socket = PupWKS.fileLookup OR b.header.dest.socket = PupWKS.fileLookup; leaf: BOOL = b.header.source.socket = PupWKS.leaf OR b.header.dest.socket = PupWKS.leaf; teleSwat: BOOL = b.header.source.socket = PupWKS.teleSwat OR b.header.dest.socket = PupWKS.teleSwat; newMS: LONG CARDINAL = MSec[time]; SELECT (newMS-prevMS) FROM < 10000 => WriteDecimal[(newMS-prevMS)]; < 1000*LONG[1000] => { -- 1000 seconds WriteDecimal[(newMS-prevMS)/1000]; WriteChar['s]; } ENDCASE => WriteText[IF prevMS = 0 THEN "first" ELSE "long"]; prevMS _ newMS; WriteChar[':]; IF mode = display THEN { IF Filter[b.header.dest] THEN { SetPos[tabTo]; WriteText["to"]; SetPos[tabAddr]; WriteAddr[b.header.dest] } ELSE { SetPos[tabFrom]; WriteText["from"]; SetPos[tabAddr]; WriteAddr[b.header.source] }; SetPos[tabPkt]; } ELSE { DiskPos[diskFrom]; IF Filter[b.header.dest] THEN { WriteText[" to "]; WriteAddr[b.header.dest] } ELSE { WriteText["from "]; WriteAddr[b.header.source] }; DiskPos[diskPkt]; }; WriteChar['[]; { type: Rope.Text = SELECT b.header.type FROM echoMe => "echoMe,", iAmEcho => "iAmEcho,", badEcho => "badEcho,", error => "error,", rfc => "rfc,", abort => "abort,", end => "end,", endRep => "endRep,", data => "data,", aData => "aData,", ack => "ack,", mark => "mark,", int => "int,", intRep => "intRep,", aMark => "aMark,", eData => "eData,", eAck => "eAck,", eEnd => "eEnd,", eAbort => "eAbort,", rpp => "rpp,", LOOPHOLE[140B] => "RPC-call-end", LOOPHOLE[141B] => "RPC-data-end", LOOPHOLE[142B] => "RPC-ack", LOOPHOLE[143B] => "RPC-spare", LOOPHOLE[144B] => "RPC-RFA", LOOPHOLE[150B] => "RPC-a-call-end", LOOPHOLE[151B] => "RPC-a-data-end", LOOPHOLE[152B] => "RPC-ping", LOOPHOLE[153B] => "RPC-a-spare", LOOPHOLE[154B] => "RPC-a-RFA", LOOPHOLE[160B] => "RPC-call-more", LOOPHOLE[161B] => "RPC-data-more", LOOPHOLE[170B] => "RPC-a-call-more", LOOPHOLE[171B] => "RPC-a-data-more", gatewayRequest--200-- => IF lookupFile THEN "fileLookup," ELSE "gatewayRequest,", gatewayInfo--201-- => IF lookupFile THEN "fileInfo," ELSE "gatewayInfo", tenexTimeRequest--202-- => IF lookupFile THEN "fileError," ELSE IF teleSwat THEN "go," ELSE "tenexTimeRequest,", tenexTimeReply--203-- => IF teleSwat THEN "goReply," ELSE "tenexTimeReply,", LOOPHOLE[204B] => IF teleSwat THEN "ack" ELSE "pt204", altoTimeRequest--206-- => "altoTimeRequest", altoTimeReply--207-- => "altoTimeReply,", mailCheck--210-- => "mailCheck,", mailIsNew--211-- => "mailIsNew", mailNotNew--212-- => "mailNotNew", mailError--213-- => "mailError", mailCheckLaurel--214-- => "mailCheckL,", nameLookup--220-- => "nameLookup,", nameReply--221-- => "nameReply,", nameError--222-- => "nameError,", addressLookup--223-- => "addrLookup,", addressReply--224-- => "addressReply,", whereIsUser--230-- => "whereIsUser", userIs--231-- => "userIs,", userError--232-- => "userError,", netDirVersion--240-- => "netDirVersion,", sendNetDir--241-- => "sendNetDir,", bootFileSend--244-- => "bootFileSend,", kissOfDeath--247-- => "kissOfDeath,", userAuthReq--250-- => "userAuthReq,", userAuthOk--251-- => "userAuthOk,", userAuthBad--252-- => "userAuthBad,", bootDirReq--257-- => "bootDirReq,", bootDirReply--260-- => IF leaf THEN "leaf," ELSE "bootDirReply,", microcodeRequest--264-- => "microcodeRequest,", microcodeReply--265-- => "microcodeReply,", pageStoreRequest--300-- => "wCore,", pageFetchRequest--301-- => "rCore,", diskAddress--302-- => "diskAddr,", diskStoreRequest--303-- => "wDisk,", diskFetchRequest--304-- => "rDisk,", ENDCASE => NIL; IF type = NIL THEN { WriteTextOctal["pt", b.header.type.ORD]; WriteText[","]; } ELSE WriteText[type]; }; IF b.header.byteLength < PupType.bytesOfPupOverhead THEN { WriteTextOctal[" *** byteLength is too short: ", b.header.byteLength]; WriteChar[Ascii.CR]; RETURN; }; SELECT b.header.type FROM error => { WriteOctal[b.error.code.ORD]; WriteString[",id:"]; WriteId[b.header.id]; IF b.header.source # b.error.header.dest THEN { WriteString[",From:"]; WriteFullAddr[b.header.source]; }; WriteString[","]; WriteTail[b, SIZE[PupType.HeaderWithoutChecksum]+1+1]; }; eData => { WriteText["id="]; WriteId[b.header.id]; WriteString[","]; WritePupHex[b]; }; eAck, eEnd => { WriteText["id="]; WriteId[b.header.id]; }; abort => { WriteOctal[b.abortCode]; WriteString[","]; WriteTail[b, 1]; }; data, aData => { l: NAT = ContentsBytes[b]; WriteTo[b.header.id, l]; WriteTextOctal[",L:", l]; }; ack => { WriteTo[b.header.id, 0]; WriteTextOctal[",b/p:", b.maximumBytesPerPup]; WriteTextOctal[",pups:", b.numberOfPupsAhead]; WriteTextOctal[",bytes:", b.numberOfBytesAhead]; }; mark, aMark => { WriteTo[b.header.id, 1]; WriteTextOctal[",mk:", b.pupBytes[0]]; }; iAmEcho => WritePupHex[b]; rfc, echoMe => { wellKnown: Rope.Text = SELECT b.header.dest.socket FROM PupWKS.telnet => "telnet", PupWKS.gatewayInfo => "gateway", PupWKS.ftp => "ftp", PupWKS.misc => "misc", PupWKS.echo => "echo", PupWKS.bspSink => "bspSink", PupWKS.eftp => "eftp", PupWKS.copyDisk => "copyDisk", PupWKS.rpc => "rpc", PupWKS.gvRSEnquiry => "RS-Enquiry", PupWKS.gvRSPoll => "RS-Poll", PupWKS.gvLily => "GV-Lily", PupWKS.gvMSPoll => "MS-Poll", PupWKS.gvMSForward => "MS-Forward", PupWKS.gvMSClientInput => "MS-Send", PupWKS.gvMSRetrieve => "MS-Retrieve", ENDCASE => NIL; IF wellKnown # NIL THEN WriteText[wellKnown]; SELECT b.header.type FROM rfc => { WriteString[","]; WriteId[b.header.id]; WriteString[","]; WriteFullAddr[b.address]; }; echoMe => { WriteString[","]; WritePupHex[b]; }; ENDCASE => NULL; }; end, endRep, int, intRep => WriteId[b.header.id]; IN [LOOPHOLE[140B]..LOOPHOLE[171B]] => { h: LONG POINTER TO RPCPkt.Header = LOOPHOLE[@b.header]; overhead: CARDINAL = SIZE[RPCPkt.Header]+1--checksum--; WriteText[",Len:"]; IF h.length < overhead THEN WriteTextOctal["**?-", overhead-h.length] ELSE WriteOctal[h.length-overhead]; WriteTextOctal[",Cnv:", h.conv.ls MOD 1000B]; WriteTextOctal[",Call:", h.pktID.callSeq MOD 1000B]; WriteTextOctal[",Pkt:", h.pktID.pktSeq MOD 1000B]; IF h.type.class = call THEN WriteTextOctal[",Disp:", h.dispatcher.dispatcherHint MOD 1000B]; }; altoTimeRequest => NULL; altoTimeReply => { pupTime: LONG CARDINAL _ Endian.CardFromF[b.time.time]; gmt: BasicTime.GMT _ BasicTime.nullGMT; gmt _ BasicTime.FromPupTime[pupTime ! BasicTime.OutOfRange => CONTINUE]; IF gmt = BasicTime.nullGMT THEN WriteLongOctal[pupTime] ELSE WriteTime[gmt]; }; mailCheck, mailCheckLaurel => WritePup[b]; mailIsNew, mailNotNew => NULL; nameLookup => WritePup[b]; nameReply => { bytes: NAT _ MIN[ContentsBytes[b], maxPupLength]; answers: NAT _ bytes/SIZE[Pup.Address, Basics.bytesPerWord]; FOR i: NAT IN [0..answers) DO IF i # 0 THEN WriteText[","]; WriteFullAddr[b.addresses[i]]; ENDLOOP; }; nameError => WritePup[b]; addressLookup => WriteFullAddr[b.address]; addressReply => WritePup[b]; gatewayRequest => IF lookupFile THEN WritePup[b]; gatewayInfo => IF lookupFile THEN { pupTime: LONG CARDINAL _ Endian.CardFromF[b.fileLookupReply.createTime]; gmt: BasicTime.GMT _ BasicTime.nullGMT; gmt _ BasicTime.FromPupTime[pupTime ! BasicTime.OutOfRange => CONTINUE]; WriteText["V:"]; WriteDecimal[b.fileLookupReply.version]; WriteText[",T:"]; IF gmt = BasicTime.nullGMT THEN WriteLongOctal[pupTime] ELSE WriteTime[gmt]; WriteText[",L:"]; WriteLongDecimal[Endian.CardFromF[b.fileLookupReply.length]]; } ELSE { -- Gateway entries: CARDINAL = ContentsBytes[b] / PupBuffer.bytesPerRoutingInfoResponse; WriteTextOctal[",N:", entries]; }; userAuthReq => WriteString[@(b.pupString)]; userAuthOk => DefaultBodyPrintout[b]; userAuthBad => DefaultBodyPrintout[b]; netDirVersion => { bytes: CARDINAL _ ContentsBytes[b]; WriteText["Old:"]; WriteDecimal[b.pupWords[0]]; IF bytes = 4 THEN { WriteText[",New:"]; WriteDecimal[b.pupWords[1]]; }; }; sendNetDir => WriteOneAddress[b]; bootFileSend => WriteBootFileNumber[b]; bootDirReply => { SELECT TRUE FROM leaf => WritePupHex[b]; ENDCASE => DefaultBodyPrintout[b]; }; microcodeRequest => WriteBootFileNumber[b]; microcodeReply => DefaultBodyPrintout[b]; pageStoreRequest, pageFetchRequest => IF teleSwat THEN WriteLongOctal[LOOPHOLE[@b.pupBody, LONG POINTER TO TeledebugProtocol.CoreStoreRequest].page]; diskAddress => IF teleSwat AND ContentsBytes[b] = 2*SIZE[TeledebugProtocol.DiskAddressSetRequest] THEN WriteLongOctal[LOOPHOLE[@b.pupBody, LONG POINTER TO TeledebugProtocol.DiskAddressSetRequest].page]; ENDCASE => DefaultBodyPrintout[b]; WriteChar[']]; SELECT TRUE FROM mode = disk => { IF showIdAndSockets THEN { WriteChar[Ascii.CR]; DiskPos[diskPkt]; WriteText["id="]; WriteId[b.header.id]; WriteText[" "]; WriteFullAddr[b.header.dest]; WriteText[" <= "]; WriteFullAddr[b.header.source]; }; { length: CARDINAL = MIN[ContentsBytes[b], maxPupLength]; IF showText > 0 THEN { WriteChar[Ascii.CR]; DiskPos[diskPkt]; IF length = 0 THEN WriteText["{empty}"] ELSE { chars: INT _ MIN[length, showText]; WriteMultiple[DESCRIPTOR[@(b.pupChars), chars]]; }; }; IF length # 0 AND showBytes THEN { WriteChar[Ascii.CR]; DiskPos[diskPkt]; WriteText["Bytes="]; FOR i: CARDINAL IN [0..length) DO WriteOctal[b.pupBytes[i]]; WriteChar[Ascii.SP] ENDLOOP; }; IF length # 0 AND showWords THEN { WriteChar[Ascii.CR]; DiskPos[diskPkt]; WriteText["Words="]; FOR i: CARDINAL IN [0..length/2) DO WriteOctal[b.pupWords[i]]; WriteChar[Ascii.SP] ENDLOOP; }; }; }; b.header.type = data, b.header.type = aData => { SetPos[tabData]; WritePup[b]; }; ENDCASE; WriteChar[Ascii.CR]; }; }; speed: {slow, fast} _ fast; lookerCount: CARDINAL; StartPause: PROC = { WriteText["Pausing ..."]; SendNow[]; NotePausing[TRUE]; pauseWanted _ TRUE; }; LookerMain: PROC = { Process.SetPriority[lookerPriority _ wantedPriority]; DO ENABLE ABORTED => EXIT; this: BufferIndex = GetBuffer[]; IF ActivateLooker[] THEN { IF lost[this] THEN { WriteText["Lost packet(s)"]; WriteChar[Ascii.CR]; }; Watch[buffers + this * bufferSize, times[this]]; ReturnBuffer[]; IF speed = slow AND (lookerCount _ lookerCount+1) >= threeQuarterScreen THEN StartPause[]; DeactivateLooker[]; }; ENDLOOP; }; WriteDiskLog: PROC = { WriteChar _ DiskChar; WriteMultiple _ DiskMultiple; mode _ disk; WriteChar[Ascii.CR]; ReplayBuffers[]; IF logged[GetBuffer[]] THEN { WriteText["{ continued from previous logged packets }"]; WriteChar[Ascii.CR]; FOR i: NAT IN [0..fullBuffers) DO this: BufferIndex = GetBuffer[]; IF logged[this] THEN prevMS _ MSec[times[this]] ELSE EXIT; ReturnBuffer[]; ENDLOOP; } ELSE { WriteTextOctal["Watching host ", wanted.net]; WriteTextOctal["#", wanted.host]; WriteText["#"]; WriteChar[Ascii.CR]; }; FOR i: NAT IN [0..fullBuffers) DO this: BufferIndex = GetBuffer[]; IF lost[this] THEN { WriteText["Lost packet(s)"]; WriteChar[Ascii.CR]; }; Watch[buffers + this * bufferSize, times[this]]; logged[this] _ TRUE; ReturnBuffer[]; ENDLOOP; DiskCommit[]; WriteChar _ DisplayChar; WriteMultiple _ DisplayMultiple; mode _ display; }; WriteTitle: PROC = { DisplayTitle[Rope.Cat["PupWatch: watching host ", PupName.AddressToRope[wanted]]]; }; DoAction: PROC [act: InputAction] = { alreadyPaused: BOOL; ActivateInput[]; alreadyPaused _ pauseWanted; IF pauseWanted AND act.act # pauseContinue THEN WriteChar[Ascii.CR]; lookerCount _ 0; WITH act: act SELECT FROM fast => { WriteText["Fast"]; speed _ fast; NoteSlow[FALSE]; pauseWanted _ FALSE; }; newHost => { WriteText["Host ... "]; SendNow[]; GiveEthernet[]; -- stops driver SELECT Lookup[act.name] FROM ok => { WriteText["ok"]; SendNow[]; Clear[]; FlushBuffers[]; WriteTitle[]; pauseWanted _ FALSE; }; noResponse => WriteText["no name-lookup response"]; noRoute => WriteText["no route to that host"]; badName => WriteText["name not found"]; ENDCASE => ERROR; TakeEthernet[]; }; -- starts driver pktSize => AllocBuffers[big: act.big]; replay => { Clear[]; WriteText["Replay (Slow)"]; ReplayBuffers[]; speed _ slow; NoteSlow[TRUE]; pauseWanted _ FALSE; }; slow => { WriteText["Slow"]; speed _ slow; NoteSlow[TRUE]; pauseWanted _ FALSE; }; start => { InitTabs[]; AllocBuffers[big: FALSE]; wanted _ PupSocket.GetMyAddress[]; myNet _ wanted.net; WriteTitle[]; pauseWanted _ FALSE; lookerProcess _ FORK LookerMain[]; TakeEthernet[]; }; stop => { Process.Abort[lookerProcess]; JOIN lookerProcess; GiveEthernet[]; FreeBuffers[]; }; writeLog => { showBytes _ act.shift; showWords _ act.control; SELECT act.mouseButton FROM red => showText _ 0; -- Left yellow => showText _ 50; -- Middle blue => showText _ 1000; -- Right ENDCASE => showText _ 1000; WriteText["Writing log file ... "]; SendNow[]; WriteDiskLog[]; WriteText["ok"]; showText _ 1000; }; pauseContinue => IF pauseWanted THEN { WriteText[" continuing"]; pauseWanted _ FALSE; } ELSE StartPause[]; ENDCASE => ERROR; IF ~pauseWanted OR alreadyPaused THEN WriteChar[Ascii.CR]; IF ~pauseWanted AND act.act # stop THEN NotePausing[FALSE]; DeactivateInput[]; }; LookupOutcome: TYPE = { ok, badName, noResponse, noRoute }; Lookup: PROC [name: ROPE] RETURNS [outcome: LookupOutcome] = { outcome _ ok; wanted _ PupName.NameLookup[IF name.Length[] = 0 THEN "ME" ELSE name, Pup.nullSocket ! PupName.Error => { outcome _ SELECT code FROM noRoute => noRoute, noResponse => noResponse, ENDCASE => badName; CONTINUE }]; }; font: ImagerFont.Font = VFonts.EstablishFont["Helvetica",8]; fontAscent: REAL _ ImagerFont.FontBoundingBox[font].ascent; fontDescent: REAL _ ImagerFont.FontBoundingBox[font].descent; fontSpace: REAL _ fontAscent+fontDescent; topPos: REAL = fontAscent; myViewer: Viewer; pause, fast, slow, hostText: Viewer; line: REF TEXT = NEW[TEXT[500+3*bigPupLength]]; xPos: REAL _ 0.0; yPos: REAL _ topPos; -- offset from top of text area -- DisplayChar: PROC [c: CHAR] = { IF c = Ascii.CR THEN { SendNow[]; xPos _ 0.0; yPos _ yPos + fontSpace; } ELSE { line[line.length] _ c; line.length _ line.length+1 }; }; DisplayMultiple: PROC [desc: LONG DESCRIPTOR FOR PACKED ARRAY OF CHAR] = { amount: CARDINAL = MIN[line.maxLength-line.length, LENGTH[desc]]; IF line.length MOD 2 = 0 THEN PrincOpsUtils.LongCopy[ from: BASE[desc], nwords: (amount+1)/2, to: LOOPHOLE[line,LONG POINTER]+SIZE[TEXT[0]]+line.length/2] ELSE FOR i: CARDINAL IN [0..amount) DO line[line.length+i] _ desc[i]; ENDLOOP; line.length _ line.length + amount; }; GetLength: PROC [r: ROPE] RETURNS [length: REAL] = { RETURN[ImagerFont.RopeWidth[font, r].x] }; SetPos: PROC [pos: REAL] = { SendNow[]; xPos _ pos; }; SendNow: PROC = { ViewerOps.PaintViewer[myViewer, client, FALSE, line]; line.length _ 0; }; Clear: PROC = { ViewerOps.PaintViewer[myViewer, client, FALSE, $Clear]; }; DisplayTitle: PROC [rope: ROPE] = { myViewer.parent.name _ rope; ViewerOps.PaintViewer[myViewer.parent, caption, FALSE, NIL]; }; myClass: ViewerClasses.ViewerClass = NEW[ViewerClasses.ViewerClassRec _[ paint: MyPaint, destroy: MyDestroy, icon: tool]]; screenLines: CARDINAL _ 0; threeQuarterScreen: CARDINAL _ 0; MyPaint: ViewerClasses.PaintProc = TRUSTED { bounds: Imager.Rectangle = ImagerBackdoor.GetBounds[context]; box: Imager.Box _ [xmin: bounds.x, xmax: bounds.x+bounds.w, ymin: bounds.y, ymax: bounds.y+bounds.h]; IF whatChanged = NIL THEN { tempY: REAL _ topPos; yPos _ topPos; screenLines _ 0; WHILE box.ymax - tempY - fontDescent >= box.ymin DO screenLines _ screenLines+1; tempY _ tempY + fontSpace; ENDLOOP; threeQuarterScreen _ (screenLines * 3) / 4; RETURN }; WITH whatChanged SELECT FROM text: REF TEXT => { IF box.ymax - yPos - fontDescent < box.ymin THEN yPos _ topPos; IF xPos = 0.0 THEN { yBase: REAL = IF box.ymax - yPos - fontDescent - fontSpace < box.ymin THEN topPos ELSE yPos + fontSpace; Imager.SetColor[context, Imager.white]; Imager.MaskBox[context, [ xmin: box.xmin, ymin: box.ymax - yBase - fontDescent, xmax: box.xmax, ymax: box.ymax - yBase + fontAscent]]; Imager.SetColor[context, Imager.black]; }; Imager.SetXY[context, [box.xmin + xPos, box.ymax - yPos]]; Imager.SetFont[context, font]; FOR i: NAT IN [0..text.length) DO IF text[i] > '\177 THEN text[i] _ '\177; ENDLOOP; Imager.ShowText[context, text]; xPos _ ImagerBackdoor.GetCP[context].x - box.xmin; }; x: ATOM => SELECT x FROM $Clear => { Imager.SetColor[context, Imager.white]; Imager.MaskBox[context, [xmin: box.xmin, ymin: box.ymin, xmax: box.xmax, ymax: box.ymax - topPos + fontAscent]]; xPos _ 0.0; yPos _ topPos; }; ENDCASE => NULL; ENDCASE => NULL; }; MyDestroy: ViewerClasses.DestroyProc = TRUSTED { Process.Detach[FORK DoAction[[stop[]]]]; }; active: ATOM = $WhiteOnBlack; Create: PROC RETURNS [text: Viewer] = { outer: Viewer = Containers.Create[ info: [name: "PupWatch", column: right, scrollable: FALSE, iconic: TRUE]]; child: Viewer _ CreateChildren[outer]; Buttons.SetDisplayStyle[fast, active, FALSE]; text _ ViewerOps.CreateViewer[ flavor: $PupWatch, info: [parent: outer, scrollable: FALSE, border: FALSE, wx: 2, wy: child.wy + child.wh + 2]]; Containers.ChildXBound[outer, text]; Containers.ChildYBound[outer, text]; ViewerOps.OpenIcon[outer]; }; CreateChildren: PROC [v: Viewer] RETURNS [child: Viewer] = { InputButton: PROC [name: ROPE, action: InputAction] = { child _ Buttons.Create[ info: [name: name, parent: v, border: TRUE, wx: IF child = NIL THEN 2 ELSE 2+child.wx+child.ww, wy: IF child = NIL THEN 1 ELSE child.wy], proc: DoSimpleAction, clientData: NEW[InputAction _ action], fork: TRUE]; }; SimpleLabel: PROC [name: ROPE, newLine: BOOL _ FALSE] = { child _ Labels.Create[ info: [name: name, parent: v, border: FALSE, wx: IF newLine THEN 2 ELSE 2+child.wx+child.ww, wy: IF newLine THEN child.wy + child.wh + 2 ELSE child.wy]]; }; SimpleButton: PROC [name: ROPE, proc: Buttons.ButtonProc, newLine: BOOL _ FALSE, border: BOOL _ TRUE] RETURNS [Viewer] = { child _ Buttons.Create[ info: [name: name, parent: v, border: border, wx: IF newLine THEN 2 ELSE 2+child.wx+child.ww, wy: IF newLine THEN child.wy + child.wh + 2 ELSE child.wy], proc: proc, fork: TRUE]; RETURN [child]; }; child _ NIL; InputButton["Continue", [pauseContinue[]]]; pause _ child; InputButton["Fast", [fast[]]]; fast _ child; InputButton["Slow", [slow[]]]; slow _ child; InputButton["Replay", [replay[]]]; [] _ SimpleButton["WriteLog", DoWriteLog]; [] _ SimpleButton["NewHost", DoHost]; [] _ SimpleButton[" Host: ", DoHostPrompt, FALSE, TRUE]; hostText _ ViewerTools.MakeNewTextViewer[ info: [parent: v, scrollable: FALSE, border: FALSE, wx: 2+child.wx+child.ww, wy: child.wy, ww: v.cw-(2+child.wx+child.ww), wh: child.wh]]; Containers.ChildXBound[v, hostText]; child _ MakeBool[name: "Bkg", init: background, change: DoPriority, parent: v, x: 2, y: child.wy+child.wh+2]; child _ MakeBool[name: "Big", init: big, change: DoSize, parent: v, x: child.wx+child.ww+2, y: child.wy]; child _ MakeBool[name: "Bcst", init: broadcast, parent: v, x: child.wx+child.ww+5, y: child.wy]; child _ MakeBool[name: "Route", init: route, parent: v, x: child.wx+child.ww+5, y: child.wy]; child _ MakeBool[name: "Misc", init: misc, parent: v, x: child.wx+child.ww+2, y: child.wy]; child _ MakeBool[name: "RPC", init: rpc, parent: v, x: child.wx+child.ww+2, y: child.wy]; child _ MakeBool[name: "Echo", init: echo, parent: v, x: child.wx+child.ww+5, y: child.wy]; child _ MakeBool[name: "Err", init: error, parent: v, x: child.wx+child.ww+2, y: child.wy]; child _ MakeBool[name: "RAEE", init: raee, parent: v, x: child.wx+child.ww+2, y: child.wy]; child _ MakeBool[name: "Data", init: aData, parent: v, x: child.wx+child.ww+2, y: child.wy]; child _ MakeBool[name: "Mark", init: mark, parent: v, x: child.wx+child.ww+2, y: child.wy]; child _ MakeBool[name: "Ack", init: ack, parent: v, x: child.wx+child.ww+2, y: child.wy]; child _ MakeBool[name: "Eftp", init: eftp, parent: v, x: child.wx+child.ww+2, y: child.wy]; }; 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 = TRUSTED { 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^]; }; NotePausing: ENTRY PROC [nowPausing: BOOL] = { Buttons.ReLabel[pause, IF nowPausing THEN "Continue" ELSE "Pause"]; }; DoSimpleAction: Buttons.ButtonProc = TRUSTED { DoAction[NARROW[clientData, REF InputAction]^]; }; isSlow: BOOL _ FALSE; NoteSlow: ENTRY PROC [nowSlow: BOOL] = { Buttons.SetDisplayStyle[IF isSlow THEN slow ELSE fast, $BlackOnWhite]; isSlow _ nowSlow; Buttons.SetDisplayStyle[IF isSlow THEN slow ELSE fast, active]; }; DoWriteLog: Buttons.ButtonProc = TRUSTED { DoAction[[writeLog[mouseButton, shift, control]]]; }; DoHost: Buttons.ButtonProc = TRUSTED { DoAction[[newHost[ViewerTools.GetContents[hostText]]]]; }; DoHostPrompt: Buttons.ButtonProc = TRUSTED { text: Viewer = hostText; SELECT mouseButton FROM red => ViewerTools.SetSelection[text, NIL]; blue => { ViewerTools.SetContents[text, NIL]; ViewerTools.SetSelection[text, NIL] }; yellow => NULL; ENDCASE => ERROR; }; DoPriority: BoolProc = TRUSTED { IF background^ THEN wantedPriority _ Process.priorityBackground ELSE wantedPriority _ Process.priorityNormal; }; DoSize: BoolProc = TRUSTED { DoAction[[pktSize[big: big^]]]; }; logName: Rope.ROPE = "PupWatch.log"; file: IO.STREAM; lineLength: NAT _ 0; lineMaxlength: NAT = 100; firstChar: BOOL _ TRUE; firstTime: BOOL _ TRUE; DiskChar: PUBLIC PROC [c: CHAR] = { IF firstChar THEN { firstChar _ FALSE; IF firstTime THEN { firstTime _ FALSE; file _ FS.StreamOpen[logName, $create]; } ELSE { [] _ FS.Copy[from: logName, to: logName, keep: 1 ! FS.Error => CONTINUE]; file _ FS.StreamOpen[logName, $append]; }; LogTime[]; }; IF c = Ascii.CR THEN { IO.PutChar[file, c]; lineLength _ 0 } ELSE { IO.PutChar[file, c]; lineLength _ lineLength+1 }; }; LogTime: PROC = { IO.PutRope[file, "PupWatch.log written at "]; IO.Put[file, [time[BasicTime.Now[]]]]; }; DiskMultiple: PUBLIC PROC [desc: LONG DESCRIPTOR FOR PACKED ARRAY OF CHAR] = { FOR i: CARDINAL IN [0..LENGTH[desc]) DO c: CHAR = desc[i]; IF c IN [40C..176C] THEN DiskChar[c] ELSE DiskChar['?]; ENDLOOP; }; DiskPos: PUBLIC PROC [pos: NAT] = { FOR i: NAT IN [lineLength..pos) DO IO.PutChar[file, Ascii.SP]; lineLength _ lineLength.SUCC; ENDLOOP; }; DiskCommit: PUBLIC PROC = { DiskChar[Ascii.CR]; DiskChar[Ascii.CR]; IO.Close[file]; { v: Viewer = ViewerOps.FindViewer["PupWatch.log"].viewer; IF v = NIL THEN [] _ ViewerTools.MakeNewTextViewer[ info: [name: logName, file: "PupWatch.log", iconic: FALSE]] ELSE ViewerOps.RestoreViewer[v]; }; firstChar _ TRUE; }; Process.EnableAborts[@bufferChange]; Process.EnableAborts[@inputChange]; Process.EnableAborts[@lookerChange]; ViewerOps.RegisterViewerClass[$PupWatch, myClass]; myViewer _ Create[]; DoAction[[start[]]]; DoAction[[newHost[NIL]]]; }. PupWatch.mesa Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. Russ Atkinson, March 13, 1985 3:56:47 pm PST Andrew Birrell October 25, 1983 11:29 am Hal Murray, December 4, 1986 2:08:47 pm PST EtherWatch and ArpaWatch have a similar structure. If you fix a bug here, consider fixing them too. Exported Types Simple things User input Pup level Patch by hand to watch other nets Synchronization with user type-in Output subroutines Display line layout is: 7777: from 77#377#777 [aData,L:777,to:177777]abcdefghijklmn 7777: to 77#377#777 [aData,L:777,to:177777]abcdefghijklmn Disk log line layout is: 7777: from 377#377#377 [aData,L:1024,to:177777] abcdefghijklmn 7777: to 377#377#377 [aData,L:1024,to:177777] abcdefghijklmn Fastest case, probably most common, too. Second fastest case, probably second most common, too. Major procedures registered pup types Cedar RPC pup types. Bits are: 8..10: 3 (=> start at pt140) 11: {end(0),notEnd(1)} 12: {dontAck(0),pleaseAck(1)} 13..15: {call,data,ack,spare,rfa} unregistered pup types (possible overlap) also dateTextRequest (socket 4), statisticsRequest (socket 22) also dateTextIs (socket 4), statisticsAre (socket 22) Avoid Bounds Fault from bogus/mashed packet Cedar RPC packet user command input InputAction: TYPE = RECORD[SELECT act: * FROM fast => NULL, newHost => [name: ROPE], pauseContinue => NULL, quit => NULL, replay => NULL, slow => NULL, start => NULL, stop => NULL, writeLog => [mouseButton: Menus.MouseButton, shift, control: BOOL], pktSize => [big: BOOL], ENDCASE]; Address Lookup Display interface self: Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOLEAN start of a new line We force all characters > \177 to be that character, to avoid problems with potentially small fonts for our printing. self: Viewer returns viewer which is the non-scrolling text area parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL Disk logging Initialization Κ5!˜codešœ ™ Kšœ Οmœ6™AKšœ,™,Kšœ(™(K™+K™Kšœc™c—K˜šΟk ˜ Kšœžœžœžœ˜Kšœžœžœ˜-Kšœ žœžœ:˜kKšœžœ8˜EKšœ žœ˜!Kšœ žœk˜{Kšœžœ˜%Kšœ žœ$˜4Kšœžœ˜Jšœžœ žœžœ˜'Kšžœžœ˜#KšœžœV˜bKšœžœ˜(Kšœ žœ$˜4Kšžœžœ žœ˜0Kšœžœ ˜Kšœžœ˜Kšœžœ ˜KšœžœZ˜gKšœžœ˜(Kšœ žœT˜cKšœžœ$˜1Kšœ žœ˜KšœžœH˜UKšœžœ·˜ΓKšœžœžœ˜%Kšœžœ ˜Kšœžœ+˜BKšœžœ˜Kšœžœ?˜RKšœ žœW˜fKšœ žœ=˜NKšžœžœA˜I—K˜šœ ž˜KšžœFžœ&žœež˜ήKšžœ˜K˜—™K˜Kšœžœžœ ˜:K˜—™ K™Kšžœžœžœ˜Kšœžœ˜$K™š Οnœžœžœžœžœžœ˜JKšžœ0˜6Kšœ˜K˜——šœ ™ K˜šœ žœžœžœž˜.Kšœžœ˜ Kšœžœ˜Kšœžœ˜Kšœ žœ˜Kšœžœ˜ Kšœ žœ˜Kšœžœ˜ Kšœ=žœ˜CKšœžœ˜Kšžœ˜ —K˜K˜—šœ ™ K˜Kšœ žœ˜Kšœ žœ˜Kšœžœ˜Kšœžœ˜+Kšœžœ˜K˜Kšœžœžœ˜Kšœ žœΟc˜&Kšœ žœ˜šœ žœ˜K˜—Kš œžœžœžœžœ ˜*š œ žœžœž œžœ˜-Jšœ ˜ Jšœ&˜&šœ žœžœž˜Kšœ ˜ Kš œžœžœžœžœ˜:Kš œžœžœžœžœ˜@Kš œžœžœžœžœ˜4Kšœžœžœžœ ˜9Kšœžœžœžœ˜1K˜%K˜KšœCžœ˜MKš œžœ žœžœžœžœ˜Gšœžœ˜Kšœ&˜&K˜Kšœžœ˜Kš œ žœžœžœžœ˜*—Kšœžœžœ˜6Kšœ@˜@Kšœ'˜'Kšžœ˜ K˜——š Ÿ œžœ žœžœžœ˜=Kšžœ3˜9Kšœ˜K˜—Kšœ žœ˜Kšœ žœ˜"K˜Kšœ žœ ˜Kš œ žœžœžœ œžœ˜CK˜Kš œžœžœ žœžœ˜7K˜Kš œžœžœ žœžœžœžœ˜4Kš œžœžœ žœžœžœžœ˜6K˜Kšœ *˜DKšœ ˜8K˜Kšœž œ˜Kšœžœžœ˜Kšœ žœžœ (˜?šœžœ˜K˜—šŸ œžœžœžœ˜(Kšžœ žœžœ˜$Kš œ žœžœžœžœžœ˜JKšœ žœžœ˜8Kšœžœ žœ'˜CKšœ žœ(˜4Kšœžœžœžœ˜˜!K˜—š Ÿœžœžœžœžœ˜-Kšžœžœžœ˜Kšžœ žœ6˜]šžœ žœ žœ˜$Kš žœ žœ žœžœ žœ˜=Kšžœžœ˜—Kšžœžœžœžœ˜,Kšœ˜K˜—šŸœžœžœ˜ Kšœžœ˜Kšžœ žœžœ˜(Kšœ˜K˜—šŸ œžœžœ˜Kšžœžœžœ˜Kšžœ žœžœ žœ˜.Kšœžœ˜Kšžœžœžœžœ˜0Kšœ˜K˜—šŸœžœžœ˜Kšœžœ˜Kšž œ ˜Kšœ˜K˜——šœ™K˜K˜!K˜šœ™Kšœ;™;Kšœ9™9Kšœ žœ˜Kšœžœ˜ Kšœ žœ˜Kšœžœ˜ Kšœ žœ˜—K˜šœ™Kšœ>™>Kšœ<™™>šœ  œ˜Kšžœ žœ žœ˜2—Kšœ5™5šœ œ˜Kšžœ žœ ˜Kšžœžœ žœžœ˜5—šœ œ˜Kšžœ žœ žœ˜3—šžœ ˜Kšžœ žœžœ ˜$—Kšœ œ˜,Kšœ  œ˜)Kšœ  œ˜!Kšœ  œ˜ Kšœ  œ˜"Kšœ  œ˜ Kšœ œ˜(Kšœ  œ˜#Kšœ  œ˜!Kšœ  œ˜!Kšœ  œ˜&Kšœ  œ˜'Kšœ  œ˜$Kšœ œ˜Kšœ  œ˜!Kšœ  œ˜)Kšœ  œ˜#Kšœ  œ˜'Kšœ  œ˜%Kšœ  œžœ˜%Kšœ  œžœ˜#Kšœ  œžœ˜%Kšœ  œ˜#šœ  œ˜Kšžœžœ žœ˜*—Kšœ œ˜/Kšœ œ˜+Kšœ œ ˜$Kšœ œ ˜$Kšœ  œ˜"Kšœ œ ˜$Kšœ œ ˜$Kšžœžœ˜—šžœžœ˜Kšœ#žœ˜(K˜—Kšžœ˜Kšœ˜—K˜šžœ2žœ˜:Kšœ+™+KšœG˜GKšœžœ˜Kšžœ˜ —K˜šžœž˜˜ Kšœžœ˜K˜Kšœ˜šžœ'žœ˜/K˜Kšœ"˜"—K˜Kšœ žœ(˜9—šœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜—šœ˜Kšœ˜Kšœ˜—˜ K˜K˜K˜—šœ˜Kšœžœ˜K˜Kšœ˜—šœ˜K˜Kšœ.˜.Kšœ.˜.Kšœ3˜3—šœ˜K˜Kšœ)˜)—Kšœ˜šœ˜šœžœž˜7K˜Kšœ ˜ K˜K˜K˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ#˜#Kšœ˜Kšœ˜Kšœ˜Kšœ#˜#Kšœ$˜$Kšœ%˜%Kšžœžœ˜—Kšžœ žœžœ˜-šžœž˜šœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ0˜0Kšžœžœ˜——Kšœ1˜1šžœžœžœ ˜(Kšœ™Kš œžœžœžœžœ ˜7Kšœ žœžœ  œ˜7Kšœ˜Kšžœžœ*˜EKšžœ˜#Kšœ"žœ˜-Kšœ)žœ˜4Kšœ'žœ˜2šžœž˜Kšœ5žœ ˜C——Kšœžœ˜šœ˜Kšœ žœžœ!˜7Kšœžœ˜'Kšœ>žœ˜HKšžœžœ˜7Kšžœ˜—Kšœžœ ˜*Kšœžœ˜Kšœ žœ ˜˜Kšœžœžœ!˜1Kšœ žœ žœ#˜<šžœžœžœž˜Kšžœžœ˜Kšœ˜Kšžœ˜ ——Kšœ˜K˜*Kšœ˜Kšœžœ žœ ˜1šœ˜šžœ žœ˜Kšœ žœžœ2˜HKšœžœ˜'Kšœ>žœ˜HKšœ˜Kšœ(˜(Kšœ˜Kšžœžœžœ˜LKšœ˜Kšœ?˜?—šžœ  ˜Kšœ žœ<˜MKšœ"˜"——Kšœ+˜+Kšœ žœ˜%Kšœ&˜&šœ˜Kšœžœ˜#Kšœ˜Kšœ˜šžœ žœ˜Kšœ˜Kšœ"˜"——Kšœ!˜!Kšœ'˜'šœ˜šžœžœž˜Kšœ˜Kšžœ˜%——Kšœ+˜+Kšœ)˜)šœ%˜%šžœ ž˜Kš œžœ žœžœžœ+˜^——šœ˜šžœ žœžœ)ž˜WKš œžœ žœžœžœ0˜c——Kšžœ˜"—K˜šžœžœž˜šœ˜šžœžœ˜Kšœžœ˜K˜Kšœ˜K˜Kšœ˜Kšœ˜Kšœ˜Kšœ"˜"—šœ˜Kšœžœžœ!˜7šžœžœ˜Kšœžœ˜K˜šžœ žœžœ˜.Kšœžœžœ˜#Kšœž œ˜6——šžœ žœ žœ˜"Kšœžœ˜K˜Kšœ˜šžœžœžœ ž˜!Kšœ˜Kšœžœ˜Kšžœ˜ ——šžœ žœ žœ˜"Kšœžœ˜K˜Kšœ˜šžœžœžœž˜#Kšœ˜Kšœžœ˜Kšžœ ˜————šœ0˜0Kšœ˜Kšœ˜—Kšžœ˜—Kšœžœ˜—Kšœ˜K˜—K˜Kšœ žœ˜K˜šŸ œžœ˜Kšœ˜Kšœ ˜ Kšœ žœ˜Kšœžœ˜Kšœ˜K˜—šŸ œžœ˜K˜5šž˜Kšžœžœžœ˜K˜ šžœžœ˜šžœ žœ˜Kšœ˜Kšœžœ˜—Kšœ0˜0K˜šžœ žœ4˜GKšžœ˜—Kšœ˜—Kšžœ˜—Kšœ˜K˜—šŸ œžœ˜K˜K˜K˜ Kšœžœ˜K˜šžœžœ˜Kšœ8˜8Kšœžœ˜šžœžœžœž˜!Kšœ ˜ Kšžœ žœžœžœ˜:K˜Kšžœ˜ ——šžœ˜Kšœ-˜-Kšœ!˜!Kšœ˜Kšœžœ˜—šžœžœžœž˜!Kšœ ˜ šžœ žœ˜Kšœ˜Kšœžœ˜—Kšœ0˜0Kšœžœ˜K˜Kšžœ˜—K˜ K˜K˜ K˜Kšœ˜—K˜—šœ™K˜šŸ œžœ˜KšœR˜RKšœ˜K˜—šŸœžœ˜%šœ žœžœžœž™-Kšœžœ™ Kšœžœ™Kšœžœ™Kšœžœ™ Kšœ žœ™Kšœžœ™ Kšœ žœ™Kšœžœ™ Kšœ=žœ™CKšœžœ™Kšžœ™ —Kšœžœ˜K˜K˜Kšžœ žœžœžœ˜DK˜šžœ žœž˜šœ ˜ Kšœ˜Kšœ ˜ Kšœ žœ˜Kšœžœ˜—šœ ˜ Kšœ˜Kšœ ˜ Kšœ ˜šžœž˜šœ˜Kšœ˜Kšœ ˜ K˜K˜K˜ Kšœžœ˜—Kšœ3˜3Kšœ.˜.Kšœ'˜'Kšžœžœ˜—Kšœ ˜#—K˜&šœ ˜ K˜Kšœ˜Kšœ˜Kšœ žœ˜Kšœžœ˜—šœ ˜ Kšœ˜Kšœ ˜ Kšœ žœ˜Kšœžœ˜—šœ ˜ K˜ Kšœžœ˜Kšœ"˜"K˜K˜ Kšœžœ˜Kšœžœ˜"Kšœ˜—šœ ˜ K˜Kšžœ˜K˜Kšœ˜—˜ Kšœ˜Kšœ˜šžœž˜Kšœ ˜Kšœ  ˜"Kšœ ˜!Kšžœ˜—Kšœ#˜#Kšœ ˜ K˜Kšœ˜Kšœ˜—˜Kšžœ žœ+žœ˜FKšžœ˜—Kšžœžœ˜—Kšžœžœžœžœ˜:Kšžœžœžœ žœ˜;K˜Kšœ˜K˜——šœ™K˜Kšœžœ(˜;K˜šŸœžœžœžœ˜>K˜ šœžœžœžœ˜Tšœ˜šœ žœžœ.˜HKšžœ ˜—Kšžœ˜ ——Kšœ˜K˜——K™™Kšœ<˜