PupNetImpl: CEDAR PROGRAM
IMPORTS Ascii, FS, IO, Rope
EXPORTS PupNet = {
buffer: REF TEXT ← NEW[TEXT[100]];
eof: BOOLEAN;
inputStream: IO.STREAM ← NIL;
-- Input IO sream
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;
};
ParseLine: PROC []
RETURNS [name, owner, location: 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;
[network,gateway,socket, eol] ← ParseAddressList[];
IF socket THEN RETURN[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 ← Rope.FromRefText[t];
ENDCASE;
ENDLOOP;
};
ParseAddressList: PROC []
RETURNS [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
-- 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;
t: REF TEXT;
WHILE TRUE DO
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;
-- if the address ends in "##", then it is a network
IF t[t.length-1]='# AND t[t.length-2]='# THEN
{network ← TRUE; LOOP};
-- 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.
eol: BOOLEAN ← FALSE;
name, value: Rope.ROPE;
-- 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;
};
ENDLOOP;
};
ParseAttribute: PROC []
RETURNS [name, value: Rope.ROPE, eol: BOOLEAN ← FALSE] = {
-- <attribute> ::= <name> : " <attribute value> "
t: REF TEXT;
t ← GetToken[];
IF t[0]=Ascii.CR THEN {eol ← TRUE; RETURN};
name ← Rope.FromRefText[t];
eol ← CheckToken[':, '=];
IF eol THEN RETURN;
eol ← CheckToken['", '"];
IF eol THEN RETURN;
t ← GetToken[];
UNTIL t[0]='" OR t[0]=Ascii.CR 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] = {
-- reads a file and calls proc
name, owner, location: Rope.ROPE;
gateway, network: BOOLEAN;
eof ← FALSE;
-- open the input stream
inputStream ← FS.StreamOpen[file];
-- now read the entire file
UNTIL eof DO
[name, owner, location, gateway, network] ← ParseLine[];
IF name#NIL THEN proc[name, owner, location, gateway, network];
ENDLOOP;
-- at end of file
inputStream.Close[];
};
}.