PupNetImpl:
CEDAR
PROGRAM
IMPORTS Ascii, Convert, FS, IO, Rope
EXPORTS PupNet = {
buffer: REF TEXT ← NEW[TEXT[100]];
eof: BOOLEAN;
inputStream: IO.STREAM ← NIL;
-- Input IO sream
PupNetImplERROR: ERROR ~ CODE;
NetRecord:
TYPE ~
RECORD [
name: Rope.ROPE ← NIL,
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: BOOLEAN ← FALSE] = {
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.STREAM ← IO.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.STREAM ← IO.RIS[rope: netAddress];
valueRope: Rope.ROPE ← IO.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.
ROPE ←
NIL, gateway, network:
BOOLEAN ←
FALSE] = {
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: BOOLEAN ← FALSE;
[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.ROPE ← NIL, eol: BOOLEAN ← FALSE] = {
<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: BOOLEAN ← FALSE] = {
<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.
ROPE ←
NIL] = {
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: BOOLEAN ← FALSE;
name, value: Rope.ROPE;
sloppy: BOOLEAN ← FALSE;
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:
BOOLEAN ←
FALSE] = {
<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[];
};
}.