Oberon/A2/Unix.Oberon.NetSystem.Mod

(* 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:&#47;/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&#91;0] >= "0") & (name&#91;0] &#60;= "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&#91;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&#91;i]) (* Skip leading whitespace. *) UNTIL R.eot OR ((R.lib IS Fonts.Font) & (" " &#60; accessString&#91;i])); WHILE ~R.eot & (R.lib IS Fonts.Font) & (" " &#60; accessString&#91;i]) & (accessString&#91;i] # "~") DO 			IF accessString&#91;i] = "@" THEN at := i END; INC(i); Texts.Read(R, accessString&#91;i]) END; accessString&#91;i] := 0X; RETURN 0 &#60; 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&#91;a] # 0X) & (a # at) & (accessString&#91;a] # ":") & (accessString&#91;a] > " ") & (accessString&#91;a] # "/") & (accessString&#91;a] # "~") (* & (R.lib IS Fonts.Font)*) DO 			str&#91;i] := accessString&#91;a]; INC(i); INC(a) END; str&#91;i] := 0X; RETURN 0 &#60; 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, ":&#47;/"); 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&#91;i] := ch; INC(i) END; Input.Read(ch) END; pwd&#91;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, "&#60;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 ":" &#91;"//"] user &#91;":" password] "@" host &#91; "/" ]. 		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&#91;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&#91;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&#91;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&#91;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&#91;c] = "@" *) END (* IF ~Next(accessString(accessString, pwd, c, at) *) 							END (* IF accessString&#91;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&#91;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&#91;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&#91;0])*100H + ORD(buf&#91;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&#91;0])*1000000H + ORD(buf&#91;1])*10000H + ORD(buf&#91;2])*100H + ORD(buf&#91;3]) ELSE x := 0 END END ReadLInt; (** Blocking read a string terminated by ( &#91;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&#91;0]; s&#91;i] := ch; UNTIL ( r # TCP.Ok) OR (ch = 0X) OR (ch = LF); IF (ch = LF) & (ch0 = CR) THEN s&#91;i - 1] := 0X ELSE s 			&#91;i] := 0X END END ReadString; (*- Write -*) PROCEDURE Write* (conn: Connection; ch: CHAR); VAR r: SIGNED32; bytes: ARRAY 1 OF CHAR; BEGIN bytes&#91;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&#91;0] := 1X ELSE bytes&#91;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&#91;0] := CHR(x DIV 100H MOD 100H); buf&#91;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&#91;0] := CHR(x DIV 1000000H MOD 100H); buf&#91;1] := CHR(x DIV 10000H MOD 100H); buf&#91;2] := CHR(x DIV 100H MOD 100H); buf&#91;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&#91;i] # 0X DO INC( i )  END; conn.Send( s, 0, i, FALSE, r); cs&#91;0] := CR; cs&#91;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&#91;pos]. *) PROCEDURE PutInt* (VAR buf: ARRAY OF S.BYTE; pos: SIGNED16; x: SIGNED16); BEGIN buf&#91;pos] := CHR(x DIV 100H MOD 100H); buf&#91;pos+1] := CHR(x MOD 100H) END PutInt; (** Write 4 bytes in network byte ordering to buf&#91;pos]. *) PROCEDURE PutLInt* (VAR buf: ARRAY OF S.BYTE; pos: SIGNED16; x: SIGNED32); BEGIN buf&#91;pos] := CHR(x DIV 1000000H MOD 100H); buf&#91;pos+1] := CHR(x DIV 10000H MOD 100H); buf&#91;pos+2] := CHR(x DIV 100H MOD 100H); buf&#91;pos+3] := CHR(x MOD 100H) END PutLInt; (** Read 2 bytes in network byte ordering from buf&#91;pos]. *) PROCEDURE GetInt* (CONST buf: ARRAY OF S.BYTE; pos: SIGNED16; VAR x: SIGNED16); BEGIN x := ORD(buf&#91;pos])*100H + ORD(buf&#91;pos+1]) END GetInt; (** Read 4 bytes in network byte ordering from buf&#91;pos]. *) PROCEDURE GetLInt* (CONST buf: ARRAY OF S.BYTE; pos: SIGNED16; VAR x: SIGNED32); BEGIN x := ORD( buf&#91;pos] )*1000000H + ORD( buf&#91;pos+1] )*10000H + ORD( buf&#91;pos+2] )*100H + ORD( buf&#91;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.