PupNetImpl.mesa
-- Gifford, July 19, 1985 3:25:05 pm PDT
-- This program parses pup-network.txt, extracting host names, owners, and locations
Donahue, May 21, 1986 11:31:37 am PDT
Ewan Tempero August 25, 1986 3:42:56 pm PDT
DIRECTORY
Ascii,
Convert USING [RopeFromCard, CardFromRope],
FS,
IO,
PupNet,
Rope;
PupNetImpl: CEDAR PROGRAM
IMPORTS Ascii, Convert, FS, IO, Rope
EXPORTS PupNet = {
buffer: REF TEXTNEW[TEXT[100]];
eof: BOOLEAN;
inputStream: IO.STREAMNIL;
-- Input IO sream
PupNetImplERROR: ERROR ~ CODE;
NetRecord: TYPE ~ RECORD [
name: Rope.ROPENIL,
value: CARD ← 0
];
NetData: TYPE ~ REF NetRecord;
netList: LIST OF NetData ← NIL;
breakProc: IO.BreakProc = {
SELECT char FROM
IN [Ascii.NUL .. Ascii.FF], IN[Ascii.ESC .. Ascii.SP], '\255 => RETURN[IO.CharClass[sepr]];
':, '=, '", ';, Ascii.CR, ', => RETURN[IO.CharClass[break]];
ENDCASE => RETURN[IO.CharClass[other]]};
GetToken: PROC []
RETURNS [token: REF TEXT] = {
get an input token
token ← IO.GetToken[inputStream, breakProc, buffer
! IO.EndOfStream => {
buffer[0] ← Ascii.CR; token ← buffer; eof ← TRUE; CONTINUE}
].token;
};
CheckToken: PROC [c1, c2: CHAR]
RETURNS [eol: BOOLEANFALSE] = {
check to see if next token is either c1 or c2
token: REF TEXT ← GetToken[];
IF token[0]=Ascii.CR THEN {eol ← TRUE; RETURN};
IF token[0]#c1 AND token[0]#c2 THEN {
SkipToEOL[]; eol ← TRUE; RETURN};
};
SkipToEOL: PROC [] = {
skip to the end of the line
WHILE GetToken[].token[0] # Ascii.CR DO ENDLOOP;
};
GetNetNameTokenRope: PROC [stream: IO.STREAM] RETURNS [token: Rope.ROPE] ~ {
... returns the next symbolic net name found in stream.
break: IO.BreakProc = {
[char: CHAR] RETURNS [IO.CharClass]
SELECT char FROM
'# => RETURN [IO.CharClass.break];
ENDCASE => RETURN [IO.CharClass.other]
}; -- break
RETURN [IO.GetTokenRope[stream: stream, breakProc: break].token]
}; -- GetNetNameTokenRope
ConvertNetAddress: PROC [symbolicName: Rope.ROPE, convertList: LIST OF NetData] RETURNS [realNetName: Rope.ROPE] ~ {
...takes symbolicName which is a net address using a symbolic net name and converts it to its real net address using the information in convertList.
stream: IO.STREAMIO.RIS[rope: symbolicName];
symbolicNetName: Rope.ROPE ← GetNetNameTokenRope[stream: stream].token;
value: CARD ← FindValueInList[name: symbolicNetName, list: convertList];
realNetName ← Rope.Cat[Convert.RopeFromCard[from: value], Rope.Substr[base: symbolicName, start: Rope.Length[base: symbolicNetName] ]]
}; -- Convert
GetNetValue: PROC [netAddress: Rope.ROPE] RETURNS [value: CARD] ~ {
... looks at netAddress and determines the net value.
stream: IO.STREAMIO.RIS[rope: netAddress];
valueRope: Rope.ROPEIO.GetCedarTokenRope[stream: stream].token;
RETURN[value ← Convert.CardFromRope[r: valueRope]]
}; -- GetNetValue
FindValueInList: PROC [name: Rope.ROPE, list: LIST OF NetData] RETURNS [value: CARD] ~ {
... finds the occurance of name in list and returns the corresponding value.
FOR netItem: LIST OF NetData ← list, netItem.rest UNTIL netItem = NIL DO
IF Rope.Equal[s1: name, s2: netItem.first.name, case: FALSE] THEN RETURN[value ← netItem.first.value]
ENDLOOP;
RETURN [0] -- somethings wrong if we don't have a value
}; -- FindValueInList
ParseLine: PROC [] RETURNS [name, owner, location, netAddress: Rope.ROPENIL, gateway, network: BOOLEANFALSE] = {
Parses a single line of the input file
Precondition: Stream pointer at beginning of line
Postcondition: Stream pointer at beginning of next line
Grammar of an input line:
<entry> ::= <name list> = <address list> ; <attribute list>
eol, socket: BOOLEANFALSE;
[name, eol] ← ParseNameList[];
IF eol THEN RETURN;
[netAddress, network, gateway, socket, eol] ← ParseAddressList[name: name];
IF socket THEN RETURN[NIL, NIL, NIL, NIL, FALSE, FALSE];
IF eol THEN RETURN;
[owner, location] ← ParseAttributeList[];
};
ParseNameList: PROC []
RETURNS [name: Rope.ROPENIL, eol: BOOLEANFALSE] = {
<name list> ::= <name> | <name list> , <name>
<name> ::= * | <string composed of alphanumerics, "-", "/">
Precondition: Stream at beginning of name list
Postcondition: Stream positioned at beginning of address list
Postcondition: If name found, name#NIL
t: REF TEXT;
WHILE TRUE DO
t ← GetToken[];
SELECT t[0] FROM
Ascii.CR => {eol ← TRUE; RETURN};
'; => {SkipToEOL[]; eol ← TRUE; RETURN};
'= => RETURN;
IN ['a .. 'z], IN['A .. 'Z] => name ← IF name#NIL THEN
Rope.Cat[name, " ", Rope.FromRefText[t]]
ELSE Rope.FromRefText[t];
ENDCASE;
ENDLOOP;
};
ParseAddressList: PROC [name: Rope.ROPE]
RETURNS [netAddress: Rope.ROPE, network, gateway, socket, eol: BOOLEANFALSE] = {
<address list> ::= <address> | <address list> , <address>
<address> ::= <expression> | <expression>
<expression> ::= <term> | <expression> + <term>
<term> ::= <name> | <address constant>
<address constant> ::= <element> | <element> # <element> |
<element> # <element> # <element>
<element> ::= <octal number> | empty
Precondition: Stream positioned at beginning of address list. AND ALL NETWORKS are listed before anything else. I think this is probably reasonable --edt.
Postconditions:
network is T if the entry is a network
gateway is T if the entry is a gateway
socket is T if the entry is a socket id and should be ignored
eol is T if we have hit the end of the line
Input Stream positioned at beginning of attribute list
count: INT ← 0;
pos: INT ← 0;
t: REF TEXT;
tNetAddress: Rope.ROPE;
WHILE TRUE DO
t ← GetToken[];
IF t[0] = ', THEN t ← GetToken[];
SELECT t[0] FROM
Ascii.CR => {eol ← TRUE; RETURN};
'; => RETURN
ENDCASE;
see if a socket
IF t[t.length-1]#'# THEN {
socket ← TRUE;
eol ← TRUE;
SkipToEOL[];
RETURN;
};
not a socket id. see if a network
IF t.length < 2 THEN LOOP;
tNetAddress ← Rope.FromRefText[t];
if the address ends in "##", then it is a network
IF t[t.length-1]='# AND t[t.length-2]='# THEN {
network ← TRUE;
netList ← CONS[NEW[NetRecord ← [name: name, value: GetNetValue[tNetAddress]]], netList];
};
A little hack to make sure that none of the "+"s that the maintainers of this file seem to love are still in the net address ( there should be only 1 and will only be in non-network machines ).
IF (pos ← Rope.Find[tNetAddress, "+"]) # -1 THEN tNetAddress ← Rope.Replace[base: tNetAddress, start: pos, len: 1, with:"#"];
IF NOT network THEN tNetAddress ← ConvertNetAddress[tNetAddress, netList];
netAddress ← IF netAddress#NIL THEN
Rope.Cat[netAddress, " ", tNetAddress]
ELSE tNetAddress;
if there is more than one address, then the host is a gateway
count ← count + 1;
IF count > 1 THEN gateway ← TRUE;
ENDLOOP;
};
ParseAttributeList: PROC []RETURNS [owner, location: Rope.ROPENIL] = {
The grammar for an attribute list USED TO BE:
<attribute list> ::= <attribute> | <attribute list> , <attribute>
Unfortunately, because this part of pup-network.txt is no longer
parsed, people have become sloppy.
People now tend to use something of the form
<attribute list> ::= <location attribute>, <owner attribute>
and this checks for both forms. --edt
eol: BOOLEANFALSE;
name, value: Rope.ROPE;
sloppy: BOOLEANFALSE;
Examine each attribute until we get to the end of line
UNTIL eol DO
[name, value, eol] ← ParseAttribute[];
IF name#NIL AND value#NIL AND (NOT Rope.Equal[value, "?"]) THEN {
IF Ascii.Upper[name.Fetch[0]]='L THEN location ← value;
IF Ascii.Upper[name.Fetch[0]]='O THEN owner ← value;
IF (Ascii.Upper[name.Fetch[0]]='S) THEN
IF sloppy THEN owner ← value ELSE { sloppy ← TRUE; location ← value } -- hack! hack! (he cried)
};
ENDLOOP;
};
ParseAttribute: PROC []RETURNS [name, value: Rope.ROPE, eol: BOOLEANFALSE] = {
<attribute> ::= <name> : " <attribute value> " | <location room number> | <owner name>
precondition seems to be start is at after the ";" --edt.
t: REF TEXT;
t ← GetToken[];
IF t[0] = ', THEN t ← GetToken[]; -- this is messy...
IF t[0]=Ascii.CR THEN {eol ← TRUE; RETURN};
name ← value ← Rope.FromRefText[t];
t← GetToken[];
IF t[0]='= OR t[0]=': THEN
BEGIN
eol ← CheckToken['", '"];
IF eol THEN RETURN;
t ← GetToken[];
value ← NIL
END
ELSE name ← "Some random attribute (location or owner)";
UNTIL t[0]='" OR t[0]=Ascii.CR OR t[0]=', DO
value ← IF value#NIL THEN
Rope.Cat[value, " ", Rope.FromRefText[t]]
ELSE Rope.FromRefText[t];
t ← GetToken[];
ENDLOOP;
IF t[0]=Ascii.CR THEN eol ← TRUE;
};
ReadFile: PUBLIC PROC [file: Rope.ROPE, proc: PupNet.P, log: IO.STREAM] = {
reads a file and calls proc
name, owner, location, netAddress: Rope.ROPE;
gateway, network: BOOLEAN;
sillyCount: CARDINAL ← 0;
eof ← FALSE;
open the input stream
inputStream ← FS.StreamOpen[file];
now read the entire file
UNTIL eof DO
[name, owner, location, netAddress, gateway, network] ← ParseLine[];
IF sillyCount = 100 THEN {
sillyCount ← 0;
log.PutF["+ "];
}
ELSE sillyCount ← sillyCount + 1;
IF name#NIL THEN proc[name, owner, location, netAddress, gateway, network];
ENDLOOP;
at end of file
inputStream.Close[];
};
}.