<<>> <> <> <> <> <> <> DIRECTORY Arpa, Convert, IO, NetworkName, PFS, Rope, SymTab ; NetworkNameEtcHostsImpl: CEDAR MONITOR <> IMPORTS Convert, IO, NetworkName, PFS, Rope, SymTab ~ { OPEN NN: NetworkName; <> ROPE: TYPE ~ Rope.ROPE; <> myFamily: ATOM ¬ $ARPA; myFlavor: ATOM ¬ $EtcHosts; <> SplitNameOrAddress: PROC [name: ROPE] RETURNS [hostPart: ROPE ¬ NIL, portPart: ROPE ¬ NIL] ~ { portPos, hostLen: INT; pos: INT ¬ 0; IF Rope.IsEmpty[name] THEN RETURN; SELECT TRUE FROM (pos ¬ Rope.FindBackward[name, ":"]) >= 0 => { portPos ¬ pos+1; hostLen ¬ pos; }; (pos ¬ Rope.FindBackward[name, "]"]) >= 0 => { portPos ¬ pos+1; hostLen ¬ pos+1; }; ENDCASE => { portPos ¬ Rope.Length[name]; hostLen ¬ portPos; }; portPart ¬ Rope.Substr[name, portPos]; hostPart ¬ Rope.Substr[name, 0, hostLen]; IF (hostLen >= 2) AND (Rope.Fetch[hostPart, 0] = '[) AND (Rope.Fetch[hostPart, hostLen-1] = ']) THEN hostPart ¬ Rope.Substr[hostPart, 1, hostLen-2]; }; ConvertHostLiteral: PROC [literal: ROPE] RETURNS [ok: BOOL ¬ FALSE, host: ROPE ¬ NIL] ~ { ENABLE Convert.Error => CONTINUE; len: INT; host ¬ Convert.RopeFromArpaAddress[ IF Rope.IsEmpty[literal] THEN Arpa.nullAddress ELSE Convert.ArpaAddressFromRope[literal] ]; len ¬ Rope.Length[host]; IF (len >= 2) AND (Rope.Fetch[host, 0] = '[) THEN host ¬ Rope.Substr[host, 1, len-2]; ok ¬ TRUE; }; LookupHostName: ENTRY PROC [name: ROPE] RETURNS [addr: ROPE ¬ NIL] ~ { errorCodes: LIST OF ATOM ¬ NIL; found: BOOL; val: REF; [found, val] ¬ SymTab.Fetch[hostsByName, name]; IF found THEN RETURN[NARROW[val]] ELSE RETURN WITH ERROR NN.Error[ LIST[$notFound], "EtcHosts lookup error" ]; }; ConvertPortLiteral: PROC [portLiteral: ROPE] RETURNS [ok: BOOL ¬ FALSE, port: ROPE ¬ NIL] ~ { ENABLE Convert.Error => CONTINUE; n: CARD; n ¬ IF Rope.IsEmpty[portLiteral] THEN 0 ELSE Convert.CardFromRope[portLiteral]; port ¬ Convert.RopeFromCard[n]; ok ¬ TRUE; }; AddressFromName: NN.AddressFromNameProc -- [r, name, portHint, components] RETURNS [addr] -- ~ { host, port, hostName, portName: ROPE ¬ NIL; ok: BOOL; addr ¬ NIL; [hostName, portName] ¬ SplitNameOrAddress[name]; SELECT components FROM host, hostAndPort => { [ok, host] ¬ ConvertHostLiteral[hostName]; IF NOT ok THEN host ¬ LookupHostName[hostName]; -- ! NN.Error }; ENDCASE; SELECT components FROM port, hostAndPort => { IF Rope.IsEmpty[portName] THEN portName ¬ portHint; [ok, port] ¬ ConvertPortLiteral[portName]; IF NOT ok THEN NN.Error[ LIST[$notFound], "port name not found"]; -- no symbolic port names from this package. }; ENDCASE; addr ¬ host; IF NOT Rope.IsEmpty[portName] THEN addr ¬ Rope.Cat[addr, ":", port]; }; LookupHost: ENTRY PROC [host: ROPE] RETURNS [hostName: ROPE ¬ NIL] ~ { errorCodes: LIST OF ATOM ¬ NIL; found: BOOL; val: REF; [found, val] ¬ SymTab.Fetch[hostsByAddr, host]; IF found THEN RETURN[NARROW[val]] ELSE RETURN WITH ERROR NN.Error[ LIST[$notFound], "EtcHosts lookup error" ]; }; NameFromAddress: NN.NameFromAddressProc -- [r, addr, components] RETURNS [name] -- ~ { host, port, hostName, portName: ROPE ¬ NIL; ok: BOOL; name ¬ NIL; [host, port] ¬ SplitNameOrAddress[addr]; SELECT components FROM host, hostAndPort => { IF NOT Rope.IsEmpty[host] THEN { [ok, host] ¬ ConvertHostLiteral[host]; IF NOT ok THEN ERROR NN.Error[ LIST[$syntax], "host address syntax"]; hostName ¬ LookupHost[host]; -- ! NN.Error }; }; ENDCASE; SELECT components FROM port, hostAndPort => { IF NOT Rope.IsEmpty[port] THEN { [ok, port] ¬ ConvertPortLiteral[port]; IF NOT ok THEN ERROR NN.Error[ LIST[$syntax], "port number syntax"]; portName ¬ port; -- no symbolic port names from this package }; }; ENDCASE; SELECT components FROM host => RETURN [hostName]; port => RETURN [portName]; hostAndPort => RETURN [Rope.Cat[hostName, ":", portName]]; ENDCASE => ERROR; }; hostsByName: SymTab.Ref ¬ SymTab.Create[]; hostsByAddr: SymTab.Ref ¬ SymTab.Create[]; nLines: CARD ¬ 0; GetNLines: PROC [] RETURNS [CARD] ~ { RETURN[nLines]; }; fileName: PFS.PATH ~ PFS.PathFromRope["/etc/hosts"]; CacheFile: ENTRY PROC [] RETURNS [] ~ { hosts: IO.STREAM ¬ PFS.StreamOpen[fileName, read ! IO.Error, PFS.Error => GOTO Out]; ReadOneLine: PROC [] RETURNS [BOOL ¬ TRUE] ~ { WhiteSpace: IO.BreakProc ~ { IF (char = '#) THEN comment ¬ TRUE; RETURN[SELECT TRUE FROM comment, char IN [IO.NUL .. IO.SP] => sepr, ENDCASE => other ]; }; comment: BOOL ¬ FALSE; -- WhiteSpace state variable line: ROPE ¬ IO.GetLineRope[hosts ! IO.EndOfStream, PFS.Error => GOTO none]; addr: ROPE; firstOne: BOOL ¬ TRUE; s: IO.STREAM; IF ( line.Length[] = 0 ) THEN RETURN; s ¬ IO.RIS[line]; addr ¬ s.GetTokenRope[WhiteSpace ! IO.EndOfStream => GO TO some].token; DO name: ROPE ¬ s.GetTokenRope[WhiteSpace ! IO.EndOfStream => GO TO some].token; [] ¬ hostsByName.Store[name, addr]; IF firstOne THEN [] ¬ hostsByAddr.Store[addr, name]; firstOne ¬ FALSE; ENDLOOP; EXITS none => RETURN[FALSE]; some => RETURN[TRUE]; }; hostsByName.Erase[]; hostsByAddr.Erase[]; { ENABLE IO.Error, PFS.Error => GOTO Close; nLines ¬ 0; WHILE ReadOneLine[] DO nLines ¬ nLines+1 ENDLOOP; hosts.Close[]; EXITS Close => { IO.Close[hosts ! IO.Error, PFS.Error => CONTINUE]; hostsByName.Erase[]; hostsByAddr.Erase[]; }; }; EXITS Out => NULL; }; RegisterSelf: PROC ~ { r: NN.Registration; RegistrationCallback: NN.RegistrationCallbackProc -- [oldRegistration] RETURNS [action, newRegistration] -- ~ { RETURN [insert, r]; }; r ¬ NEW[NN.RegistrationObject ¬ [ family~myFamily, flavor~myFlavor, addressFromName~AddressFromName, nameFromAddress~NameFromAddress ]]; NN.Register[myFamily, RegistrationCallback]; }; CacheFile[]; RegisterSelf[]; }. <> <> <>