Skip to main content

  | मौसम…

411 समाचार
Public archive edition · Attributed source reporting with original dates · AI drafts are excluded

411 REFERENCE · Wikibooks

Oberon/A2/Unix.Oberon.NetSystem.Mod

Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.

(* ETH Oberon, Copyright 2000 ETH Zürich Institut für Computersysteme, ETH Zentrum, CH-8092 Zürich. Refer to the general ETH Oberon System license contract available at: http://www.oberon.ethz.ch/ *) MODULE NetSystem IN Oberon; IMPORT S := SYSTEM, IP IN A2, DNS := DNS IN A2, TCP := TCP IN A2, UDP := UDP IN A2, Texts, Oberon, Input, Fonts, Strings, Out; CONST CR = 0DX; LF = 0AX; (* res values *) done* = 0; (*everything went ok*) error* = 1; (*failure occured*) timeout* = 2; (*opening a connection is timed out*) (* return values of procedure State *) closed* = 0; (*connection is closed (neither sending nor receiving)*) listening* = 1; (*passive connection is listening for a request*) in* = 2; (*receiving only*) out* = 3; (*sending only*) inout* = 4; (*sending and receiving is possible*) waitCon* = 5; (** still waiting for connection *) errorCon* = 6; (** connecting failed *) undef = -1; (** unknown state *) IPAdrLen = 4; (* any port value *) anyport* = 0; TYPE IPAdr* = IP.Adr; Connection* = TCP.Connection; (* TCP-Connection *) HostInfo* = POINTER TO HostInfoDesc; (** handle for asynchronous GetIP and GetName *) HostInfoDesc* = RECORD next: HostInfo; ip-: IPAdr; (** the ip-number of host name *) name-: ARRAY 64 OF CHAR; (** the host name for ip-number *) done-, err-, getip: BOOLEAN (** indicating success or failure *) END; Socket*= UDP.Socket; (* UDP-Connection *) Access = POINTER TO AccessDesc; AccessDesc = RECORD service, user, host, passwd: ARRAY 64 OF CHAR; next: Access END; Bytes = ARRAY MAX( SIGNED32 ) OF CHAR; VAR hostIP* : IPAdr; (** the ip-number of host name *) anyIP*, allIP*: IPAdr; hostName*: ARRAY 65 OF CHAR; (** own machine name *) hostInfos: HostInfo; W: Texts.Writer; accessList: Access; verbose: BOOLEAN; PROCEDURE Start*; VAR res: INTEGER; BEGIN DNS.GetHostName( hostName, res ); IF res = DNS.Ok THEN DNS.HostByName( hostName, hostIP, res ); END END Start; PROCEDURE Stop*; BEGIN END Stop; PROCEDURE ToHost* ( CONST num: ARRAY OF CHAR; VAR adr: IPAdr; VAR done: BOOLEAN ); BEGIN adr := IP.StrToAdr(num); done := ~(IP.IsNilAdr(adr)) END ToHost; PROCEDURE ToNum*( adr: IPAdr; VAR num: ARRAY OF CHAR ); BEGIN IP.AdrToStr(adr, num) END ToNum; PROCEDURE AsyncGetIP*( VAR hostInfo: HostInfo; name: ARRAY OF CHAR ); VAR res: INTEGER; BEGIN NEW( hostInfo ); Strings.Lower( name, name ); hostInfo.next := NIL; COPY( name, hostInfo.name ); hostInfo.done := FALSE; hostInfo.err := FALSE; IF (name[0] >= "0") & (name[0] <= "9") THEN DNS.HostByNumber( hostInfo.ip, name, res ); hostInfo.err := res # DNS.Ok; hostInfo.done := TRUE ELSE DNS.HostByName( name, hostInfo.ip, res ); (* human comprehensible name *) IF res = DNS.Ok THEN hostInfo.err := FALSE; hostInfo.done := TRUE; hostInfo.next := hostInfos; hostInfos := hostInfo ELSE hostInfo.err := TRUE END END; END AsyncGetIP; PROCEDURE GetIP* ( CONST name: ARRAY OF CHAR; VAR IP: IPAdr ); VAR hostInfo: HostInfo; BEGIN IP := anyIP; AsyncGetIP( hostInfo, name ); IF ~hostInfo.err THEN IP := hostInfo.ip END END GetIP; PROCEDURE AsyncGetName*( VAR hostInfo: HostInfo; IP: IPAdr ); VAR res: INTEGER; BEGIN NEW( hostInfo ); hostInfo.getip := FALSE; hostInfo.next := NIL; S.MOVE( ADDRESSOF( IP ), ADDRESSOF( hostInfo.ip ), IPAdrLen ); hostInfo.done := FALSE; hostInfo.err := FALSE; DNS.HostByNumber( hostInfo.ip, hostInfo.name, res ); IF res = DNS.Ok THEN hostInfo.err := FALSE; hostInfo.done := TRUE; hostInfo.next := hostInfos; hostInfos := hostInfo ELSE hostInfo.err := TRUE; hostInfo.name[0]:= 0X END END AsyncGetName; PROCEDURE GetName* ( IP: IPAdr; VAR name: ARRAY OF CHAR ); VAR hostInfo: HostInfo; BEGIN COPY("", name); AsyncGetName( hostInfo, IP ); IF ~hostInfo.err THEN COPY( hostInfo.name, name ) END END GetName; (** Passwords *) PROCEDURE WriteURL( CONST service, user, host: ARRAY OF CHAR ); BEGIN Out.String("NetSystem.SetUser "); Out.String(service); Out.Char(":"); Out.String(user); Out.Char("@"); Out.String(host); Out.String(" ~"); Out.Ln END WriteURL; (** Retrieve the password for user using service on host. Parameters service, host and user can be specified. Parameter user is in/out. If user is empty, the first (user,password) pair for host is returned. Otherwise the password of the user is returned. *) PROCEDURE GetPassword*(service, host: ARRAY OF CHAR; VAR user, password: ARRAY OF CHAR); VAR access: Access; r: Texts.Reader; ch: CHAR; BEGIN Strings.Lower(service, service); Strings.Lower(host, host); access := accessList; WHILE (access # NIL) & ~((access.service = service) & (access.host = host) & ((user = "") OR (access.user = user))) DO access := access.next END; IF access # NIL THEN COPY(access.user, user); COPY(access.passwd, password) ELSE IF (service # "") & (user # "") THEN IF Oberon.Log.len > 0 THEN Texts.OpenReader(r, Oberon.Log, Oberon.Log.len-1); Texts.Read(r, ch); IF ch # CHR(13) THEN Out.Ln END END; WriteURL(service, user, host); END; COPY("", user); COPY("", password) END END GetPassword; (** Remove access for user using service on host. *) PROCEDURE DelPassword*( CONST pservice, user, phost: ARRAY OF CHAR); VAR paccess, access: Access; service, host: ARRAY 64 OF CHAR; BEGIN Strings.Lower( pservice, service ); Strings.Lower( phost, host ); paccess := NIL; access := accessList; WHILE (access # NIL) & ((access.service # service) & (access.host # host) & (access.user # user)) DO paccess := access; access := access.next END; IF access # NIL THEN IF paccess # NIL THEN paccess.next := access.next ELSE accessList := access.next END END END DelPassword; (* An accessString is delimited by whitespace or "~" or a non-font entity. *) PROCEDURE ReadAccessString(VAR R: Texts.Reader; VAR accessString: ARRAY OF CHAR; VAR at: SIGNED32): BOOLEAN; VAR i: SIGNED32; BEGIN i := 0; at := -1; REPEAT Texts.Read(R, accessString[i]) (* Skip leading whitespace. *) UNTIL R.eot OR ((R.lib IS Fonts.Font) & (" " < accessString[i])); WHILE ~R.eot & (R.lib IS Fonts.Font) & (" " < accessString[i]) & (accessString[i] # "~") DO IF accessString[i] = "@" THEN at := i END; INC(i); Texts.Read(R, accessString[i]) END; accessString[i] := 0X; RETURN 0 < i END ReadAccessString; (* Extract next component from an accessString. *) PROCEDURE Next(VAR accessString, str: ARRAY OF CHAR; VAR a, at: SIGNED32): BOOLEAN; VAR i: SIGNED32; BEGIN i := 0; WHILE (accessString[a] # 0X) & (a # at) & (accessString[a] # ":") & (accessString[a] > " ") & (accessString[a] # "/") & (accessString[a] # "~") (* & (R.lib IS Fonts.Font)*) DO str[i] := accessString[a]; INC(i); INC(a) END; str[i] := 0X; RETURN 0 < i END Next; (* Accept a password from the user via the keyboard. *) PROCEDURE ReadPwd(VAR access: Access; VAR pwd: ARRAY OF CHAR; VAR ch: CHAR); VAR i: SIGNED32; BEGIN Texts.SetColor(W, 1); Texts.WriteString(W, "Mouse left click in Oberon to focus input.");Texts.WriteLn(W); Texts.WriteString(W, "Enter password for "); Texts.WriteString(W, access.service); Texts.WriteString(W, "://"); Texts.WriteString(W, access.user); Texts.Write(W, "@"); Texts.WriteString(W, access.host); Texts.WriteString(W, ": "); Texts.SetColor(W, 15); Texts.Append(Oberon.Log, W.buf); Input.Read(ch); i := 0; WHILE ch > " " DO IF ch = 7FX (* Delete *) THEN IF i > 0 THEN Texts.Delete(Oberon.Log, Oberon.Log.len-1, Oberon.Log.len); DEC(i) END ELSE Texts.Write(W, "*"); Texts.Append(Oberon.Log, W.buf); pwd[i] := ch; INC(i) END; Input.Read(ch) END; pwd[i] := 0X; Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf) END ReadPwd; (* access points to a tetrad. If a match to the (service, user, host) of access is found in accessList, the password is replaced. Otherwise access is prepended to accessList. *) PROCEDURE Update(access: Access); VAR a: Access; BEGIN IF verbose THEN Texts.WriteString(W, "Update of access list requested with "); Texts.WriteString(W, access.service); Texts.Write(W, ":"); Texts.WriteString(W, access.user); Texts.Write(W, ":"); IF access.passwd # "" THEN Texts.WriteString(W, "<password>") END; Texts.Write(W, "@"); Texts.WriteString(W, access.host); Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf) END; a := accessList; WHILE (a # NIL) & ~((a.service = access.service) & (a.host = access.host) & (a.user = access.user)) DO a := a.next END; IF a = NIL THEN IF verbose THEN Texts.WriteString(W, "Matching access not found. Prepend access for "); Texts.WriteString(W, access.service); Texts.WriteString(W, " to accessList.");Texts.WriteLn(W) END; access.next := accessList; accessList := access ELSE IF verbose THEN Texts.WriteString(W, "Matching access found. Copy password in."); Texts.WriteLn(W) END; COPY(access.passwd, a.passwd) END; Texts.Append(Oberon.Log, W.buf) END Update; (** NetSystem.SetUser {"\v" | access } "~". access = service ":" ["//"] user [":" password] "@" host [ "/" ]. If password is not specified in-line, submit interactively. "@" can occur anywhere in an accessString and host begins after the last @. The (service, host, user, password) tetrad is stored in memory for retrieval with GetPassword. *) PROCEDURE SetUser*; VAR R: Texts.Reader; service, usr, host, pwd: ARRAY 64 OF CHAR; (* Components of an accessDesc. *) accessString: ARRAY 256 OF CHAR; c: SIGNED32; (* Index in accessString. *) at: SIGNED32; (* Index of the last "@" in the accessString. *) acc: Access; tempAccess: Access; (* Temporary list of accesses for which passwords are pending. *) ch: CHAR; BEGIN (* Accesses where the password is included in parameters. *) Texts.OpenReader(R, Oberon.Par.text, Oberon.Par.pos); tempAccess := NIL; WHILE ReadAccessString(R, accessString, at) DO IF accessString = "\v" THEN verbose := TRUE ELSE c := 0; WHILE Next(accessString, service, c, at) DO Strings.Lower(service, service); IF verbose THEN Texts.WriteLn(W); Texts.WriteString(W, "service = "); Texts.WriteString(W, service); Texts.WriteLn(W) END; IF accessString[c] = ":" THEN INC(c); IF ~Next(accessString, usr, c, at) THEN Texts.WriteString(W, "For service = "); Texts.WriteString(W, service); Texts.WriteString(W, ", Next() failed to retrieve usr."); Texts.WriteLn(W) ELSE (* Found usr. Look fo password. *) IF accessString[c] = "@" THEN IF verbose THEN Texts.WriteString(W, "Password omitted. Prompt for it later."); Texts.WriteLn(W); END; ASSERT(c = at); INC(c); IF ~Next(accessString, host, c, at) THEN Texts.WriteString(W, "For service = "); Texts.WriteString(W, service); Texts.WriteString(W, ", Next() failed to retrieve host."); Texts.WriteLn(W) ELSE Strings.Lower(host, host); NEW(acc); acc.next := tempAccess; tempAccess := acc; (* prepend to temp list *) COPY(service, acc.service); COPY(host, acc.host); COPY(usr, acc.user) END ELSIF accessString[c] = ":" THEN IF verbose THEN Texts.WriteString(W, ": separator provided for password."); Texts.WriteLn(W); END; INC(c); IF ~Next(accessString, pwd, c, at) THEN Texts.WriteString(W, "For service = "); Texts.WriteString(W, service); Texts.WriteString(W, ", Next() failed to retrieve password."); Texts.WriteLn(W) ELSE IF verbose THEN IF pwd # "" THEN Texts.WriteString(W, "Password supplied in SetUser command."); Texts.WriteLn(W) ELSE Texts.WriteString(W, ": separator present but password not in parameter of SetUser()."); Texts.WriteLn(W) END END; IF accessString[c] = "@" THEN ASSERT(c = at); INC(c); IF ~Next(accessString, host, c, at) THEN Texts.WriteString(W, "For service = "); Texts.WriteString(W, service); Texts.WriteString(W, ", Next() failed to retrieve host."); Texts.WriteLn(W) ELSE IF verbose THEN Texts.WriteString(W, "host = "); Texts.WriteString(W, host); Texts.WriteLn(W) END; IF host # "" THEN NEW(acc); COPY(service, acc.service); COPY(host, acc.host); COPY(usr, acc.user); COPY(pwd, acc.passwd); Update(acc) END (* IF host # "" *) END (* ~Next(accessString, host, c, at) *) END (* IF accessString[c] = "@" *) END (* IF ~Next(accessString(accessString, pwd, c, at) *) END (* IF accessString[c] = "@" *) END (* ~Next(accessString, usr, c, at) *) END (* IF accessString = ":" *) END (* WHILE Next(accessString, service, c, at) *) END (* IF accessString = "\v" *) END; (* WHILE ReadAccessString *) (* Accesses where the password is requested interactively. *) WHILE tempAccess # NIL DO (* Fill password in each access of "temporary" list. *) ReadPwd(tempAccess, pwd, ch); IF ch = CR THEN (* password was entered *) acc := tempAccess; tempAccess := tempAccess.next; COPY(pwd, acc.passwd); Update(acc) END END; Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf) END SetUser; (** clears accessList from memory *) PROCEDURE ClearUser*; BEGIN accessList := NIL END ClearUser; (************************************** TCP ****************************************) PROCEDURE Available* (conn: Connection): SIGNED32; BEGIN RETURN conn.Available() END Available; PROCEDURE OpenConnection* ( VAR conn: Connection; locPort: UNSIGNED16; remIP: IPAdr; remPort: UNSIGNED16; VAR res: SIGNED16 ); VAR r: SIGNED32; BEGIN IF remPort = anyport THEN remIP := anyIP END; NEW( conn ); conn.Open( locPort, remIP, remPort MOD 10000H, r ); IF r = TCP.Ok THEN res := done ELSE res := error END END OpenConnection; PROCEDURE AsyncOpenConnection* ( VAR conn: Connection; locPort: UNSIGNED16; remIP: IPAdr; remPort: UNSIGNED16; VAR res: SIGNED16 ); BEGIN OpenConnection( conn, locPort, remIP, remPort, res ) END AsyncOpenConnection; PROCEDURE CloseConnection* ( conn: Connection ); BEGIN IF conn # NIL THEN conn.Close() END END CloseConnection; PROCEDURE Requested* ( conn: Connection ): BOOLEAN; BEGIN RETURN (conn.state = listening) & conn.Requested() END Requested; PROCEDURE Accept* ( conn: Connection; VAR newC: Connection; VAR res: SIGNED16 ); VAR r: SIGNED32; BEGIN conn.Accept( newC, r ); IF r = TCP.Ok THEN res := done ELSE res := error END END Accept; PROCEDURE State* (conn: Connection): SIGNED16; VAR res: SIGNED16; BEGIN CASE conn.state OF | TCP.Closed: res := closed | TCP.Listen: res := listening | TCP.Established: res := inout ELSE res := undef END; RETURN res END State; PROCEDURE GetPartner* (conn:Connection; VAR remIP: IPAdr; VAR remPort: UNSIGNED16); BEGIN remIP := conn.fip; remPort := SHORT( conn.fport ) END GetPartner; (*----- Read -----*) PROCEDURE Read* (conn: Connection; VAR ch: CHAR); VAR l: SIZE; r: INTEGER; bytes: ARRAY 1 OF CHAR; BEGIN conn.Receive( bytes, 0, 1, 1, l, r ); ch := bytes[0] END Read; PROCEDURE ReadBytes* ( conn: Connection; pos, len: SIGNED32; VAR buf: ARRAY OF S.BYTE ); VAR l: SIZE; r: INTEGER; BEGIN conn.Receive( S.VAL( Bytes, buf ), pos, len, len, l, r ); END ReadBytes; PROCEDURE ReadBool* (conn: Connection; VAR b: BOOLEAN); VAR l: SIZE; r: INTEGER; bytes: ARRAY 1 OF CHAR; BEGIN conn.Receive(bytes, 0, 1, 1, l, r ); b := ODD(ORD(bytes[0])); END ReadBool; PROCEDURE ReadInt* ( conn: Connection; VAR x: SIGNED16 ); VAR buf: ARRAY 4 OF CHAR; len: SIZE; res: INTEGER; BEGIN conn.Receive( buf, 0, 2, 2, len, res ); IF (res = 0) & (len = 2) THEN x := ORD(buf[0])*100H + ORD(buf[1]) ELSE x := 0 END END ReadInt; PROCEDURE ReadLInt* (conn: Connection; VAR x: SIGNED32); VAR buf: ARRAY 4 OF CHAR; len: SIZE; res: INTEGER; BEGIN conn.Receive( buf, 0, 4, 4, len, res ); IF (res = 0) & (len = 4) THEN x := ORD(buf[0])*1000000H + ORD(buf[1])*10000H + ORD(buf[2])*100H + ORD(buf[3]) ELSE x := 0 END END ReadLInt; (** Blocking read a string terminated by ( [CR]LF | 0X ). *) PROCEDURE ReadString* (conn: Connection; VAR s: ARRAY OF CHAR); VAR ch, ch0: CHAR; i, l: SIZE; r: INTEGER; bytes: ARRAY 1 OF CHAR; BEGIN i := -1; ch := 0X; REPEAT INC( i ); ch0 := ch; conn.Receive( bytes, 0, 1, 1, l, r ); ch := bytes[0]; s[i] := ch; UNTIL ( r # TCP.Ok) OR (ch = 0X) OR (ch = LF); IF (ch = LF) & (ch0 = CR) THEN s[i - 1] := 0X ELSE s [i] := 0X END END ReadString; (*----- Write -----*) PROCEDURE Write* (conn: Connection; ch: CHAR); VAR r: SIGNED32; bytes: ARRAY 1 OF CHAR; BEGIN bytes[0] := ch; conn.Send( bytes, 0, 1, TRUE, r ) END Write; PROCEDURE WriteBytes* (conn: Connection; pos, len: SIGNED32; CONST buf: ARRAY OF S.BYTE ); VAR r: SIGNED32; BEGIN conn.Send( S.VAL( Bytes, buf ), pos, len, TRUE, r) END WriteBytes; PROCEDURE WriteBool* (conn: Connection; b: BOOLEAN); VAR r: SIGNED32; bytes: ARRAY 1 OF CHAR; BEGIN IF b THEN bytes[0] := 1X ELSE bytes[0] := 0X END; conn.Send( bytes, 0, 1, TRUE, r ) END WriteBool; PROCEDURE WriteInt* (conn: Connection; x: SIGNED16); VAR buf: ARRAY 2 OF CHAR; r: SIGNED32; BEGIN buf[0] := CHR(x DIV 100H MOD 100H); buf[1] := CHR(x MOD 100H); conn.Send( buf, 0, 2, TRUE, r ) END WriteInt; PROCEDURE WriteLInt* (conn: Connection; x: SIGNED32); VAR buf: ARRAY 4 OF CHAR; r: SIGNED32; BEGIN buf[0] := CHR(x DIV 1000000H MOD 100H); buf[1] := CHR(x DIV 10000H MOD 100H); buf[2] := CHR(x DIV 100H MOD 100H); buf[3] := CHR(x MOD 100H); conn.Send( buf, 0, 4, TRUE, r ) END WriteLInt; PROCEDURE WriteString* (conn: Connection; CONST s: ARRAY OF CHAR); VAR cs: ARRAY 2 OF CHAR; i, r: SIGNED32; BEGIN i := 0; WHILE s[i] # 0X DO INC( i ) END; conn.Send( s, 0, i, FALSE, r); cs[0] := CR; cs[1] := LF; conn.Send( cs, 0, 2, TRUE, r ) END WriteString; (******************************** UDP **************************************) PROCEDURE OpenSocket* ( VAR soc: Socket; locPort: UNSIGNED16; VAR res: SIGNED16 ); VAR r: SIGNED32; BEGIN NEW( soc, locPort MOD 10000H, r ); IF r = UDP.Ok THEN res := done ELSE res := error END END OpenSocket; PROCEDURE CloseSocket* (S: Socket); BEGIN S.Close(); END CloseSocket; PROCEDURE AvailableDG* (soc: Socket): SIGNED32; BEGIN RETURN 0 END AvailableDG; PROCEDURE SendDG* ( soc: Socket; remIP: IPAdr; remport: UNSIGNED16; pos, len: SIGNED32; CONST buf: ARRAY OF S.BYTE ); VAR res: INTEGER; BEGIN soc.Send( remIP, remport MOD 10000H, S.VAL( Bytes, buf ), pos, len, res ) END SendDG; PROCEDURE ReceiveDG* ( soc: Socket; VAR remIP: IPAdr; VAR remport: UNSIGNED16; pos: SIGNED32; VAR len: SIGNED32; VAR buf: ARRAY OF S.BYTE ); VAR res: SIGNED32; port: SIGNED32; length: SIZE; BEGIN soc.Receive( S.VAL( Bytes, buf ), pos, len, 0, remIP, port, length, res ); len := length(SIGNED32); remport := SHORT( port) END ReceiveDG; (** Write 2 bytes in network byte ordering to buf[pos]. *) PROCEDURE PutInt* (VAR buf: ARRAY OF S.BYTE; pos: SIGNED16; x: SIGNED16); BEGIN buf[pos] := CHR(x DIV 100H MOD 100H); buf[pos+1] := CHR(x MOD 100H) END PutInt; (** Write 4 bytes in network byte ordering to buf[pos]. *) PROCEDURE PutLInt* (VAR buf: ARRAY OF S.BYTE; pos: SIGNED16; x: SIGNED32); BEGIN buf[pos] := CHR(x DIV 1000000H MOD 100H); buf[pos+1] := CHR(x DIV 10000H MOD 100H); buf[pos+2] := CHR(x DIV 100H MOD 100H); buf[pos+3] := CHR(x MOD 100H) END PutLInt; (** Read 2 bytes in network byte ordering from buf[pos]. *) PROCEDURE GetInt* (CONST buf: ARRAY OF S.BYTE; pos: SIGNED16; VAR x: SIGNED16); BEGIN x := ORD(buf[pos])*100H + ORD(buf[pos+1]) END GetInt; (** Read 4 bytes in network byte ordering from buf[pos]. *) PROCEDURE GetLInt* (CONST buf: ARRAY OF S.BYTE; pos: SIGNED16; VAR x: SIGNED32); BEGIN x := ORD( buf[pos] )*1000000H + ORD( buf[pos+1] )*10000H + ORD( buf[pos+2] )*100H + ORD( buf[pos+3] ) END GetLInt; PROCEDURE WriteAccess (service, user, password, host: ARRAY OF CHAR); BEGIN Texts.WriteString(W, service); Texts.Write(W, ":"); Texts.WriteString(W, user); Texts.Write(W, ":"); Texts.WriteString(W, password); Texts.Write(W, "@"); Texts.WriteString(W, host); Texts.WriteLn(W) (* ; Texts.Append(Oberon.Log, W.buf) *) END WriteAccess; PROCEDURE Show*; VAR p: Access; BEGIN p := accessList; Texts.WriteLn(W); Texts.WriteString(W, "NetSystem.Show"); Texts.WriteLn(W); WHILE p # NIL DO WriteAccess(p.service, p.user, "hidden password", p.host); p := p.next END; Texts.Append(Oberon.Log, W.buf) END Show; BEGIN verbose := FALSE; anyIP := IP.NilAdr; allIP := IP.NilAdr; allIP.usedProtocol := IP.IPv4; allIP.ipv4Adr := SIGNED32( 0FFFFFFFFH ); Texts.OpenWriter(W); accessList := NIL; Start END NetSystem.