-- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved. -- NetDirBuilderOld.mesa, HGM, 28-Jun-85 17:34:45 DIRECTORY Ascii USING [CR, SP, TAB, FF, NUL], Checksum USING [ComputeChecksum], Environment USING [], Inline USING [LowHalf, HighHalf], MSegment USING [FreePages, FreeWords, GetPages, GetWords], MStream USING [Error, ReadOnly, WriteOnly], Process USING [Yield], Put USING [Char, CR, Text, Line, Number, LongDecimal], Stream USING [Delete, EndOfStream, GetChar, Handle, PutBlock, PutWord], String USING [UpperCase], Time USING [AppendCurrent], Window USING [Handle], PupTypes USING [PupAddress], PupWireFormat USING [BcplSTRING, BcplMaxLength], HeapSort USING [Sort], NetDirDefs, NetDirBuilderOps USING [GetNewVersionNumber, log]; NetDirBuilderOld: PROGRAM IMPORTS Checksum, Inline, MSegment, MStream, Process, Put, Stream, String, Time, HeapSort, NetDirBuilderOps EXPORTS NetDirBuilderOps = BEGIN OPEN NetDirDefs; stick: Window.Handle; fixup: CARDINAL = 100B; nameFudge: NameOffset = LOOPHOLE[fixup]; addrFudge: AddrOffset = LOOPHOLE[fixup]; entryFudge: EntryOffset = LOOPHOLE[fixup]; stringFudge: StringOffset = LOOPHOLE[fixup]; sizeOfStatementBuffer: CARDINAL = 1000; slop: CARDINAL = 50; scratchSize: CARDINAL = MAX[maxNamesInFile, maxAddrsInFile]; BcplString: TYPE = LONG POINTER TO PupWireFormat.BcplSTRING; PupAddress: TYPE = PupTypes.PupAddress; nameTable: LONG POINTER TO ARRAY [0..maxNamesInFile) OF NameOffset; numberOfNames: CARDINAL; addrTable: LONG POINTER TO ARRAY [0..maxAddrsInFile) OF AddrOffset; numberOfAddrs: CARDINAL; entryTable: LONG POINTER TO ARRAY [0..maxEntrysInFile) OF EntryOffset; numberOfEntries: CARDINAL; stringTable: LONG POINTER TO ARRAY [0..maxStringsInFile) OF StringOffset; numberOfStrings: CARDINAL; numberOfSkips: CARDINAL; numberOfDiscards: CARDINAL; e: EntryBase; n: NameBase; a: AddrBase; s: StringBase; nextEntry: EntryBase RELATIVE POINTER TO Entry; nextName, lastName: NameBase RELATIVE POINTER TO Name; nextAddr: AddrBase RELATIVE POINTER TO Addr; nextString: StringBase RELATIVE POINTER TO PupWireFormat.BcplSTRING; scratch: LONG POINTER TO ARRAY [0..scratchSize) OF Offset; digitsOnly: BOOLEAN ← TRUE; BuildOldDirectory: PUBLIC PROCEDURE RETURNS [BOOLEAN] = BEGIN stick ← NetDirBuilderOps.log; Put.CR[stick]; IF ~FindInputFile[] THEN RETURN[FALSE]; errors ← 0; numberOfNames ← numberOfAddrs ← numberOfEntries ← numberOfStrings ← 0; numberOfSkips ← numberOfDiscards ← 0; nextEntry ← LOOPHOLE[fixup]; nextName ← lastName ← LOOPHOLE[fixup]; nextAddr ← LOOPHOLE[fixup]; nextString ← LOOPHOLE[fixup]; AllocateThings[]; BEGIN ENABLE GetMeOutOfHere => BEGIN errors ← errors + 1; CONTINUE; END; Announce["Parsing input file for old file format..."]; ParseInput[]; CloseInputFile[]; CheckForErrors[]; Put.Text[stick, "There were "]; Put.LongDecimal[stick, input]; Put.Line[stick, " characters in the input file."]; Announce["Sorting name table..."]; SortNameTable[]; CheckNameTable[]; Announce["Sorting address table..."]; SortAddressTable[]; CheckAddressTable[]; CheckForErrors[]; Announce["Fixing up offsets..."]; FindLocations[]; header ← [ numberOfNames: numberOfNames, nameLookupTable: LOOPHOLE[nameTableLocation], numberOfAddrs: numberOfAddrs, addrLookupTable: LOOPHOLE[addrTableLocation], lengthOfEntries: nextEntry - entryFudge, firstEntry: LOOPHOLE[entryLocation], version: NetDirBuilderOps.GetNewVersionNumber[]]; FixupNamePointers[]; FixupAddrPointers[]; FixupEntryPointers[]; Announce["Writing out Pup-network.directory..."]; FindOutputFile[]; WriteOutFile[]; CloseOutputFile[]; Announce["Done."]; END; -- of ENABLE FreeThings[]; PrintInfo[]; IF errors = 0 THEN BEGIN Put.Text[stick, "There are "]; Put.LongDecimal[stick, output]; Put.Line[stick, " words in the output file."]; Put.Text[stick, "There are "]; Put.LongDecimal[stick, LAST[CARDINAL]-output]; Put.Line[stick, " words left in the output file."]; END; Put.CR[stick]; RETURN[errors = 0]; END; input: LONG CARDINAL; source: Stream.Handle; statement: STRING = [sizeOfStatementBuffer]; finger: CARDINAL; terminator: CHARACTER; FindInputFile: PROCEDURE RETURNS [BOOLEAN] = BEGIN input ← 0; source ← MStream.ReadOnly[ "Pup-Network.txt"L, [] ! MStream.Error => GOTO NotFound]; RETURN[TRUE]; EXITS NotFound => BEGIN Put.Line[stick, "*** I can't find Pup-Network.txt on this disk."]; RETURN[FALSE]; END; END; EndOfInput: SIGNAL = CODE; CloseInputFile: PROCEDURE = BEGIN Stream.Delete[source]; source ← NIL; END; ParseInput: PROCEDURE = BEGIN ENABLE EndOfInput => CONTINUE; DO ENABLE ParsingError => CONTINUE; GetStatment[ ! Stream.EndOfStream => Abort["Unexpected end of input data."]]; InitializeNewEntry[]; Process.Yield[]; DO BuildName[]; SELECT terminator FROM ', => NULL; '= => EXIT; ENDCASE => ParseError["Syntax error, = expected"]; ENDLOOP; IF namesThisEntry = 0 AND name.string.length = 0 THEN BEGIN -- No interesting names, skip this whole entry. numberOfDiscards ← numberOfDiscards + 1; LOOP; END; Process.Yield[]; DO BuildAddress[]; SELECT terminator FROM ', => LOOP; Ascii.CR => EXIT; '; => BEGIN IF TRUE THEN EXIT; -- File too big: Patch out Attribute Value pairs DO BuiltAttributeValuePair[]; SELECT terminator FROM ', => LOOP; Ascii.CR => EXIT; ENDCASE => ParseError["Syntax error, end of statment expected"]; ENDLOOP; EXIT; END; ENDCASE => ParseError["Syntax error, end of statment expected"]; ENDLOOP; KeepThisEntry[]; Process.Yield[]; ENDLOOP; END; GetChar: PROCEDURE RETURNS [CHARACTER] = BEGIN input ← input + 1; RETURN[Stream.GetChar[source]]; END; GetStatment: PROCEDURE = BEGIN c: CHARACTER; statement.length ← finger ← 0; DO c ← GetChar[ ! Stream.EndOfStream => SIGNAL EndOfInput]; SELECT c FROM '; => UNTIL c = Ascii.CR DO c ← GetChar[]; ENDLOOP; Ascii.SP, Ascii.TAB, Ascii.FF, Ascii.CR => NULL; ENDCASE => EXIT; ENDLOOP; UNTIL c = Ascii.CR DO AppendCharToString[statement, c]; SELECT c FROM ',, ';, '=, '+ => BEGIN DO c ← GetChar[]; SELECT c FROM Ascii.CR => LOOP; ENDCASE => GOTO AlreadyPeekedAhead; ENDLOOP; EXITS AlreadyPeekedAhead => LOOP; END; ENDCASE => NULL; c ← GetChar[]; ENDLOOP; -- 140C is invisible FOR i: CARDINAL IN [0..statement.length) DO IF statement[i] = 140C THEN ParseError[ "Strange Character: 140C encountered (it's probably invisible)"]; ENDLOOP; END; SkipSpaces: PROCEDURE = BEGIN c: CHARACTER; UNTIL finger = statement.length DO c ← statement[finger]; SELECT c FROM Ascii.SP, Ascii.TAB, Ascii.CR => NULL; ENDCASE => RETURN; finger ← finger + 1; ENDLOOP END; FindTerminator: PROCEDURE = BEGIN SkipSpaces[]; IF finger = statement.length THEN terminator ← Ascii.CR ELSE BEGIN terminator ← statement[finger]; finger ← finger + 1; END; Process.Yield[]; END; CollectString: PROCEDURE [where: STRING] = BEGIN c: CHARACTER; where.length ← 0; SkipSpaces[]; UNTIL finger = statement.length DO c ← statement[finger]; SELECT c FROM IN ['a..'z], IN ['A..'Z], IN ['0..'9], '-, '/ => AppendCharToString[where, c]; ENDCASE => EXIT; finger ← finger + 1; ENDLOOP; FindTerminator[]; Process.Yield[]; END; CollectQuotedBcplString: PROCEDURE [where: BcplString] = BEGIN c: CHARACTER; where.length ← 0; SkipSpaces[]; c ← statement[finger]; IF c # '" THEN ParseError["Syntax error: opening "" expected"]; finger ← finger + 1; UNTIL finger = statement.length DO c ← statement[finger]; finger ← finger + 1; IF c = '" THEN BEGIN IF finger = statement.length OR statement[finger] # '" THEN EXIT; finger ← finger + 1; -- "" case END; AppendCharToBcplString[where, c]; ENDLOOP; IF c # '" THEN ParseError["Syntax error: closing "" expected"]; where.char[where.length] ← Ascii.NUL; -- keep file clean FindTerminator[]; Process.Yield[]; END; CollectBcplString: PROCEDURE [where: BcplString] = BEGIN c: CHARACTER; where.length ← 0; digitsOnly ← TRUE; SkipSpaces[]; UNTIL finger = statement.length DO c ← statement[finger]; SELECT c FROM IN ['0..'9] => NULL; IN ['a..'z], IN ['A..'Z], '-, '/ => digitsOnly ← FALSE; '* => BEGIN -- "*" OK, * not allowed in bigger words IF where.length # 0 THEN EXIT; AppendCharToBcplString[where, c]; finger ← finger + 1; digitsOnly ← FALSE; EXIT; END; ENDCASE => EXIT; AppendCharToBcplString[where, c]; finger ← finger + 1; ENDLOOP; where.char[where.length] ← Ascii.NUL; -- keep file clean FindTerminator[]; END; oldAddrs, newAddrs: ARRAY [0..maxAddrsPerEntry) OF PupAddress; old, new: CARDINAL; InitAddrLists: PROCEDURE = BEGIN -- initialize to a single empty item oldAddrs[0] ← [[0], [0], [0, 0]]; old ← 1; new ← 0; END; CrossPort: PROCEDURE [a: PupAddress] = BEGIN FOR i: CARDINAL IN [0..old) DO b: PupAddress ← oldAddrs[i]; IF ~(a.net = b.net OR a.net = 0 OR b.net = 0) THEN LOOP; IF ~(a.host = b.host OR a.host = 0 OR b.host = 0) THEN LOOP; IF ~(a.socket = b.socket OR a.socket = [0, 0] OR b.socket = [0, 0]) THEN LOOP; -- it got past the filter, add it to the list IF new = maxAddrsPerEntry THEN ERROR; IF b.net = 0 THEN b.net ← a.net; IF b.host = 0 THEN b.host ← a.host; IF b.socket = [0, 0] THEN b.socket ← a.socket; newAddrs[new] ← b; new ← new + 1; Process.Yield[]; ENDLOOP; END; ResetAddrLists: PROCEDURE = BEGIN -- flush old, move new to old oldAddrs ← newAddrs; old ← new; new ← 0; END; BuildAddress: PROCEDURE = BEGIN temp: STRING = [255]; number, constantInProgress: BOOLEAN ← FALSE; val, net, host, socket: LONG CARDINAL; InitAddrLists[]; DO CollectString[temp]; [number, val] ← TryStringAsOctal[temp]; IF number THEN BEGIN IF ~constantInProgress THEN BEGIN constantInProgress ← TRUE; net ← host ← 0; END; socket ← val; IF terminator = '# THEN BEGIN IF net # 0 OR socket > 377B THEN ParseError["Malformed Address constant"]; net ← host; host ← socket; socket ← 0; LOOP; END; CrossPort[ [ [Inline.LowHalf[net]], [Inline.LowHalf[host]], [ Inline.HighHalf[socket], Inline.LowHalf[socket]]]]; END ELSE BEGIN IF constantInProgress THEN ParseError["Non Octal Address constant"]; FOR addr: AddrOffset ← LookupName[temp], a[addr].next UNTIL addr = last DO CrossPort[a[addr].port]; ENDLOOP; END; ResetAddrLists[]; IF terminator # '+ THEN EXIT; ENDLOOP; IF old = 0 THEN ParseError["Empty address expression"]; FOR i: CARDINAL IN [0..old) DO port: PupAddress ← oldAddrs[i]; IF port.net = 0 AND port.host # 0 AND port.socket = [0, 0] THEN ParseError["Strange address, probably missing net number"]; IF port.net = 0 AND port.host # 0 AND port.socket # [0, 0] THEN ParseError["Strange address, probably mixedup net number"]; IF port.net # 0 AND port.host = 0 AND port.socket # [0, 0] THEN ParseError["Strange address, probably missing # after host number"]; IF port.net = 0 AND port.host = 0 AND port.socket = [0, 0] THEN ParseError[ "Strange address, probably extra "","" after a normal address"]; IF firstAddrSeen THEN KeepThisAddr[FALSE]; addr.port ← port; firstAddrSeen ← TRUE; ENDLOOP; END; LookupName: PROCEDURE [target: LONG STRING] RETURNS [addr: AddrOffset] = BEGIN entry: EntryOffset; FOR i: CARDINAL IN [0..numberOfNames) DO IF Same[target, @n[nameTable[i]].string] THEN BEGIN entry ← n[nameTable[i]].entry; EXIT; END; REPEAT FINISHED => BEGIN ParseError["Name not known"]; END; ENDLOOP; addr ← e[entry].addr; END; BuildName: PROCEDURE = BEGIN IF name.string.length # 0 THEN KeepThisName[]; CollectBcplString[@name.string]; IF name.string.length > 0 AND digitsOnly THEN BEGIN numberOfSkips ← numberOfSkips + 1; name.string.length ← 0; END; IF name.string.length > maxCharsPerName THEN ParseError["Name too long"]; END; NameSize: PROCEDURE [name: LONG POINTER TO Name] RETURNS [CARDINAL] = BEGIN -- SIZE[Name] uses 255 characters RETURN[sizeOfBasicName + (name.string.length + 2)/2]; END; BuiltAttributeValuePair: PROCEDURE = BEGIN slot: LONG POINTER TO Attribute ← @entry.attributes[entry.numberOfAttributes]; CollectBcplString[@s[nextString]]; slot.name ← FindAttributeSlot[]; CollectQuotedBcplString[@s[nextString]]; slot.value ← FindAttributeSlot[]; entry.numberOfAttributes ← entry.numberOfAttributes + 1; END; FindAttributeSlot: PROCEDURE RETURNS [out: StringOffset] = BEGIN bottom, top: CARDINAL; string: BcplString = @s[nextString]; bottom ← 0; top ← numberOfStrings; WHILE bottom < top DO -- target IN [bottom..top) finger: CARDINAL ← (top + bottom)/2; this: BcplString ← @s[stringTable[finger]]; IF EquivBcplStrings[this, string] THEN RETURN[stringTable[finger]]; IF LessBcplStrings[this, string] THEN bottom ← finger + 1 ELSE top ← finger; ENDLOOP; out ← nextString; IF numberOfStrings = maxStringsInFile THEN Abort["String table overflow"]; FOR i: CARDINAL DECREASING IN (top..numberOfStrings] DO stringTable[i] ← stringTable[i - 1]; ENDLOOP; stringTable[top] ← out; numberOfStrings ← numberOfStrings + 1; nextString ← nextString + (string.length + 2)/2; END; name: LONG POINTER TO Name; addr: LONG POINTER TO Addr; firstAddrSeen: BOOLEAN ← TRUE; entry: LONG POINTER TO Entry; namesThisEntry, addrsThisEntry: CARDINAL; InitializeNewEntry: PROCEDURE = BEGIN namesThisEntry ← addrsThisEntry ← 0; firstAddrSeen ← FALSE; entry ← @e[nextEntry]; name ← @n[nextName]; addr ← @a[nextAddr]; entry↑ ← [name: nextName, addr: nextAddr, numberOfAttributes: 0, attributes:]; name↑ ← [next: last, entry: nextEntry, string:]; name.string.length ← 0; addr↑ ← [next:, entry: nextEntry, port:, numberOfAttributes: 0, attributes:]; END; KeepThisEntry: PROCEDURE = BEGIN size: CARDINAL = SIZE[Entry] + entry.numberOfAttributes*SIZE[Attribute]; IF numberOfEntries = maxEntrysInFile THEN Abort["Entry table overflow"]; entryTable[numberOfEntries] ← nextEntry; numberOfEntries ← numberOfEntries + 1; IF name.string.length # 0 THEN KeepThisName[]; n[lastName].next ← last; IF firstAddrSeen THEN KeepThisAddr[TRUE]; nextEntry ← nextEntry + size; IF nextEntry - entryFudge > oldMaxEntryBufferLength THEN BEGIN IF nextEntry - entryFudge - size <= oldMaxEntryBufferLength THEN Put.CR[stick]; Put.Text[stick, "******** Entry buffer just overflowed the limit of (Apr-81) Alto and Rubicon Gateways"]; Put.CR[stick]; END; IF namesThisEntry = 0 THEN ParseError["No names for this entry"]; IF addrsThisEntry = 0 THEN ParseError["No addresses for this entry"]; IF addrsThisEntry > maxAddrsPerEntry THEN ParseError["Too many address in this entry"]; IF namesThisEntry > maxNamesPerEntry THEN ParseError["Too many names in this entry"]; END; KeepThisName: PROCEDURE = BEGIN size: CARDINAL = ForceEven[name, NameSize[name]]; IF numberOfNames = maxNamesInFile THEN Abort["Name table overflow"]; nameTable[numberOfNames] ← nextName; numberOfNames ← numberOfNames + 1; namesThisEntry ← namesThisEntry + 1; lastName ← nextName; nextName ← nextName + size; name.next ← nextName; name ← @n[nextName]; name↑ ← [next: last, entry: nextEntry, string:]; END; KeepThisAddr: PROCEDURE [end: BOOLEAN] = BEGIN size: CARDINAL = ForceEven[addr, SIZE[Addr]]; IF numberOfAddrs = maxAddrsInFile THEN Abort["Address table overflow"]; addrTable[numberOfAddrs] ← nextAddr; numberOfAddrs ← numberOfAddrs + 1; addrsThisEntry ← addrsThisEntry + 1; nextAddr ← nextAddr + size; addr.next ← IF end THEN last ELSE nextAddr; addr ← @a[nextAddr]; addr↑ ← [next:, entry: nextEntry, port:, numberOfAttributes: 0, attributes:]; END; ForceEven: PROCEDURE [loc: LONG POINTER, size: CARDINAL] RETURNS [CARDINAL] = BEGIN IF (size MOD 2) = 0 THEN RETURN[size]; (loc + size)↑ ← 0; RETURN[size + 1]; END; SortNameTable: PROCEDURE = BEGIN Test: PROCEDURE [x, y: NameOffset] RETURNS [BOOLEAN] = BEGIN RETURN[LessBcplStrings[@n[x].string, @n[y].string]]; END; HeapSort.Sort[nameTable, numberOfNames, Test]; END; -- Assumes that nameTable has been sorted CheckNameTable: PROCEDURE = BEGIN FOR i: CARDINAL IN [0..numberOfNames - 1) DO IF EquivBcplStrings[@n[nameTable[i]].string, @n[nameTable[i + 1]].string] THEN BEGIN errors ← errors + 1; Put.Text[stick, "Duplicate name: "L]; Put.Char[stick, '"]; PutBcplString[stick, @n[nameTable[i]].string]; Put.Char[stick, '"]; Put.CR[stick]; END; ENDLOOP; END; SortAddressTable: PROCEDURE = BEGIN Test: PROCEDURE [x, y: AddrOffset] RETURNS [BOOLEAN] = BEGIN RETURN[LessPupAddress[@a[x].port, @a[y].port]]; END; HeapSort.Sort[addrTable, numberOfAddrs, Test]; END; -- Assumes that addrTable has been sorted CheckAddressTable: PROCEDURE = BEGIN FOR i: CARDINAL IN [0..numberOfAddrs - 1) DO IF a[addrTable[i]].port = a[addrTable[i + 1]].port THEN BEGIN -- Neither the EntryBuffer nor the NameBuffer is in memory at this point. name1: NameOffset; name2: NameOffset; errors ← errors + 1; name1 ← e[a[addrTable[i]].entry].name; name2 ← e[a[addrTable[i + 1]].entry].name; Put.Text[stick, "Duplicate address: "]; PutPupAddress[stick, a[addrTable[i]].port]; Put.Text[stick, ", names are: "]; PutBcplString[stick, @n[name1].string]; Put.Text[stick, " and "]; PutBcplString[stick, @n[name2].string]; Put.CR[stick]; END; ENDLOOP; END; header: Header; nameTableLocation: CARDINAL; addrTableLocation: CARDINAL; entryLocation: CARDINAL; nameLocation: CARDINAL; addressLocation: CARDINAL; stringLocation: CARDINAL; FindLocations: PROCEDURE = BEGIN nameTableLocation ← 20B; addrTableLocation ← RoundUp[nameTableLocation + numberOfNames]; entryLocation ← RoundUp[addrTableLocation + numberOfAddrs]; nameLocation ← RoundUp[entryLocation + (nextEntry - entryFudge)]; addressLocation ← RoundUp[nameLocation + (nextName - nameFudge)]; stringLocation ← RoundUp[addressLocation + (nextAddr - addrFudge)]; END; RoundUp: PROCEDURE [w: CARDINAL] RETURNS [CARDINAL] = BEGIN IF (w MOD 2) # 0 THEN w ← w + 1; RETURN[w]; END; FixupNamePointers: PROCEDURE = BEGIN next: NameOffset ← LOOPHOLE[nameLocation]; FOR i: CARDINAL IN [0..numberOfNames) DO scratch[i] ← next; next ← next + RoundUp[NameSize[@n[nameTable[i]]]]; ENDLOOP; FOR i: CARDINAL IN [0..numberOfNames) DO name: LONG POINTER TO Name ← @n[nameTable[i]]; IF name.next = last THEN LOOP; FOR j: CARDINAL IN [0..numberOfNames) DO IF name.next = nameTable[j] THEN BEGIN name.next ← scratch[j]; EXIT; END; REPEAT FINISHED => Abort["Can't fixup nameTable[i].next"]; ENDLOOP; Process.Yield[]; ENDLOOP; FOR i: CARDINAL IN [0..numberOfEntries) DO entry: LONG POINTER TO Entry ← @e[entryTable[i]]; FOR j: CARDINAL IN [0..numberOfNames) DO IF entry.name = nameTable[j] THEN BEGIN entry.name ← scratch[j]; EXIT; END; REPEAT FINISHED => Abort["Can't fixup entryTable[i].name"]; ENDLOOP; Process.Yield[]; ENDLOOP; FOR i: CARDINAL IN [0..numberOfNames) DO name: LONG POINTER TO Name ← @n[nameTable[i]]; name.entry ← name.entry - fixup + entryLocation; Process.Yield[]; ENDLOOP; END; FixupAddrPointers: PROCEDURE = BEGIN next: AddrOffset ← LOOPHOLE[addressLocation]; FOR i: CARDINAL IN [0..numberOfAddrs) DO scratch[i] ← next; next ← next + SIZE[Addr]; ENDLOOP; FOR i: CARDINAL IN [0..numberOfAddrs) DO addr: LONG POINTER TO Addr ← @a[addrTable[i]]; IF addr.next = last THEN LOOP; FOR j: CARDINAL IN [0..numberOfAddrs) DO IF addr.next = addrTable[j] THEN BEGIN addr.next ← scratch[j]; EXIT; END; REPEAT FINISHED => Abort["Can't fixup addrTable[i].next"]; ENDLOOP; Process.Yield[]; ENDLOOP; FOR i: CARDINAL IN [0..numberOfEntries) DO entry: LONG POINTER TO Entry ← @e[entryTable[i]]; FOR j: CARDINAL IN [0..numberOfAddrs) DO IF entry.addr = addrTable[j] THEN BEGIN entry.addr ← scratch[j]; EXIT; END; REPEAT FINISHED => Abort["Can't fixup entryTable[i].addr"]; ENDLOOP; Process.Yield[]; ENDLOOP; FOR i: CARDINAL IN [0..numberOfAddrs) DO addr: LONG POINTER TO Addr ← @a[addrTable[i]]; addr.entry ← addr.entry - fixup + entryLocation; Process.Yield[]; ENDLOOP; END; FixupEntryPointers: PROCEDURE = BEGIN FOR i: CARDINAL IN [0..numberOfEntries) DO entry: LONG POINTER TO Entry ← @e[entryTable[i]]; FOR j: CARDINAL IN [0..entry.numberOfAttributes) DO attribute: LONG POINTER TO Attribute ← @entry.attributes[j]; attribute.name ← attribute.name - fixup + stringLocation; attribute.value ← attribute.value - fixup + stringLocation; ENDLOOP; Process.Yield[]; ENDLOOP; END; WriteOutFile: PROCEDURE = BEGIN OutBlock[@header, SIZE[Header]]; OutZeros[nameTableLocation - SIZE[Header]]; WriteOutNameTable[]; WriteOutAddrTable[]; WriteOutEntries[]; WriteOutNames[]; WriteOutAddrs[]; WriteOutStrings[]; END; WriteOutNameTable: PROCEDURE = BEGIN next: NameOffset ← LOOPHOLE[nameLocation]; FOR i: CARDINAL IN [0..numberOfNames) DO scratch[i] ← next; next ← next + RoundUp[NameSize[@n[nameTable[i]]]]; ENDLOOP; OutBlock[scratch, numberOfNames]; OutEven[]; END; WriteOutAddrTable: PROCEDURE = BEGIN next: AddrOffset ← LOOPHOLE[addressLocation]; FOR i: CARDINAL IN [0..numberOfAddrs) DO scratch[i] ← next; next ← next + SIZE[Addr]; ENDLOOP; OutBlock[scratch, numberOfAddrs]; OutEven[]; END; WriteOutEntries: PROCEDURE = BEGIN OutBlock[e + fixup, (nextEntry - entryFudge)]; OutEven[]; END; WriteOutNames: PROCEDURE = BEGIN FOR i: CARDINAL IN [0..numberOfNames) DO name: LONG POINTER TO Name ← @n[nameTable[i]]; OutBlock[name, NameSize[name]]; OutEven[]; ENDLOOP; END; WriteOutAddrs: PROCEDURE = BEGIN FOR i: CARDINAL IN [0..numberOfAddrs) DO addr: LONG POINTER TO Addr ← @a[addrTable[i]]; OutBlock[addr, SIZE[Addr]]; OutEven[]; ENDLOOP; END; WriteOutStrings: PROCEDURE = BEGIN OutBlock[s + fixup, (nextString - stringFudge)]; OutEven[]; END; sink: Stream.Handle ← NIL; output: CARDINAL; checksum: WORD; FindOutputFile: PROCEDURE = BEGIN sink ← MStream.WriteOnly["Pup-network.directory"L, [], binary]; output ← 0; checksum ← 0; END; OutBlock: PROCEDURE [p: LONG POINTER, words: CARDINAL] = BEGIN [] ← Stream.PutBlock[sink, [p, 0, 2*words]]; IF LONG[words] + output > LAST[CARDINAL] THEN Abort["Output file length > 64K."]; output ← output + words; checksum ← Checksum.ComputeChecksum[checksum, words, p]; END; OutZeros: PROCEDURE [l: CARDINAL] = BEGIN zero: WORD ← 0; THROUGH [0..l) DO OutBlock[@zero, 1]; ENDLOOP; END; OutEven: PROCEDURE = BEGIN IF (output MOD 2) # 0 THEN OutZeros[1]; END; CloseOutputFile: PROCEDURE = BEGIN Stream.PutWord[sink, checksum]; Stream.Delete[sink]; sink ← NIL; END; Announce: PROCEDURE [s: LONG STRING] = BEGIN text: STRING = [30]; Time.AppendCurrent[text]; Put.Text[stick, text]; Put.Char[stick, ' ]; Put.Char[stick, ' ]; Put.Line[stick, s]; END; PrintInfo: PROCEDURE = BEGIN Put.CR[stick]; Put.LongDecimal[stick, numberOfNames]; Put.Text[stick, " out of "]; Put.LongDecimal[stick, maxNamesInFile]; Put.Line[stick, " slots in the name table were used."]; Put.LongDecimal[stick, numberOfAddrs]; Put.Text[stick, " out of "]; Put.LongDecimal[stick, maxAddrsInFile]; Put.Line[stick, " slots in the address table were used."]; Put.LongDecimal[stick, numberOfEntries]; Put.Text[stick, " out of "]; Put.LongDecimal[stick, maxEntrysInFile]; Put.Line[stick, " slots in the entry table were used."]; Put.CR[stick]; Put.LongDecimal[stick, numberOfSkips]; Put.Line[stick, " DLion names were skipped."]; Put.LongDecimal[stick, numberOfDiscards]; Put.Line[stick, " DLion entrys were discarded."]; Put.CR[stick]; Put.LongDecimal[stick, nextName - nameFudge]; Put.Line[stick, " words in the name buffer were used."]; Put.LongDecimal[stick, nextAddr - addrFudge]; Put.Line[stick, " words in the address buffer were used."]; Put.LongDecimal[stick, nextEntry - entryFudge]; Put.Line[stick, " words in the entry buffer were used."]; END; LessPupAddress: PROCEDURE [a, b: LONG POINTER TO PupAddress] RETURNS [BOOLEAN] = BEGIN IF a.net < b.net THEN RETURN[TRUE]; IF a.net > b.net THEN RETURN[FALSE]; IF a.host < b.host THEN RETURN[TRUE]; IF a.host > b.host THEN RETURN[FALSE]; IF a.socket.a < b.socket.a THEN RETURN[TRUE]; IF a.socket.a > b.socket.a THEN RETURN[FALSE]; IF a.socket.b < b.socket.b THEN RETURN[TRUE]; IF a.socket.b > b.socket.b THEN RETURN[FALSE]; RETURN[FALSE]; END; PutPupAddress: PROCEDURE [where: Window.Handle, p: PupAddress] = BEGIN Put.Number[where, p.net, [8, FALSE, TRUE, 0]]; Put.Char[where, '#]; Put.Number[where, p.host, [8, FALSE, TRUE, 0]]; Put.Char[where, '#]; IF p.socket.a # 0 THEN BEGIN Put.Number[where, p.socket.a, [8, FALSE, TRUE, 0]]; Put.Char[where, '|]; END; Put.Number[where, p.socket.b, [8, FALSE, TRUE, 0]]; END; PutBcplString: PROCEDURE [where: Window.Handle, s: BcplString] = BEGIN FOR i: CARDINAL IN [0..s.length) DO Put.Char[where, s.char[i]]; ENDLOOP; END; AppendCharToBcplString: PROCEDURE [where: BcplString, c: CHARACTER] = BEGIN IF where.length = PupWireFormat.BcplMaxLength THEN ParseError["String too long"]; where.char[where.length] ← c; where.length ← where.length + 1; END; AppendCharToString: PROCEDURE [where: LONG STRING, c: CHARACTER] = BEGIN IF where.length = where.maxlength THEN ParseError["String too long"]; where[where.length] ← c; where.length ← where.length + 1; END; EquivBcplStrings: PROCEDURE [a, b: BcplString] RETURNS [BOOLEAN] = BEGIN i: CARDINAL; IF a.length # b.length THEN RETURN[FALSE]; FOR i IN [0..a.length) DO IF String.UpperCase[a.char[i]] # String.UpperCase[b.char[i]] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; END; EqualBcplStrings: PROCEDURE [a, b: BcplString] RETURNS [BOOLEAN] = BEGIN IF a.length # b.length THEN RETURN[FALSE]; FOR i: CARDINAL IN [0..a.length) DO IF a.char[i] # b.char[i] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; END; Same: PROCEDURE [s: LONG STRING, t: BcplString] RETURNS [BOOLEAN] = BEGIN IF s.length # t.length THEN RETURN[FALSE]; FOR i: CARDINAL IN [0..s.length) DO IF String.UpperCase[s[i]] # String.UpperCase[t.char[i]] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; END; LessBcplStrings: PROCEDURE [a, b: BcplString] RETURNS [BOOLEAN] = BEGIN FOR i: CARDINAL IN [0..MIN[a.length, b.length]) DO x: CHARACTER ← String.UpperCase[a.char[i]]; y: CHARACTER ← String.UpperCase[b.char[i]]; IF x < y THEN RETURN[TRUE]; IF x > y THEN RETURN[FALSE]; ENDLOOP; RETURN[a.length < b.length]; END; GetMeOutOfHere: SIGNAL = CODE; Abort: PROCEDURE [s: LONG STRING] = BEGIN Put.CR[stick]; Put.Text[stick, "*** "]; Put.Line[stick, s]; Put.CR[stick]; ERROR GetMeOutOfHere; END; errors: CARDINAL ← 0; ParsingError: SIGNAL = CODE; ParseError: PROCEDURE [s: LONG STRING] = BEGIN Put.Text[stick, "*** "]; Put.Line[stick, s]; Put.Line[stick, statement]; THROUGH [0..finger) DO Put.Char[stick, Ascii.SP]; ENDLOOP; Put.Char[stick, '↑]; Put.CR[stick]; errors ← errors + 1; SIGNAL ParsingError; END; CheckForErrors: PROCEDURE = BEGIN IF errors = 0 THEN RETURN; Put.LongDecimal[stick, errors]; Put.Line[stick, " errors."]; Abort["Errors encountered."]; END; TryStringAsOctal: PROCEDURE [s: LONG STRING] RETURNS [BOOLEAN, LONG CARDINAL] = BEGIN val: LONG CARDINAL ← 0; FOR i: CARDINAL IN [0..s.length) DO c: CHARACTER ← s[i]; IF c ~IN ['0..'7] THEN RETURN[FALSE, 0]; IF val > 3777777777B THEN RETURN[FALSE, 0]; val ← val*8 + (c - '0); ENDLOOP; RETURN[TRUE, val]; END; AllocateThings: PROCEDURE = BEGIN nameTable ← MSegment.GetWords[maxNamesInFile]; addrTable ← MSegment.GetWords[maxAddrsInFile]; entryTable ← MSegment.GetWords[maxEntrysInFile]; stringTable ← MSegment.GetWords[maxStringsInFile]; scratch ← MSegment.GetWords[scratchSize]; e ← MSegment.GetPages[256]; n ← MSegment.GetPages[256]; a ← MSegment.GetPages[256]; s ← MSegment.GetPages[256]; END; FreeThings: PROCEDURE = BEGIN MSegment.FreeWords[nameTable]; MSegment.FreeWords[addrTable]; MSegment.FreeWords[entryTable]; MSegment.FreeWords[stringTable]; MSegment.FreeWords[scratch]; MSegment.FreePages[e]; MSegment.FreePages[n]; MSegment.FreePages[a]; MSegment.FreePages[s]; END; END.