-- Copyright (C) 1982 by Xerox Corporation. All rights reserved. -- NetDirPrinter.Mesa, HGM, 30-Jan-82 18:36:13 DIRECTORY Ascii USING [FF], Checksum USING [ComputeChecksum], Inline USING [LowHalf], FormSW USING [ ClientItemsProcType, ProcType, AllocateItemDescriptor, newLine, CommandItem], MFile USING [Error, GetLength, Handle, ReadOnly], MSegment USING [Address, Create, Delete, Handle], Put USING [Char, CR, Decimal, Text, Line, LongDecimal, Octal, Number], Runtime USING [GetBcdTime], String USING [AppendString], Time USING [Append, Unpack], Tool USING [Create, MakeSWsProc, MakeFormSW, MakeFileSW], UserInput USING [UserAbort], Window USING [Handle], PupWireFormat USING [BcplSTRING], PupDefs USING [PupAddress], NetDirDefs USING [ Addr, AddrOffset, Entry, EntryOffset, Name, NameOffset, StringOffset, NameBase, AddrBase, EntryBase, StringBase, Offset, Header, Attribute, last, maxAddrsPerEntry, maxNamesPerEntry]; NetDirPrinter: PROGRAM IMPORTS Checksum, FormSW, Inline, MFile, MSegment, Put, Runtime, String, Time, Tool, UserInput = BEGIN OPEN NetDirDefs; form, log: Window.Handle; header: LONG POINTER TO Header; p: LONG POINTER; nameTable: LONG POINTER TO ARRAY [0..0) OF NameOffset; numberOfNames: CARDINAL ← 0; addrTable: LONG POINTER TO ARRAY [0..0) OF AddrOffset; numberOfAddrs: CARDINAL ← 0; e: EntryBase; n: NameBase; a: AddrBase; s: StringBase; nameTableLocation: CARDINAL; addrTableLocation: CARDINAL; entryLocation: CARDINAL; lengthOfEntries: CARDINAL; BcplString: TYPE = LONG POINTER TO PupWireFormat.BcplSTRING; PupAddress: TYPE = PupDefs.PupAddress; PrintHeader: PROCEDURE = BEGIN Put.Text[log, "Name lookup table length "]; Put.Octal[log, header.numberOfNames]; Put.Text[log, " ("]; Put.Decimal[log, header.numberOfNames]; Put.Text[log, "), address "]; Put.Octal[log, header.nameLookupTable]; Put.Line[log, "."]; Put.Text[log, "Address lookup table length "]; Put.Octal[log, header.numberOfAddrs]; Put.Text[log, " ("]; Put.Decimal[log, header.numberOfAddrs]; Put.Text[log, "), address "]; Put.Octal[log, header.addrLookupTable]; Put.Line[log, "."]; Put.Text[log, "Entry table length "]; Put.Octal[log, header.lengthOfEntries]; Put.Text[log, " ("]; Put.Decimal[log, header.lengthOfEntries]; Put.Text[log, "), address "]; Put.Octal[log, header.firstEntry]; Put.Line[log, "."]; Put.Text[log, "version = "]; Put.Decimal[log, header.version]; Put.Line[log, "."]; Put.CR[log]; Put.CR[log]; END; WriteBcplString: PROCEDURE [string: BcplString] = BEGIN FOR i: CARDINAL IN [0..string.length) DO Put.Char[log, string.char[i]]; ENDLOOP; END; O6: PROCEDURE [n: UNSPECIFIED] = BEGIN Put.Number[log, n, [8, FALSE, TRUE, 6]]; END; O7: PROCEDURE [n: UNSPECIFIED] = BEGIN Put.Number[log, n, [8, FALSE, TRUE, 7]]; END; O: PROCEDURE [n: UNSPECIFIED] = BEGIN Put.Number[log, n, [8, FALSE, TRUE, 0]]; END; PrintPupAddress: PROCEDURE [a: LONG POINTER TO PupAddress] = BEGIN Put.Number[log, a.net, [8, FALSE, TRUE, 0]]; Put.Char[log, '#]; Put.Number[log, a.host, [8, FALSE, TRUE, 0]]; Put.Char[log, '#]; IF a.socket.a # 0 THEN BEGIN Put.Number[log, a.socket.a, [8, FALSE, TRUE, 0]]; Put.Char[log, '|]; END; Put.Number[log, a.socket.b, [8, FALSE, TRUE, 0]]; END; OutOfBounds: PROCEDURE [x: Offset] RETURNS [BOOLEAN] = BEGIN y: CARDINAL = x; RETURN[y > length - 1]; END; NotEven: PROCEDURE [x: Offset] RETURNS [BOOLEAN] = BEGIN y: CARDINAL = x; RETURN[(x MOD 2) # 0]; END; Names: FormSW.ProcType = BEGIN name: LONG POINTER TO Name; IF source = NIL THEN BEGIN Put.Line[log, "Please get a reasonable Pup-Network.directory."]; RETURN; END; Put.Line[log, "Name lookup table:"]; Put.CR[log]; Put.Line[log, " Table Loc Next Entry Text"]; FOR i: CARDINAL IN [0..numberOfNames) DO IF UserInput.UserAbort[log] THEN EXIT; O7[i + nameTableLocation]; O7[nameTable[i]]; IF OutOfBounds[nameTable[i]] THEN BEGIN Put.Line[log, " (out of bounds)"]; LOOP; END; name ← @n[nameTable[i]]; O7[name.next]; O7[name.entry]; Put.Text[log, " "]; WriteBcplString[@name.string]; IF NotEven[nameTable[i]] THEN Put.Text[log, " ****** (not even)"]; Put.CR[log]; ENDLOOP; Put.Char[log, Ascii.FF]; Put.CR[log]; END; Addresses: FormSW.ProcType = BEGIN addr: LONG POINTER TO Addr; IF source = NIL THEN BEGIN Put.Line[log, "Please get a reasonable Pup-Network.directory."]; RETURN; END; Put.Line[log, "Address lookup table:"]; Put.CR[log]; Put.Line[log, " Table Loc Next Entry Address"]; FOR i: CARDINAL IN [0..numberOfAddrs) DO IF UserInput.UserAbort[log] THEN EXIT; O7[i + addrTableLocation]; O7[addrTable[i]]; IF OutOfBounds[addrTable[i]] THEN BEGIN Put.Line[log, " (out of bounds)"]; LOOP; END; addr ← @a[addrTable[i]]; O7[addr.next]; O7[addr.entry]; Put.Text[log, " "]; PrintPupAddress[@addr.port]; IF NotEven[addrTable[i]] THEN Put.Text[log, " ****** (not even)"]; Put.CR[log]; ENDLOOP; Put.Char[log, Ascii.FF]; Put.CR[log]; END; Entries: FormSW.ProcType = BEGIN file: CARDINAL ← entryLocation; size: CARDINAL; entry: LONG POINTER TO Entry ← LOOPHOLE[e + entryLocation]; name: NameOffset; addr: AddrOffset; attribute: LONG POINTER TO Attribute; IF source = NIL THEN BEGIN Put.Line[log, "Please get a reasonable Pup-Network.directory."]; RETURN; END; Put.Line[log, "Entry blocks:"]; Put.CR[log]; Put.Line[log, " Loc Name Addr Atrributes"]; WHILE file < entryLocation + lengthOfEntries DO IF UserInput.UserAbort[log] THEN EXIT; O6[file]; Put.Text[log, " Names: "]; name ← entry.name; FOR i: CARDINAL ← 0, i + 1 UNTIL i > 50 DO O[name]; Put.Text[log, " "]; IF OutOfBounds[name] THEN BEGIN Put.Text[log, " (out of bounds)"]; EXIT; END; WriteBcplString[@n[name].string]; name ← n[name].next; IF name = last THEN BEGIN IF i >= maxNamesPerEntry THEN Put.Line[log, "****** Too many names."]; EXIT; END; Put.Text[log, ", "]; REPEAT FINISHED => Put.Line[log, "****** Looks like a loop in the name list."]; ENDLOOP; Put.CR[log]; Put.Text[log, " Addresses: "]; addr ← entry.addr; FOR i: CARDINAL ← 0, i + 1 UNTIL i > 50 DO O[addr]; Put.Text[log, " "]; IF OutOfBounds[addr] THEN BEGIN Put.Line[log, " (out of bounds)"]; EXIT; END; PrintPupAddress[@a[addr].port]; addr ← a[addr].next; IF addr = last THEN BEGIN IF i >= maxAddrsPerEntry THEN Put.Line[log, "****** Too many addresses."]; EXIT; END; Put.Text[log, ", "]; REPEAT FINISHED => Put.Line[log, "****** Looks like a loop in the address list."]; ENDLOOP; FOR i: CARDINAL IN [0..entry.numberOfAttributes) DO IF UserInput.UserAbort[log] THEN EXIT; attribute ← @entry.attributes[i]; IF i = 0 THEN BEGIN Put.CR[log]; Put.Text[log, " "]; Put.Decimal[log, entry.numberOfAttributes]; Put.Text[log, " Attributes: "]; END ELSE Put.Text[log, ", "]; Put.Char[log, '(]; O[attribute.name]; Put.Text[log, ") "]; IF OutOfBounds[attribute.name] THEN Put.Text[log, "(out of bounds)"] ELSE WriteBcplString[@s[attribute.name]]; Put.Text[log, ": ("]; O[attribute.value]; Put.Text[log, ") """]; IF OutOfBounds[attribute.value] THEN Put.Text[log, "(out of bounds)"] ELSE WriteBcplString[@s[attribute.value]]; Put.Char[log, '"]; ENDLOOP; size ← SIZE[Entry] + entry.numberOfAttributes*SIZE[Attribute]; IF size > 50 THEN BEGIN Put.Line[ log, " ****** The size of this Entry is huge. The file is probably trash."]; EXIT; END; file ← file + size; entry ← entry + size; Put.CR[log]; IF file > length THEN BEGIN EXIT; END; ENDLOOP; Put.Char[log, Ascii.FF]; Put.CR[log]; END; source: MSegment.Handle ← NIL; length: CARDINAL; RoundUp: PROCEDURE [w: CARDINAL] RETURNS [CARDINAL] = BEGIN IF (w MOD 2) # 0 THEN w ← w + 1; RETURN[w]; END; Initialize: FormSW.ProcType = BEGIN CheckFile[]; END; CheckFile: PROCEDURE = BEGIN file: MFile.Handle; IF source # NIL THEN RETURN; file ← MFile.ReadOnly[ "Pup-Network.Directory", [] ! MFile.Error => BEGIN Put.Line[log, "Can't read Pup-Network.Directory."]; GOTO NoFile; END]; BEGIN eof: LONG CARDINAL ← MFile.GetLength[file]; IF eof < 2*SIZE[Header] THEN BEGIN Put.Line[log, "The file is way too short."]; GOTO FileTooShort; END; Put.Text[log, "There are "]; Put.LongDecimal[log, eof]; Put.Line[log, " bytes in the file."]; IF eof > 2*LONG[LAST[CARDINAL]] THEN BEGIN Put.Line[log, "The file is too big."]; GOTO FileTooBig; END; length ← Inline.LowHalf[eof/2]; END; source ← MSegment.Create[file, [], 0]; p ← MSegment.Address[source]; e ← p; n ← p; a ← p; s ← p; header ← p; PrintHeader[]; IF (p + length - 1)↑ # Checksum.ComputeChecksum[0, length - 1, p] THEN Put.Line[log, "******The Checksum is bad."]; numberOfNames ← header.numberOfNames; nameTableLocation ← LOOPHOLE[header.nameLookupTable]; nameTable ← LOOPHOLE[e + nameTableLocation]; IF NotEven[nameTableLocation] THEN Put.Line[ log, "****** The Name lookup table does not start on an even word."]; IF nameTableLocation + numberOfNames > length THEN Put.Line[log, "****** (Part of) The Name table is out of bounds."]; numberOfAddrs ← header.numberOfAddrs; addrTableLocation ← LOOPHOLE[header.addrLookupTable]; addrTable ← LOOPHOLE[e + addrTableLocation]; IF NotEven[addrTableLocation] THEN Put.Line[ log, "****** The Address lookup table does not start on an even word."]; IF addrTableLocation + numberOfAddrs > length THEN Put.Line[log, "****** (Part of) The Address table is out of bounds."]; lengthOfEntries ← header.lengthOfEntries; entryLocation ← RoundUp[addrTableLocation + numberOfAddrs]; IF entryLocation + lengthOfEntries > length THEN Put.Line[log, "****** (Some of) The Entry blocks are out of bounds."]; EXITS NoFile, FileTooShort, FileTooBig => BEGIN IF source # NIL THEN MSegment.Delete[source]; source ← NIL; END; END; MakeSWs: Tool.MakeSWsProc = BEGIN form ← Tool.MakeFormSW[window: window, formProc: MakeForm]; log ← Tool.MakeFileSW[window: window, name: "NetDirPrinter.log"]; CheckFile[]; END; MakeForm: FormSW.ClientItemsProcType = BEGIN nParams: CARDINAL = 4; items ← FormSW.AllocateItemDescriptor[nParams]; items[0] ← FormSW.CommandItem[ tag: "Initialize"L, proc: Initialize, place: FormSW.newLine]; items[1] ← FormSW.CommandItem[tag: "Names"L, proc: Names]; items[2] ← FormSW.CommandItem[tag: "Addresses"L, proc: Addresses]; items[3] ← FormSW.CommandItem[tag: "Entries"L, proc: Entries]; RETURN[items, TRUE]; END; Init: PROCEDURE = BEGIN herald: STRING = [50]; String.AppendString[herald, "NetDirPrinter of "L]; Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]]; [] ← Tool.Create[name: herald, makeSWsProc: MakeSWs]; END; Init[]; END.