-- 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.