Bläddra i källkod

sync

git-svn-id: https://svn.inf.ethz.ch/svn/lecturers/a2/trunk@7608 8c9fc860-2736-0410-a75d-ab315db34111
eth.metacore 7 år sedan
förälder
incheckning
13001e68ab
4 ändrade filer med 1310 tillägg och 1324 borttagningar
  1. 2 1
      source/FSTools64.Mod
  2. 90 167
      source/Files64.Mod
  3. 1147 1101
      source/Streams64.Mod
  4. 71 55
      source/Win32.WinFiles64.Mod

+ 2 - 1
source/FSTools64.Mod

@@ -1125,7 +1125,8 @@ END FSTools64.
 
 SystemTools.Free FSTools64 ~
 
-FSTools64.DeleteFiles X:*.Bak ~
+FSTools64.DeleteFiles ./*.Bak ~
+FSTools64.DeleteFiles ../../source/*.Bak ~
 
 FSTools64.SplitFile BootManager.Bin 0200H BootManagerMBR.Bin BootManagerTail.Bin ~
 

+ 90 - 167
source/Files64.Mod

@@ -4,64 +4,64 @@ MODULE Files64;	(* pjm *)
 
 (** Aos file system base. *)
 
-IMPORT SYSTEM, Streams := Streams64, KernelLog, Modules, Kernel, Commands;
+IMPORT SYSTEM, Files, Streams := Streams64, KernelLog, Modules, Kernel, Commands;
 
 CONST
 	(** Volume & file flags *)
-	ReadOnly* = 0;
+	ReadOnly*			= Files.ReadOnly;
 
 	(** Volume flags *)
-	Removable* = 1;
-	Boot* = 2;
+	Removable*			= Files.Removable;
+	Boot*				= Files.Boot;
 
 	(** File flags *)
-	Directory* = 1;
-	Hidden* = 2;
-	System* = 3;
-	Archive* = 4;
-	Temporary* = 5;
-
-	Ok* = 0;
-
-	(* Volume level errors *)
-	ReadOnlyError = 2901;		(* Tried to modify read-only volume. Causes HALT *)
-	VolumeFull = 2902;			(* Tried to allocate block on full volume. Causes HALT *)
-	InvalidAdr= 2903;			(* Block address outside of volume. Causes HALT *)
+	Directory*			= Files.Directory;
+	Hidden*				= Files.Hidden;
+	System*				= Files.System;
+	Archive*			= Files.Archive;
+	Temporary*			= Files.Temporary;
 
+	Ok*					= Files.Ok;
+(*
+	(* Volume level errors *)(* as Files.Mod *)
+	ReadOnlyError		= 2901;		(* Tried to modify read-only volume. Causes HALT *)
+	VolumeFull			= 2902;			(* Tried to allocate block on full volume. Causes HALT *)
+	InvalidAdr			= 2903;			(* Block address outside of volume. Causes HALT *)
+*)
 	(* File level errors *)
-	VolumeReadOnly* = 2905; 	(** Cannot modify read-only volume *)
-	FsNotFound* = 2906; 		(** File system not found *)
-	FileAlreadyExists* = 2908;	(** File already exists *)
-	BadFileName* = 2909;		(** Bad file name *)
-	FileNotFound* = 2910;		(** File not found *)
+	VolumeReadOnly*		= Files.VolumeReadOnly; 	(** Cannot modify read-only volume *)
+	FsNotFound*			= Files.FsNotFound; 		(** File system not found *)
+	FileAlreadyExists*	= Files.FileAlreadyExists;	(** File already exists *)
+	BadFileName*		= Files.BadFileName;		(** Bad file name *)
+	FileNotFound*		= Files.FileNotFound;		(** File not found *)
 
-	EnumSize* = 0; EnumTime* = 1;	(** Enumerate flags. *)
+	(* Enumerate flags. *)
+	EnumSize*			= Files.EnumSize;
+	EnumTime*			= Files.EnumTime;
 
-	PrefixLength* = 16;	(** maximum length of a file system prefix. *)
-	NameLength* = 256;	(** maximum length of a file name. *)
+	PrefixLength*		= Files.PrefixLength;	(** maximum length of a file system prefix. *)
+	NameLength*			= Files.NameLength;	(** maximum length of a file name. *)
 
-	Trace = FALSE;
+	Trace				= FALSE;
 
-	WriteError = 2907;
+	WriteError			= 2907;
 
-	DefaultWriterSize = 4096;
-	DefaultReaderSize = 4096;
+	DefaultWriterSize	= 4096;
+	DefaultReaderSize	= 4096;
 
-	PathDelimiter* = "/";	(** Path delimiter *)
+	PathDelimiter*		= "/";	(** Path delimiter *)
 
-	BufferSize = 32*1024; 	(* Buffersize for file copy operation *)
+	BufferSize			= 32 * 1024; 	(* Buffersize for file copy operation *)
 
-	SetSize = MAX (SET) + 1;
+	SetSize				= MAX(SET) + 1;
 
 	(* file system behaviour flags *)
-	NeedsPrefix* = 0; (* if no prefix given, then this file system cannot handle it, to prevent file systems from being in the search path *)
+	NeedsPrefix*		= Files.NeedsPrefix; (* if no prefix given, then this file system cannot handle it, to prevent file systems from being in the search path *)
 
 TYPE
-	BESTSIZE* = Streams.BESTSIZE;
-
 (** All record fields are read-only for users, and read-write for extenders. *)
 
-	FileName* = ARRAY PrefixLength+NameLength OF CHAR;
+	FileName* = Files.FileName;
 
 		(** A rider points to some location in a file, where reading and writing will be done. *)
 	Rider* = RECORD	(** not shareable between multiple processes *)
@@ -69,7 +69,7 @@ TYPE
 		eof*: BOOLEAN;	(** has end of file been passed *)
 		res*: LONGINT;	(** leftover byte count for ReadBytes/WriteBytes *)
 			(** private fields for implementors *)
-		apos*: BESTSIZE;
+		apos*: HUGEINT;
 		bpos*: LONGINT;
 		hint*: Hint;
 		file*: File;
@@ -96,14 +96,14 @@ TYPE
 			RETURN TRUE;
 		END CanSetPos;
 
-		PROCEDURE SetPos*(pos : BESTSIZE);
+		PROCEDURE SetPos*(pos : HUGEINT);
 		BEGIN
 			file.Set(r, pos);
 			Reset;
 			received := pos; (* this effects that Streams.Reader.Pos() returns the correct location in the file *)
 		END SetPos;
 
-		PROCEDURE &InitFileReader*(file : File; pos: BESTSIZE);
+		PROCEDURE &InitFileReader*(file : File; pos: HUGEINT);
 		BEGIN
 			ASSERT(file # NIL);
 			SELF.file := file;
@@ -116,7 +116,7 @@ TYPE
 
 TYPE
 
-		(** Writer for buffered writing of a file via Streams.Write* procedures.  See OpenWriter. *)
+	(** Writer for buffered writing of a file via Streams.Write* procedures.  See OpenWriter. *)
 	Writer* = OBJECT (Streams.Writer)	(** not sharable between multiple processes *)
 	VAR
 		file : File;
@@ -134,20 +134,20 @@ TYPE
 			RETURN TRUE;
 		END CanSetPos;
 
-		PROCEDURE SetPos*(pos : BESTSIZE);
+		PROCEDURE SetPos*(pos : HUGEINT);
 			BEGIN
 			Update;
 			file.Set(r, pos);
 			Reset;
 		END SetPos;
 
-		PROCEDURE Pos*(): BESTSIZE;
+		PROCEDURE Pos*(): HUGEINT;
 		BEGIN
 			Update;
 			RETURN file.Pos(r)
 		END Pos;
 
-		PROCEDURE &InitFileWriter*(file: File; pos: BESTSIZE);
+		PROCEDURE &InitFileWriter*(file: File; pos: HUGEINT);
 		BEGIN
 			ASSERT(file # NIL);
 			SELF.file := file;
@@ -157,11 +157,8 @@ TYPE
 
 	END Writer;
 
-	Prefix* = ARRAY PrefixLength OF CHAR;
-
-	Address* = LONGINT;	(** Volume block address [1..size] *)
-
-	Hint* = POINTER TO RECORD END;	(** for use by file system implementors. *)
+	Prefix* = Files.Prefix;
+	Hint* = Files.Hint;	(** for use by file system implementors. *)
 
 	Bytes2 = ARRAY 2 OF CHAR;
 	Bytes4 = ARRAY 4 OF CHAR;
@@ -170,95 +167,7 @@ TYPE
 TYPE
 (** Volume is the base type of all volumes.  It provides operations on an abstract array of file system data blocks of blockSize bytes, numbered from 1 to size.  It is mainly used by file system implementations. *)
 
-	Volume* = OBJECT	(** shareable *)
-		VAR
-			size*: LONGINT;	(** size in blocks *)
-			blockSize*: LONGINT;	(** block size in bytes *)
-			flags*: SET;	(** ReadOnly, Removable, Boot *)
-			name*: ARRAY 32 OF CHAR;	(** descriptive name - e.g. for matching with Partitions.Show *)
-
-			map: POINTER TO ARRAY OF SET;	(* Block allocation table *)
-			used: LONGINT;	(* used blocks *)
-			reserved: LONGINT;	(* blocks reserved for system *)
-
-		PROCEDURE AllocBlock*(hint: Address; VAR adr: Address);
-		BEGIN {EXCLUSIVE}
-			IF ReadOnly IN flags THEN HALT(ReadOnlyError) END;
-			IF size - used <= reserved THEN HALT(VolumeFull) END;
-			ASSERT(hint >= 0);
-			IF hint > size THEN hint := 0 END;
-			adr := hint+1;
-			LOOP
-				IF adr > size THEN adr := 0 END;
-				IF (adr MOD SetSize) IN map[adr DIV SetSize] THEN
-					INC(adr) (* Block in use *)
-				ELSE
-					INCL(map[adr DIV SetSize], adr MOD SetSize);
-					EXIT
-				END;
-				IF adr = hint THEN HALT(VolumeFull) END
-			END;
-			INC(used)
-		END AllocBlock;
-
-		PROCEDURE FreeBlock*(adr: Address);
-		BEGIN {EXCLUSIVE}
-			IF (adr < 1) OR (adr > size) THEN HALT(InvalidAdr) END;
-			IF ReadOnly IN flags THEN HALT(ReadOnlyError) END;
-			EXCL(map[adr DIV SetSize], adr MOD SetSize);
-			DEC(used)
-		END FreeBlock;
-
-		PROCEDURE MarkBlock*(adr: Address);
-		BEGIN {EXCLUSIVE}
-			IF (adr < 1) OR (adr > size) THEN HALT(InvalidAdr) END;
-			IF ReadOnly IN flags THEN HALT(ReadOnlyError) END;
-			INCL(map[adr DIV SetSize], adr MOD SetSize);
-			INC(used)
-		END MarkBlock;
-
-		PROCEDURE Marked*(adr: Address): BOOLEAN;
-		BEGIN {EXCLUSIVE}
-			IF (adr < 1) OR (adr > size) THEN HALT(InvalidAdr) END;
-			IF ReadOnly IN flags THEN HALT(ReadOnlyError) END;
-			RETURN (adr MOD SetSize) IN map[adr DIV SetSize]
-		END Marked;
-
-		PROCEDURE Available*(): LONGINT;
-		BEGIN {EXCLUSIVE}
-			RETURN size - used
-		END Available;
-
-		PROCEDURE GetBlock*(adr: LONGINT; VAR blk: ARRAY OF CHAR);
-		BEGIN HALT(301) END GetBlock;	(* abstract *)
-
-		PROCEDURE PutBlock*(adr: LONGINT; VAR blk: ARRAY OF CHAR);
-		BEGIN HALT(301) END PutBlock;	(* abstract *)
-
-		(* FIX: This procedure can not be declared exclusive, because it will be overridden by an exclusive procedure in the actual implementation, from where it will be supercalled.  This could be a good example for allowing recursive locks, or an example of where an alternative for overriding methods is needed. In this case the procedure is only called from the exclusive overridden procedure, so it is not a real problem (although it is ugly). *)
-		PROCEDURE Finalize*;
-		BEGIN
-			map := NIL; size := 0; blockSize := 0
-		END Finalize;
-
-		(** Init procedure for private data of above methods only.  If the above methods are not required, this procedure should not be called, and the volume fields should be initialized directly.  The flags parameter defines the volume flags, the size parameter its size, and the reserved parameter says how many blocks are reserved for the system (out of disk space trap occurs when less than this amount of blocks are present). *)
-
-		PROCEDURE Init*(flags: SET; size, reserved: LONGINT);
-		VAR maplen: LONGINT;
-		BEGIN
-			SELF.flags := flags; SELF.size := size; SELF.reserved := reserved;
-			IF ~(ReadOnly IN flags) THEN
-				maplen := (size + SetSize) DIV SetSize;
-				NEW(map, maplen);
-				WHILE maplen > 0 DO DEC(maplen); map[maplen] := {} END;
-				INCL(map[0], 0);	(* reserve sector 0 (illegal to use) *)
-				used := 0
-			ELSE
-				used := size
-			END
-		END Init;
-
-	END Volume;
+	Volume* = Files.Volume;	(** shareable *)
 
 TYPE
 	FileSystem* = OBJECT	(** shareable *)
@@ -319,17 +228,17 @@ TYPE
 		BEGIN	(* see note in Volume.Finalize *)
 			vol := NIL
 		END Finalize;
-		
+
 		(* default implementation using an enumerator *)
 		PROCEDURE Has*(CONST name: ARRAY OF CHAR; VAR fullName: ARRAY OF CHAR; VAR flags: SET): BOOLEAN;
-		VAR enum: Enumerator; time, date: LONGINT; size: BESTSIZE;
+		VAR enum: Enumerator; time, date: LONGINT; size: HUGEINT;
 		BEGIN
 			NEW(enum);
 			enum.Open(name,{});
-			IF enum.HasMoreEntries() & enum.GetEntry(fullName, flags, time, date, size) THEN 
-				RETURN TRUE 
-			ELSE 
-				RETURN FALSE 
+			IF enum.HasMoreEntries() & enum.GetEntry(fullName, flags, time, date, size) THEN
+				RETURN TRUE
+			ELSE
+				RETURN FALSE
 			END;
 		END Has;
 
@@ -352,12 +261,12 @@ TYPE
 
 		(** Position a Rider at a certain position in a file. Multiple Riders can be positioned at different locations in a file. A Rider cannot be positioned beyond the end of a file. *)
 
-		PROCEDURE Set*(VAR r: Rider; pos: BESTSIZE);
+		PROCEDURE Set*(VAR r: Rider; pos: HUGEINT);
 		BEGIN HALT(301) END Set;	(* abstract *)
 
 		(** Return the offset of a Rider positioned on a file. *)
 
-		PROCEDURE Pos*(VAR r: Rider): BESTSIZE;
+		PROCEDURE Pos*(VAR r: Rider): HUGEINT;
 		BEGIN HALT(301) END Pos;	(* abstract *)
 
 		(** Read a byte from a file, advancing the Rider one byte further.  R.eof indicates if the end of the file has been passed. *)
@@ -382,7 +291,7 @@ TYPE
 
 		(** Return the current length of a file. *)
 
-		PROCEDURE Length*(): BESTSIZE;
+		PROCEDURE Length*(): HUGEINT;
 		BEGIN HALT(301) END Length;	(* abstract *)
 
 		(** Return the time (t) and date (d) when a file was last modified. *)
@@ -465,15 +374,14 @@ TYPE
 
 		(** Get one entry from the enumerator. *)
 
-		PROCEDURE GetEntry*(VAR name: ARRAY OF CHAR; VAR flags: SET; VAR time, date: LONGINT; size: BESTSIZE): BOOLEAN;
+		PROCEDURE GetEntry*(VAR name: ARRAY OF CHAR; VAR flags: SET; VAR time, date: LONGINT; size: HUGEINT): BOOLEAN;
 		VAR len, temp: LONGINT;
 		BEGIN
 			ReadNum(r, len);
 			IF ~r.eof THEN
 				name[len] := 0X;	(* index check *)
 				r.file.ReadBytes(r, name, 0, len);
-				ReadSet(r, flags); ReadNum(r, time); ReadNum(r, date);
-				ReadBESTSIZE(r, size);
+				ReadSet(r, flags); ReadNum(r, time); ReadNum(r, date); ReadHInt(r, size);
 				ASSERT(~r.eof)
 			END;
 			RETURN ~r.eof
@@ -489,14 +397,14 @@ TYPE
 
 		(** For internal use only. *)
 
-		PROCEDURE PutEntry*(VAR name: ARRAY OF CHAR; flags: SET; time, date: LONGINT; size:BESTSIZE);
+		PROCEDURE PutEntry*(VAR name: ARRAY OF CHAR; flags: SET; time, date: LONGINT; size:HUGEINT);
 		VAR len: LONGINT;
 		BEGIN
 			ASSERT(adding);
 			INC(SELF.size);
 			len := 0; WHILE name[len] # 0X DO INC(len) END;
 			WriteNum(r, len); r.file.WriteBytes(r, name, 0, len);
-			WriteSet(r, flags); WriteNum(r, time); WriteNum(r, date); WriteBESTSIZE(r, size)
+			WriteSet(r, flags); WriteNum(r, time); WriteNum(r, date); WriteHInt(r, size)
 		END PutEntry;
 
 	END Enumerator;
@@ -518,12 +426,8 @@ TYPE
 	END FileSearcher;
 
 TYPE
-		(** Parameters passed to volume and file system generator commands.  The str field contains a generic parameter string from the mount command.  The vol field returns the new volume from volume generators and passes the volume parameter to file system generators.  The prefix field contains the mount prefix, mainly for file system generators to add themselves with Files.Add. *)
-	Parameters* = OBJECT(Commands.Context)
-	VAR
-		vol*: Volume;	(** out parameter of volume generators and in parameter of file system generators. *)
-		prefix*: Prefix;
-	END Parameters;
+	(** Parameters passed to volume and file system generator commands.  The str field contains a generic parameter string from the mount command.  The vol field returns the new volume from volume generators and passes the volume parameter to file system generators.  The prefix field contains the mount prefix, mainly for file system generators to add themselves with Files.Add. *)
+	Parameters* = Files.Parameters;
 
 	FileSystemFactory* = PROCEDURE(context : Parameters);
 
@@ -537,7 +441,7 @@ VAR
 
 (** Open a reader on a file at the specified position. *)
 
-PROCEDURE OpenReader*(VAR b: Reader; f: File; pos: BESTSIZE);
+PROCEDURE OpenReader*(VAR b: Reader; f: File; pos: HUGEINT);
 BEGIN
 	IF b = NIL THEN
 		NEW(b, f, pos)
@@ -548,7 +452,7 @@ END OpenReader;
 
 (** Open a writer on a file at the specified position.  Remember to call Streams.Update before registering or closing the file! *)
 
-PROCEDURE OpenWriter*(VAR b: Writer; f: File; pos: BESTSIZE);
+PROCEDURE OpenWriter*(VAR b: Writer; f: File; pos: HUGEINT);
 BEGIN
 	NEW(b, f, pos)
 END OpenWriter;
@@ -938,7 +842,7 @@ VAR
 	sfs, dfs : FileSystem;
 	sfile, dfile : File;
 	buffer : ARRAY BufferSize OF CHAR;
-	i : BESTSIZE;
+	i : HUGEINT;
 BEGIN
 	SplitName(source, sprefix, sname);
 	SplitName(destination, dprefix, dname);
@@ -1119,14 +1023,33 @@ PROCEDURE AddSearchPath*(context: Commands.Context);
 VAR cmd: ARRAY 32 OF CHAR; msg: ARRAY 256 OF CHAR;  res: LONGINT;
 BEGIN
 	(* preliminary implementation until we know how to solve this generically *)
-	IF Modules.ModuleByName("WinFiles64") # NIL THEN 
+	IF Modules.ModuleByName("WinFiles64") # NIL THEN
 		cmd := "WinFiles64.AddSearchPath";
+	ELSIF Modules.ModuleByName("UnixFiles64") # NIL THEN
+		cmd := "UnixFiles64.AddSearchPath";
 	END;
-	IF cmd # "" THEN 
+	IF cmd # "" THEN
 		Commands.Activate(cmd, context, {Commands.Wait}, res, msg);
 		IF res # 0 THEN context.error.String(msg); context.error.Ln; END;
 	END
 END AddSearchPath;
+
+(* add a search path to the system *)
+PROCEDURE SetWorkPath*(context: Commands.Context);
+VAR cmd: ARRAY 32 OF CHAR; msg: ARRAY 256 OF CHAR;  res: LONGINT;
+BEGIN
+	(* preliminary implementation until we know how to solve this generically *)
+	IF Modules.ModuleByName("WinFiles64") # NIL THEN
+		cmd := "WinFiles64.SetWorkPath";
+	ELSIF Modules.ModuleByName("UnixFiles64") # NIL THEN
+		cmd := "UnixFiles64.SetWorkPath";
+	END;
+	IF cmd # "" THEN
+		Commands.Activate(cmd, context, {Commands.Wait}, res, msg);
+		IF res # 0 THEN context.error.String(msg); context.error.Ln; END;
+	END
+END SetWorkPath;
+
 (* Find an open file. *)
 
 PROCEDURE FindOpenFile(enum: FileSearcher; fs: FileSystem; key: LONGINT): File;
@@ -1153,10 +1076,10 @@ BEGIN
 	r.file.ReadBytes(r, SYSTEM.VAL(Bytes4, x), 0, 4)
 END ReadLInt;
 
-PROCEDURE ReadBESTSIZE*(VAR r: Rider; VAR x: BESTSIZE);
+PROCEDURE ReadHInt*(VAR r: Rider; VAR x: HUGEINT);
 BEGIN
-	r.file.ReadBytes(r, SYSTEM.VAL(Bytes8, x), 0, SIZEOF(BESTSIZE))
-END ReadBESTSIZE;
+	r.file.ReadBytes(r, SYSTEM.VAL(Bytes8, x), 0, SIZEOF(HUGEINT))
+END ReadHInt;
 
 PROCEDURE ReadSet*(VAR r: Rider; VAR x: SET);
 CONST Size = SIZEOF (SET);
@@ -1227,10 +1150,10 @@ BEGIN
 	r.file.WriteBytes(r, SYSTEM.VAL(Bytes4, x), 0, 4)
 END WriteLInt;
 
-PROCEDURE WriteBESTSIZE*(VAR r: Rider; x: BESTSIZE);(*TODO BESTSIZE *)
+PROCEDURE WriteHInt*(VAR r: Rider; x: HUGEINT);(*TODO HUGEINT *)
 BEGIN
-	r.file.WriteBytes(r, SYSTEM.VAL(Bytes8, x), 0, SIZEOF(BESTSIZE))
-END WriteBESTSIZE;
+	r.file.WriteBytes(r, SYSTEM.VAL(Bytes8, x), 0, SIZEOF(HUGEINT))
+END WriteHInt;
 
 PROCEDURE WriteSet*(VAR r: Rider; x: SET);
 CONST Size = SIZEOF (SET);

+ 1147 - 1101
source/Streams64.Mod

@@ -1,1101 +1,1147 @@
-(* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
-
-MODULE Streams64;   (** AUTHOR "pjm/be"; PURPOSE "I/O buffering and formatted writing and reading"; *)
-
-IMPORT SYSTEM;
-
-CONST
-	Ok* = 0;   (** zero result code means no error occurred *)
-	EOF* = 4201;   (** error returned when Receive reads past end of file or stream *)
-
-	EOT* = 1AX;   (** EOT character *)
-
-	StringFull = 4202;
-	FormatError* = 4203;   (** error returned when ReadInt fails *)
-
-	DefaultWriterSize* = 4096;
-	DefaultReaderSize* = 4096;
-
-CONST
-	CR = 0DX;  LF = 0AX;  TAB = 9X;  SP = 20X;
-
-VAR
-	H, L: INTEGER;
-TYPE
-	BESTSIZE* = HUGEINT;
-
-	(** Any stream output procedure or method. *)
-	Sender* = PROCEDURE {DELEGATE} ( CONST buf: ARRAY OF CHAR;  ofs, len: LONGINT;  propagate: BOOLEAN;  VAR res: LONGINT );
-
-	(** Any stream input procedure or method. *)
-	Receiver* = PROCEDURE {DELEGATE} ( VAR buf: ARRAY OF CHAR;  ofs, size, min: LONGINT;  VAR len, res: LONGINT );
-
-	Connection* = OBJECT
-
-		PROCEDURE Send*( CONST data: ARRAY OF CHAR;  ofs, len: LONGINT;  propagate: BOOLEAN;  VAR res: LONGINT );
-		END Send;
-
-		PROCEDURE Receive*( VAR data: ARRAY OF CHAR;  ofs, size, min: LONGINT;  VAR len, res: LONGINT );
-		END Receive;
-
-		PROCEDURE Close*;
-		END Close;
-
-	END Connection;
-
-	(** A writer buffers output before it is sent to a Sender.  Must not be shared between processes. *)
-TYPE
-	Writer* = OBJECT
-	VAR
-		tail: LONGINT;
-		buf: POINTER TO ARRAY OF CHAR;
-		res*: LONGINT; (** result of last output operation. *)
-		send: Sender;
-		sent*: BESTSIZE;  (** count of sent bytes *)
-		(* buf[0..tail-1] contains data to write. *)
-
-		PROCEDURE & InitWriter*( send: Sender;  size: LONGINT );
-		BEGIN
-			ASSERT ( send # NIL );
-			NEW( buf, size );  SELF.send := send;  Reset
-		END InitWriter;
-
-		PROCEDURE Reset*;
-		BEGIN
-			tail := 0;  res := Ok;  sent := 0
-		END Reset;
-
-		PROCEDURE CanSetPos*( ): BOOLEAN;
-		BEGIN
-			RETURN FALSE
-		END CanSetPos;
-
-		PROCEDURE SetPos*( pos: BESTSIZE );
-		BEGIN
-			HALT( 1234 )
-		END SetPos;
-
-		PROCEDURE Update*;
-		BEGIN
-			IF (res = Ok) THEN
-				send( buf^, 0, tail, TRUE , res );
-				IF res = Ok THEN INC( sent, tail );  tail := 0 END
-			END
-		END Update;
-
-	(** Current write position. *)
-		PROCEDURE Pos*( ): BESTSIZE;
-		BEGIN
-			RETURN sent + tail;
-		END Pos;
-
-		(** -- Write raw binary data -- *)
-
-	(** Write one byte. *)
-		PROCEDURE Char*( x: CHAR );
-		BEGIN
-			IF (tail = LEN( buf )) & (res = Ok) THEN
-				send( buf^, 0, tail, FALSE , res );
-				IF res = Ok THEN INC( sent, tail );  tail := 0 END
-			END;
-			IF res = Ok THEN buf[tail] := x;  INC( tail ) END
-		END Char;
-
-	(** Write len bytes from x, starting at ofs. *)
-		PROCEDURE Bytes*(CONST x: ARRAY OF CHAR;  ofs, len: LONGINT );
-		VAR n: LONGINT;
-		BEGIN
-			ASSERT ( len >= 0 );
-			LOOP
-				n := LEN( buf ) - tail;   (* space available *)
-				IF n = 0 THEN
-					IF res = Ok THEN  (* send current buffer *)
-						send( buf^, 0, tail, FALSE , res );
-						IF res = Ok THEN INC( sent, tail );  tail := 0 ELSE EXIT END
-					ELSE
-						EXIT  (* should not be writing on an erroneous rider *)
-					END;
-					n := LEN( buf )
-				END;
-				IF n > len THEN n := len END;
-				ASSERT ( tail + n <= LEN( buf ) );   (* index check *)
-				SYSTEM.MOVE( ADDRESSOF( x[ofs] ), ADDRESSOF( buf[tail] ), n );  INC( tail, n );
-				IF len = n THEN EXIT END;   (* done *)
-				INC( ofs, n );  DEC( len, n )
-			END
-		END Bytes;
-
-	(** Write a SHORTINT. *)
-		PROCEDURE RawSInt*( x: SHORTINT );
-		BEGIN
-			Char( SYSTEM.VAL( CHAR, x ) )
-		END RawSInt;
-
-	(** Write an INTEGER. *)
-		PROCEDURE RawInt*( x: INTEGER );
-		BEGIN
-			Bytes( SYSTEM.VAL( Bytes2, x ), 0, 2 )
-		END RawInt;
-
-	(** Write a LONGINT. *)
-		PROCEDURE RawLInt*( x: LONGINT );
-		BEGIN
-			Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4 )
-		END RawLInt;
-
-	(** Write a HUGEINT. *)
-		PROCEDURE RawHInt*( x: HUGEINT );
-		BEGIN
-			Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8 )
-		END RawHInt;
-
-	(** Write a 64 bit value in network byte order (most significant byte first) *)
-		PROCEDURE Net64*( x: HUGEINT );
-		BEGIN
-			Net32(LONGINT( x DIV 100000000H MOD 100000000H ));
-			Net32(LONGINT( x MOD 100000000H ));
-		END Net64;
-
-	(** Write a 32 bit value in network byte order (most significant byte first) *)
-		PROCEDURE Net32*( x: LONGINT );
-		BEGIN
-			Char( CHR( x DIV 1000000H MOD 100H ) );  Char( CHR( x DIV 10000H MOD 100H ) );  Char( CHR( x DIV 100H MOD 100H ) );
-			Char( CHR( x MOD 100H ) )
-		END Net32;
-
-	(** Write a 16 bit value in network byte order (most significant byte first) *)
-		PROCEDURE Net16*( x: LONGINT );
-		BEGIN
-			Char( CHR( x DIV 100H MOD 100H ) );  Char( CHR( x MOD 100H ) )
-		END Net16;
-
-	(** write unsigned byte *)
-		PROCEDURE Net8*( x: LONGINT );
-		BEGIN
-			Char( CHR( x MOD 100H ) )
-		END Net8;
-
-	(** Write a SET. *)
-		PROCEDURE RawSet*( x: SET );
-		BEGIN
-			Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4 )
-		END RawSet;
-
-	(** Write a BOOLEAN. *)
-		PROCEDURE RawBool*( x: BOOLEAN );
-		BEGIN
-			IF x THEN Char( 1X ) ELSE Char( 0X ) END
-		END RawBool;
-
-	(** Write a REAL. *)
-		PROCEDURE RawReal*( x: REAL );
-		BEGIN
-			Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4 )
-		END RawReal;
-
-	(** Write a LONGREAL. *)
-		PROCEDURE RawLReal*( x: LONGREAL );
-		BEGIN
-			Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8 )
-		END RawLReal;
-
-	(** Write a 0X-terminated string, including the 0X terminator. *)
-		PROCEDURE RawString*(CONST x: ARRAY OF CHAR );
-		VAR i: LONGINT;
-		BEGIN
-			i := 0;
-			WHILE x[i] # 0X DO Char( x[i] );  INC( i ) END;
-			Char( 0X )
-		END RawString;
-
-	(** Write a number in a compressed format. *)
-		PROCEDURE RawNum*( x: LONGINT );
-		BEGIN
-			WHILE (x < -64) OR (x > 63) DO Char( CHR( x MOD 128 + 128 ) );  x := x DIV 128 END;
-			Char( CHR( x MOD 128 ) )
-		END RawNum;
-
-		(** -- Write formatted data -- *)
-
-	(** Write an ASCII end-of-line (CR/LF). *)
-		PROCEDURE Ln*;
-		BEGIN
-			Char( CR );  Char( LF )
-		END Ln;
-
-	(** Write a 0X-terminated string, excluding the 0X terminator. *)
-		PROCEDURE String*(CONST x: ARRAY OF CHAR );
-		VAR i: LONGINT;
-		BEGIN
-			i := 0;
-			WHILE x[i] # 0X DO Char( x[i] );  INC( i ) END
-		END String;
-
-	(** Write an integer in decimal right-justified in a field of at least w characters. *)
-		PROCEDURE Int*( x: HUGEINT; w: LONGINT );
-		VAR i: SIZE; x0: HUGEINT;
-			a: ARRAY 21 OF CHAR;
-		BEGIN
-			IF x < 0 THEN
-				IF x = MIN( HUGEINT ) THEN
-					DEC( w, 20 );
-					WHILE w > 0 DO Char( " " );  DEC( w ) END;
-					String( "-9223372036854775808" );  RETURN
-				ELSE DEC( w );  x0 := -x
-				END
-			ELSE x0 := x
-			END;
-			i := 0;
-			REPEAT a[i] := CHR( x0 MOD 10 + 30H );  x0 := x0 DIV 10;  INC( i ) UNTIL x0 = 0;
-			WHILE w > i DO Char( " " );  DEC( w ) END;
-			IF x < 0 THEN Char( "-" ) END;
-			REPEAT DEC( i );  Char( a[i] ) UNTIL i = 0
-		END Int;
-
-	(** Write a SET in Oberon notation. *)
-	(*	PROCEDURE Set*( s: SET );   (* from P. Saladin *)
-		VAR i, last: LONGINT;  dots: BOOLEAN;
-		BEGIN
-			Char( "{" );  last := MIN( LONGINT );  dots := FALSE;
-			FOR i := MIN( SET ) TO MAX( SET ) DO
-				IF i IN s THEN
-					IF last = (i - 1) THEN
-						IF dots THEN String( ".." );  dots := FALSE END;
-						IF (i = MAX( SET )) OR ~((i + 1) IN s) THEN Int( i, 1 ) END
-					ELSE
-						IF last >= MIN( SET ) THEN String( ", " ) END;
-						Int( i, 1 );  dots := TRUE
-					END;
-					last := i
-				END
-			END;
-			Char( "}" )
-		END Set;	*)
-		
-		PROCEDURE Set*( s: SET );   (* from P. Saladin *)
-		VAR i, last: LONGINT;  dots: BOOLEAN;
-		BEGIN
-			Char( "{" );  last := MAX( LONGINT );  dots := FALSE;
-			FOR i := MAX( SET ) TO 0 BY -1 DO
-				IF i IN s THEN
-					IF last = (i + 1) THEN
-						IF dots THEN String( ".." );  dots := FALSE END;
-						IF (i = 0) OR ~((i - 1) IN s) THEN Int( i, 1 ) END
-					ELSE
-						IF last <= MAX( SET ) THEN String( ", " ) END;
-						Int( i, 1 );  dots := TRUE
-					END;
-					last := i
-				END
-			END;
-			Char( "}" )
-		END Set;
-
-		(**
-			Write an integer in hexadecimal right-justified in a field of at least ABS(w) characters.
-			If w < 0 THEN w least significant hex digits of x are written (potentially including leading zeros)
-		*)
-		PROCEDURE Hex*(x: HUGEINT; w: LONGINT);
-		VAR filler: CHAR; i,maxw: LONGINT; a: ARRAY 20 OF CHAR; y: HUGEINT;
-		BEGIN
-			IF w < 0 THEN filler := '0'; w := -w; maxw := w ELSE filler := ' '; maxw := 16 END;
-			i := 0;
-			REPEAT
-				y := x MOD 10H;
-				IF y < 10 THEN a[i] := CHR(y+ORD('0')) ELSE a[i] := CHR(y-10+ORD('A')) END;
-				x := x DIV 10H;
-				INC(i);
-			UNTIL (x=0) OR (i=maxw);
-			WHILE w > i DO Char(filler);  DEC( w ) END;
-			REPEAT DEC( i ); Char( a[i] ) UNTIL i = 0
-		END Hex;
-
-		(** Write "x" as a hexadecimal address. Do not use Hex because of arithmetic shift of the sign !*)
-		PROCEDURE Address* (x: ADDRESS);
-		BEGIN
-			Hex(x,-2*SIZEOF(ADDRESS));
-		END Address;
-
-		PROCEDURE Pair( ch: CHAR;  x: LONGINT );
-		BEGIN
-			IF ch # 0X THEN Char( ch ) END;
-			Char( CHR( ORD( "0" ) + x DIV 10 MOD 10 ) );  Char( CHR( ORD( "0" ) + x MOD 10 ) )
-		END Pair;
-
-	(** Write the date and time in ISO format (yyyy-mm-dd hh:mm:ss).  The t and d parameters are in Oberon time and date format.
-			If all parameters are within range, the output string is exactly 19 characters wide.  The t or d parameter can be -1, in which
-			case the time or date respectively are left out. *)
-		PROCEDURE Date*( t, d: LONGINT );
-		VAR ch: CHAR;
-		BEGIN
-			IF d # -1 THEN
-				Int( 1900 + d DIV 512, 4 );   (* year *)
-				Pair( "-", d DIV 32 MOD 16 );   (* month *)
-				Pair( "-", d MOD 32 );   (* day *)
-				ch := " " (* space between date and time *)
-			ELSE
-				ch := 0X (* no space before time *)
-			END;
-			IF t # -1 THEN
-				Pair( ch, t DIV 4096 MOD 32 );   (* hour *)
-				Pair( ":", t DIV 64 MOD 64 );   (* min *)
-				Pair( ":", t MOD 64 ) (* sec *)
-			END
-		END Date;
-
-	(** Write the date and time in RFC 822/1123 format without the optional day of the week (dd mmm yyyy hh:mm:ss SZZZZ) .
-			The t and d parameters are in Oberon time and date format.  The tz parameter specifies the time zone offset in minutes
-			(from -720 to 720 in steps of 30).  If all parameters are within range, the output string is exactly 26 characters wide.
-			The t, d or tz parameter can be -1, in which case the time, date or timezone respectively are left out. *)
-		PROCEDURE Date822*( t, d, tz: LONGINT );
-		VAR i, m: LONGINT;  ch: CHAR;
-		BEGIN
-			IF d # -1 THEN
-				Int( d MOD 32, 2 );   (* day *)
-				m := (d DIV 32 MOD 16 - 1) * 4;   (* month *)
-				FOR i := m TO m + 3 DO Char( months[i] ) END;
-				Int( 1900 + d DIV 512, 5 );   (* year *)
-				ch := " " (* space *)
-			ELSE
-				ch := 0X (* no space *)
-			END;
-			IF t # -1 THEN
-				Pair( ch, t DIV 4096 MOD 32 );   (* hour *)
-				Pair( ":", t DIV 64 MOD 64 );   (* min *)
-				Pair( ":", t MOD 64 );   (* sec *)
-				ch := " " (* space *)
-			ELSE
-				(* leave ch as before *)
-			END;
-			IF tz # -1 THEN
-				IF ch # 0X THEN Char( ch ) END;
-				IF tz >= 0 THEN Pair( "+", tz DIV 60 ) ELSE Pair( "-", (-tz) DIV 60 ) END;
-				Pair( 0X, ABS( tz ) MOD 60 )
-			END
-		END Date822;
-
-
-	(** Write LONGREAL x  using n character positions. *)
-		PROCEDURE Float*( x: LONGREAL;  n: LONGINT );
-		(* BM 1993.4.22. Do not simplify rounding! *)
-		VAR e, h, l, i: LONGINT;  z: LONGREAL;
-			d: ARRAY 16 OF CHAR;
-		BEGIN
-			e := ExpoL( x );
-			IF e = 2047 THEN
-				WHILE n > 5 DO Char( " " );  DEC( n ) END;
-				NaNCodeL( x, h, l );
-				IF (h # 0) OR (l # 0) THEN String( "      NaN" )
-				ELSIF x < 0 THEN String( "     -INF" )
-				ELSE String( "      INF" )
-				END
-			ELSE
-				IF n <= 9 THEN n := 1 ELSE DEC( n, 8 ) END;
-				REPEAT Char( " " );  DEC( n ) UNTIL n <= 15;   (* 0 <= n <= 15 fraction digits *)
-				IF (e # 0) & (x < 0) THEN Char( "-" );  x := -x ELSE Char( " " ) END;
-				IF e = 0 THEN
-					h := 0;  l := 0 (* no denormals *)
-				ELSE
-					e := (e - 1023) * 301029 DIV 1000000;   (* ln(2)/ln(10) = 0.301029996 *)
-					z := Ten( e + 1 );
-					IF x >= z THEN x := x / z;  INC( e ) ELSE x := x * Ten( -e ) END;
-					IF x >= 10 THEN x := x * Ten( -1 ) + 0.5D0 / Ten( n );  INC( e )
-					ELSE
-						x := x + 0.5D0 / Ten( n );
-						IF x >= 10 THEN x := x * Ten( -1 );  INC( e ) END
-					END;
-					x := x * Ten( 7 );  h := ENTIER( x );  x := (x - h) * Ten( 8 );  l := ENTIER( x )
-				END;
-				i := 15;
-				WHILE i > 7 DO d[i] := CHR( l MOD 10 + ORD( "0" ) );  l := l DIV 10;  DEC( i ) END;
-				WHILE i >= 0 DO d[i] := CHR( h MOD 10 + ORD( "0" ) );  h := h DIV 10;  DEC( i ) END;
-				Char( d[0] );  Char( "." );  i := 1;
-				WHILE i <= n DO Char( d[i] );  INC( i ) END;
-				IF e < 0 THEN String( "E-" );  e := -e ELSE String( "E+" ) END;
-				Char( CHR( e DIV 100 + ORD( "0" ) ) );  e := e MOD 100;  Char( CHR( e DIV 10 + ORD( "0" ) ) );  Char( CHR( e MOD 10 + ORD( "0" ) ) )
-			END
-		END Float;
-
-	(** Write LONGREAL x in a fixed point notation. n is the overall minimal length for the output field, f the number of fraction digits following the decimal point, D the fixed exponent (printed only when D # 0). *)
-		PROCEDURE FloatFix*( x: LONGREAL;  n, f, D: LONGINT );
-		(* BM 1993.4.22. Do not simplify rounding! / JG formatting adjusted *)
-		VAR e, h, l, i: LONGINT;  r, z: LONGREAL;
-			d: ARRAY 16 OF CHAR;
-			s: CHAR;  dot: BOOLEAN;
-		BEGIN
-			e := ExpoL( x );
-			IF (e = 2047) OR (ABS( D ) > 308) THEN
-				WHILE n > 5 DO Char( " " );  DEC( n ) END;
-				NaNCodeL( x, h, l );
-				IF (h # 0) OR (l # 0) THEN String( "      NaN" )
-				ELSIF x < 0 THEN String( "     -INF" )
-				ELSE String( "      INF" )
-				END
-			ELSE
-				IF D = 0 THEN IF (f=0) THEN dot := FALSE; DEC( n, 1 ) ELSE dot := TRUE; DEC(n,2);  END;  ELSE dot := TRUE; DEC( n, 7 ) END;
-				IF n < 2 THEN n := 2 END;
-				IF f < 0 THEN f := 0 END;
-				IF n < f + 2 THEN n := f + 2 END;
-				DEC( n, f );
-				IF (e # 0) & (x < 0) THEN s := "-";  x := -x ELSE s := " " END;
-				IF e = 0 THEN
-					h := 0;  l := 0;  DEC( e, D - 1 ) (* no denormals *)
-				ELSE
-					e := (e - 1023) * 301029 DIV 1000000;   (* ln(2)/ln(10) = 0.301029996 *)
-					z := Ten( e + 1 );
-					IF x >= z THEN x := x / z;  INC( e ) ELSE x := x * Ten( -e ) END;
-					DEC( e, D - 1 );  i := -(e + f);
-					IF i <= 0 THEN r := 5 * Ten( i ) ELSE r := 0 END;
-					IF x >= 10 THEN x := x * Ten( -1 ) + r;  INC( e )
-					ELSE
-						x := x + r;
-						IF x >= 10 THEN x := x * Ten( -1 );  INC( e ) END
-					END;
-					x := x * Ten( 7 );  h := ENTIER( x );  x := (x - h) * Ten( 8 );  l := ENTIER( x )
-				END;
-				i := 15;
-				WHILE i > 7 DO d[i] := CHR( l MOD 10 + ORD( "0" ) );  l := l DIV 10;  DEC( i ) END;
-				WHILE i >= 0 DO d[i] := CHR( h MOD 10 + ORD( "0" ) );  h := h DIV 10;  DEC( i ) END;
-				IF n <= e THEN n := e + 1 END;
-				IF e > 0 THEN
-					WHILE n > e DO Char( " " );  DEC( n ) END;
-					Char( s );  e := 0;
-					WHILE n > 0 DO
-						DEC( n );
-						IF e < 16 THEN Char( d[e] );  INC( e ) ELSE Char( "0" ) END
-					END;
-					IF dot THEN
-					Char( "." )
-					END;
-				ELSE
-					WHILE n > 1 DO Char( " " );  DEC( n ) END;
-					Char( s );  Char( "0" );  IF dot THEN Char( "." );  END;
-					WHILE (0 < f) & (e < 0) DO Char( "0" );  DEC( f );  INC( e ) END
-				END;
-				WHILE f > 0 DO
-					DEC( f );
-					IF e < 16 THEN Char( d[e] );  INC( e ) ELSE Char( "0" ) END
-				END;
-				IF D # 0 THEN
-					IF D < 0 THEN String( "E-" );  D := -D ELSE String( "E+" ) END;
-					Char( CHR( D DIV 100 + ORD( "0" ) ) );  D := D MOD 100;  Char( CHR( D DIV 10 + ORD( "0" ) ) );  Char( CHR( D MOD 10 + ORD( "0" ) ) )
-				END
-			END
-		END FloatFix;
-
-	END Writer;
-
-	(** A special writer that buffers output to be fetched by GetString or GetRawString. *)
-	StringWriter* = OBJECT (Writer)
-
-		PROCEDURE & InitStringWriter*( size: LONGINT );
-		BEGIN
-			InitWriter( Send, size )
-		END InitStringWriter;
-
-		PROCEDURE Send( CONST buf: ARRAY OF CHAR;  ofs, len: LONGINT;  propagate: BOOLEAN;  VAR res: LONGINT );
-		BEGIN
-			res := StringFull
-		END Send;
-
-		PROCEDURE CanSetPos*( ): BOOLEAN;
-		BEGIN
-			RETURN TRUE;
-		END CanSetPos;
-
-	(* Set the position for the writer *)
-		PROCEDURE SetPos*( pos: BESTSIZE );
-		BEGIN
-			IF pos > LEN( buf ) THEN pos := LEN( buf ) END;
-			tail := LONGINT( pos );  sent := 0;  res := Ok;
-		END SetPos;
-
-		PROCEDURE Update;
-		(* nothing to do *)
-		END Update;
-
-	(** Return the contents of the string writer (0X-terminated). *)
-		PROCEDURE Get*( VAR s: ARRAY OF CHAR );
-		VAR i, m: LONGINT;
-		BEGIN
-			m := LEN( s ) - 1;  i := 0;
-			WHILE (i # tail) & (i < m) DO s[i] := buf[i];  INC( i ) END;
-			s[i] := 0X;  tail := 0;  res := Ok
-		END Get;
-
-	(** Return the contents of the string writer (not 0X-terminated).  The len parameters returns the string length. *)
-		PROCEDURE GetRaw*( VAR s: ARRAY OF CHAR;  VAR len: LONGINT );
-		VAR i, m: LONGINT;
-		BEGIN
-			m := LEN( s );  i := 0;
-			WHILE (i # tail) & (i < m) DO s[i] := buf[i];  INC( i ) END;
-			len := i;  tail := 0;  res := Ok
-		END GetRaw;
-
-	END StringWriter;
-
-TYPE
-	(** A reader buffers input received from a Receiver.  Must not be shared between processes. *)
-	Reader* = OBJECT
-	VAR
-		head, tail: LONGINT;
-		buf: POINTER TO ARRAY OF CHAR;
-		res*: LONGINT;   (** result of last input operation. *)
-		receive: Receiver;
-		received*: BESTSIZE;   (** count of received bytes *)
-		(* buf[buf.head..buf.tail-1] contains data to read. *)
-
-		PROCEDURE & InitReader*( receive: Receiver;  size: LONGINT );
-		BEGIN
-			ASSERT ( receive # NIL );
-			IF (buf = NIL) OR (LEN(buf) # size) THEN
-				NEW( buf, size );
-			END;
-			SELF.receive := receive;  Reset
-		END InitReader;
-
-	(** reset the reader by dropping the bytes in the buffer, resetting the result code and setting received to 0.
-			This is used by seekable extensions of the reader *)
-		PROCEDURE Reset*;
-		BEGIN
-			head := 0;  tail := 0;  res := Ok;  received := 0
-		END Reset;
-
-		PROCEDURE CanSetPos*( ): BOOLEAN;
-		BEGIN
-			RETURN FALSE
-		END CanSetPos;
-
-		PROCEDURE SetPos*( pos: BESTSIZE );
-		BEGIN
-			HALT( 1234 )
-		END SetPos;
-
-	(** Return bytes currently available in input buffer. *)
-		PROCEDURE Available*( ): LONGINT;
-		VAR n: LONGINT;
-		BEGIN
-			IF (res = Ok) THEN
-				IF (head = tail) THEN head := 0;  receive( buf^, 0, LEN( buf ), 0, tail, res );  INC( received, tail );
-				ELSIF (tail # LEN( buf )) THEN
-					receive( buf^, tail, LEN( buf ) - tail, 0, n, res );   (* poll *)
-					INC( tail, n );  INC( received, n )
-				END;
-				IF res = EOF THEN res := Ok END  (* ignore EOF here *)
-			END;
-			RETURN tail - head
-		END Available;
-
-	(** Current read position. *)
-		PROCEDURE Pos*( ): BESTSIZE;
-		BEGIN
-			RETURN received - BESTSIZE(tail) - BESTSIZE(head)
-		END Pos;
-
-		(** -- Read raw binary data -- *)
-
-	(** Read one byte. x=0X if no success (e.g. file ended) *)
-		PROCEDURE Char*( VAR x: CHAR );
-		BEGIN
-			IF (head = tail) & (res = Ok) THEN head := 0;  receive( buf^, 0, LEN( buf ), 1, tail, res );  INC( received, tail ) END;
-			IF res = Ok THEN x := buf[head];  INC( head ) ELSE x := 0X END
-		END Char;
-
-	(** Like Read, but return result. Return 0X if no success (e.g. file ended) *)
-		PROCEDURE Get*( ): CHAR;
-		BEGIN
-			IF (head = tail) & (res = Ok) THEN head := 0;  receive( buf^, 0, LEN( buf ), 1, tail, res );  INC( received, tail ) END;
-			IF res = Ok THEN INC( head );  RETURN buf[head - 1] ELSE RETURN 0X END
-		END Get;
-
-	(** Like Get, but leave the byte in the input buffer. *)
-		PROCEDURE Peek*( ): CHAR;
-		BEGIN
-			IF (head = tail) & (res = Ok) THEN
-				head := 0;  receive( buf^, 0, LEN( buf ), 1, tail, res );  INC( received, tail );
-				IF res = EOF THEN  (* ignore EOF here *)
-					res := Ok;  tail := 0; RETURN 0X (* Peek returns 0X at eof *)
-				END
-			END;
-			IF res = Ok THEN RETURN buf[head] ELSE RETURN 0X END
-		END Peek;
-
-	(** Read size bytes into x, starting at ofs.  The len parameter returns the number of bytes that were actually read. *)
-		PROCEDURE Bytes*( VAR x: ARRAY OF CHAR;  ofs, size: LONGINT;  VAR len: LONGINT );
-		VAR n: LONGINT;
-		BEGIN
-			ASSERT ( size >= 0 );
-			len := 0;
-			LOOP
-				n := tail - head;   (* bytes available *)
-				IF n = 0 THEN  (* no data available *)
-					head := 0;
-					IF res = Ok THEN  (* fill buffer *)
-						receive( buf^, 0, LEN( buf ), 1, tail, res );  INC( received, tail )
-					END;
-					IF res # Ok THEN  (* should not be reading from erroneous rider *)
-						WHILE size # 0 DO x[ofs] := 0X;  INC( ofs );  DEC( size ) END;   (* clear rest of buffer *)
-						IF (res = EOF) & (len # 0) THEN res := Ok END;   (* ignore EOF if some data being returned *)
-						EXIT
-					END;
-					n := tail
-				END;
-				IF n > size THEN n := size END;
-				ASSERT ( ofs + n <= LEN( x ) );   (* index check *)
-				SYSTEM.MOVE( ADDRESSOF( buf[head] ), ADDRESSOF( x[ofs] ), n );  INC( head, n );  INC( len, n );
-				IF size = n THEN EXIT END;   (* done *)
-				INC( ofs, n );  DEC( size, n )
-			END
-		END Bytes;
-
-	(** Skip n bytes on the reader. *)
-		PROCEDURE SkipBytes*( n: LONGINT );
-		VAR ch: CHAR;
-		BEGIN
-			WHILE n > 0 DO ch := Get();  DEC( n ) END
-		END SkipBytes;
-
-	(** Read a SHORTINT. *)
-		PROCEDURE RawSInt*( VAR x: SHORTINT );
-		BEGIN
-			x := SYSTEM.VAL( SHORTINT, Get() )
-		END RawSInt;
-
-	(** Read an INTEGER. *)
-		PROCEDURE RawInt*( VAR x: INTEGER );
-		VAR x0, x1: CHAR;
-		BEGIN
-			x0 := Get();  x1 := Get();   (* defined order *)
-			x := ORD( x1 ) * 100H + ORD( x0 )
-		END RawInt;
-
-	(** Read a LONGINT. *)
-		PROCEDURE RawLInt*( VAR x: LONGINT );
-		VAR ignore: LONGINT;
-		BEGIN
-			Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
-		END RawLInt;
-
-	(** Read a HUGEINT. *)
-		PROCEDURE RawHInt*( VAR x: HUGEINT );
-		VAR ignore: LONGINT;
-		BEGIN
-			Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8, ignore )
-		END RawHInt;
-
-		(** Read a 64 bit value in network byte order (most significant byte first) *)
-		PROCEDURE Net64*( ): HUGEINT;
-		BEGIN
-			RETURN Net32() * 100000000H + Net32()
-		END Net64;
-
-	(** Read a 32 bit value in network byte order (most significant byte first) *)
-		PROCEDURE Net32*( ): LONGINT;
-		BEGIN
-			RETURN LONG( ORD( Get() ) ) * 1000000H + LONG( ORD( Get() ) ) * 10000H + LONG( ORD( Get() ) ) * 100H + LONG( ORD( Get() ) )
-		END Net32;
-
-	(** Read an unsigned 16bit value in network byte order (most significant byte first) *)
-		PROCEDURE Net16*( ): LONGINT;
-		BEGIN
-			RETURN LONG( ORD( Get() ) ) * 100H + LONG( ORD( Get() ) )
-		END Net16;
-
-	(** Read an unsigned byte *)
-		PROCEDURE Net8*( ): LONGINT;
-		BEGIN
-			RETURN LONG( ORD( Get() ) )
-		END Net8;
-
-	(** Read a SET. *)
-		PROCEDURE RawSet*( VAR x: SET );
-		VAR ignore: LONGINT;
-		BEGIN
-			Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
-		END RawSet;
-
-	(** Read a BOOLEAN. *)
-		PROCEDURE RawBool*( VAR x: BOOLEAN );
-		BEGIN
-			x := (Get() # 0X)
-		END RawBool;
-
-	(** Read a REAL. *)
-		PROCEDURE RawReal*( VAR x: REAL );
-		VAR ignore: LONGINT;
-		BEGIN
-			Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
-		END RawReal;
-
-	(** Read a LONGREAL. *)
-		PROCEDURE RawLReal*( VAR x: LONGREAL );
-		VAR ignore: LONGINT;
-		BEGIN
-			Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8, ignore )
-		END RawLReal;
-
-	(** Read a 0X-terminated string.  If the input string is larger than x, read the full string and assign the truncated 0X-terminated value to x. *)
-		PROCEDURE RawString*( VAR x: ARRAY OF CHAR );
-		VAR i, m: LONGINT;  ch: CHAR;
-		BEGIN
-			i := 0;  m := LEN( x ) - 1;
-			LOOP
-				ch := Get();   (* also returns 0X on error *)
-				IF ch = 0X THEN EXIT END;
-				IF i < m THEN x[i] := ch;  INC( i ) END
-			END;
-			x[i] := 0X
-		END RawString;
-
-	(** Read a number in a compressed format. *)
-		PROCEDURE RawNum*( VAR x: LONGINT );
-		VAR ch: CHAR;  n, y: LONGINT;
-		BEGIN
-			n := 0;  y := 0;  ch := Get();
-			WHILE ch >= 80X DO INC( y, LSH( LONG( ORD( ch ) ) - 128, n ) );  INC( n, 7 );  ch := Get() END;
-			x := ASH( LSH( LONG( ORD( ch ) ), 25 ), n - 25 ) + y
-		END RawNum;
-
-		(** -- Read formatted data (uses Peek for one character lookahead) -- *)
-
-	 (** Read an integer value in decimal or hexadecimal.  If hex = TRUE, recognize the "H" postfix for hexadecimal numbers. *)
-
-		PROCEDURE Int*( VAR x: LONGINT;  hex: BOOLEAN );
-		VAR vd, vh, sgn, d: LONGINT;  ch: CHAR;  ok: BOOLEAN;
-		BEGIN
-			vd := 0;  vh := 0;  sgn := 1;  ok := FALSE;
-			IF Peek() = "-" THEN sgn := -1;  ch := Get() END;
-			LOOP
-				ch := Peek();
-				IF (ch >= "0") & (ch <= "9") THEN d := ORD( ch ) - ORD( "0" )
-				ELSIF hex & (CAP( ch ) >= "A") & (CAP( ch ) <= "F") THEN d := ORD( CAP( ch ) ) - ORD( "A" ) + 10
-				ELSE EXIT
-				END;
-				vd := 10 * vd + d;  vh := 16 * vh + d;   (* ignore overflow *)
-				ch := Get();  ok := TRUE
-			END;
-			IF hex & (CAP( ch ) = "H") THEN  (* optional "H" present *)
-				vd := vh;   (* use the hex value *)
-				ch := Get()
-			END;
-			x := sgn * vd;
-			IF (res = 0) & ~ok THEN res := FormatError END
-		END Int;
-
-	(** Return TRUE iff at the end of a line (or file). *)
-		PROCEDURE EOLN*( ): BOOLEAN;
-		VAR ch: CHAR;
-		BEGIN
-			ch := Peek();  RETURN (ch = CR) OR (ch = LF) OR (res # Ok)
-		END EOLN;
-
-	(** Read all characters until the end of the line (inclusive).  If the input string is larger than x, read the full string and assign
-			the truncated 0X-terminated value to x. *)
-		PROCEDURE Ln*( VAR x: ARRAY OF CHAR );
-		VAR i, m: LONGINT;  ch: CHAR;
-		BEGIN
-			i := 0;  m := LEN( x ) - 1;
-			LOOP
-				ch := Peek();
-				IF (ch = CR) OR (ch = LF) OR (res # Ok) THEN EXIT END;
-				IF i < m THEN x[i] := ch;  INC( i ) END;
-				ch := Get()
-			END;
-			x[i] := 0X;
-			IF ch = CR THEN ch := Get() END;
-			IF Peek() = LF THEN ch := Get() END
-		END Ln;
-
-	(** Read all characters until the end of the line (inclusive) or an <EOT> character.
-			If the input string is larger than x, read the full string and assign the truncated 0X-terminated
-			value to x. *)
-		PROCEDURE LnEOT*( VAR x: ARRAY OF CHAR );
-		VAR i, m: LONGINT;  ch: CHAR;
-		BEGIN
-			i := 0;  m := LEN( x ) - 1;
-			LOOP
-				ch := Peek();
-				IF (ch = CR) OR (ch = LF) OR (ch = EOT) OR (res # Ok) THEN EXIT END;
-				IF i < m THEN x[i] := ch;  INC( i ) END;
-				ch := Get()
-			END;
-			x[i] := 0X;
-			IF ch = CR THEN ch := Get() END;
-			IF Peek() = LF THEN ch := Get() END;
-			IF ch = EOT THEN ch := Get() END
-		END LnEOT;
-
-	(** Skip over all characters until the end of the line (inclusive). *)
-		PROCEDURE SkipLn*;
-		VAR ch: CHAR;
-		BEGIN
-			LOOP
-				ch := Peek();
-				IF (ch = CR) OR (ch = LF) OR (res # Ok) THEN EXIT END;
-				ch := Get()
-			END;
-			IF ch = CR THEN ch := Get() END;
-			IF Peek() = LF THEN ch := Get() END
-		END SkipLn;
-
-	(** Skip over space and TAB characters. *)
-		PROCEDURE SkipSpaces*;
-		VAR ch: CHAR;
-		BEGIN
-			LOOP
-				ch := Peek();
-				IF (ch # TAB) & (ch # SP) THEN EXIT END;
-				ch := Get()
-			END
-		END SkipSpaces;
-
-	(** Skip over space, TAB and EOLN characters. *)
-		PROCEDURE SkipWhitespace*;
-		VAR ch: CHAR;
-		BEGIN
-			LOOP
-				ch := Peek();
-				IF (ch # SP) & (ch # CR) & (ch # LF) & (ch # TAB) THEN EXIT END;
-				ch := Get()
-			END
-		END SkipWhitespace;
-
-	(** Read a token, consisting of any string of characters terminated by space, TAB or EOLN. *)
-		PROCEDURE Token*( VAR token: ARRAY OF CHAR );
-		VAR j, max: LONGINT;  ch: CHAR;
-		BEGIN
-			j := 0;  max := LEN( token ) - 1;
-			LOOP
-				ch := Peek();
-				IF (ch = SP) OR (ch = CR) OR (ch = LF) OR (ch = TAB) OR (res # Ok) THEN EXIT END;
-				IF j < max THEN token[j] := ch;  INC( j ) END;
-				ch := Get()
-			END;
-			token[j] := 0X
-		END Token;
-
-	(** Read an optionally "" or '' enquoted string.  Will not read past the end of a line. *)
-		PROCEDURE String*( VAR string: ARRAY OF CHAR );
-		VAR c, delimiter: CHAR;  i, len: LONGINT;
-		BEGIN
-			c := Peek();
-			IF (c # "'") & (c # '"') THEN Token( string )
-			ELSE
-				delimiter := Get();  c := Peek();  i := 0;  len := LEN( string ) - 1;
-				WHILE (i < len) & (c # delimiter) & (c # CR) & (c # LF) & (res = Ok) DO string[i] := Get();  INC( i );  c := Peek() END;
-				IF (c = delimiter) THEN c := Get() END;
-				string[i] := 0X
-			END
-		END String;
-
-		(** First skip whitespace, then read string *)
-		PROCEDURE GetString*(VAR string : ARRAY OF CHAR): BOOLEAN;
-		VAR c: CHAR;
-		BEGIN
-			SkipWhitespace;
-			c := Peek();
-			String(string);
-			RETURN (string[0] # 0X) OR (c = "'") OR (c = '"');
-		END GetString;
-
-		(** First skip whitespace, then read integer *)
-		PROCEDURE GetInteger*(VAR integer : LONGINT; isHexadecimal : BOOLEAN): BOOLEAN;
-		BEGIN
-			SkipWhitespace;
-			Int(integer, isHexadecimal);
-			RETURN res = Ok;
-		END GetInteger;
-
-		(** First skip whitespace, then read 1 byte character *)
-		PROCEDURE GetChar*(VAR ch : CHAR): BOOLEAN;
-		BEGIN
-			SkipWhitespace;
-			Char(ch);
-			RETURN ch # 0X;
-		END GetChar;
-
-	END Reader;
-
-TYPE
-	(** A special reader that buffers input set by SetString or SetRawString. *)
-	StringReader* = OBJECT (Reader)
-
-		PROCEDURE & InitStringReader*( size: LONGINT );
-		BEGIN
-			InitReader( Receive, size )
-		END InitStringReader;
-
-		PROCEDURE CanSetPos*( ): BOOLEAN;
-		BEGIN
-			RETURN TRUE
-		END CanSetPos;
-
-	(** Set the reader position *)
-		PROCEDURE SetPos*( pos: BESTSIZE );
-		BEGIN
-			IF pos > LEN( buf ) THEN pos := LEN( buf ) END;
-			head := LONGINT( pos );  tail := LEN( buf );  received := LEN( buf );  res := Ok;
-		END SetPos;
-
-		PROCEDURE Receive( VAR buf: ARRAY OF CHAR;  ofs, size, min: LONGINT;  VAR len, res: LONGINT );
-		BEGIN
-			IF min = 0 THEN res := Ok ELSE res := EOF END;
-			len := 0;
-		END Receive;
-
-	(** Set the contents of the string buffer.  The s parameter is a 0X-terminated string. *)
-		PROCEDURE Set*(CONST  s: ARRAY OF CHAR );
-		VAR len: LONGINT;
-		BEGIN
-			len := 0;
-			WHILE s[len] # 0X DO INC( len ) END;
-			IF len > LEN( buf ) THEN len := LEN( buf ) END;
-			head := 0;  tail := len;  received := len;  res := Ok;
-			IF len > 0 THEN
-				SYSTEM.MOVE( ADDRESSOF( s[0] ), ADDRESSOF( buf[0] ), len )
-			END;
-		END Set;
-
-	(** Set the contents of the string buffer.  The len parameter specifies the size of the buffer s. *)
-		PROCEDURE SetRaw*(CONST s: ARRAY OF CHAR;  ofs, len: LONGINT );
-		BEGIN
-			IF len > LEN( buf ) THEN len := LEN( buf ) END;
-			head := 0;  tail := len;  received := len;  res := Ok;
-			ASSERT ( (len >= 0) & (ofs + len <= LEN( s )) );   (* index check *)
-			IF len > 0 THEN
-				SYSTEM.MOVE( ADDRESSOF( s[ofs] ), ADDRESSOF( buf[0] ), len )
-			END;
-		END SetRaw;
-
-	END StringReader;
-
-	Bytes2 = ARRAY 2 OF CHAR;
-	Bytes4 = ARRAY 4 OF CHAR;
-	Bytes8 = ARRAY 8 OF CHAR;
-
-VAR
-	months: ARRAY 12 * 4 + 1 OF CHAR;
-
-
-	(** Open a writer to the specified stream sender.  Update must be called after writing to ensure the buffer is written to the stream. *)
-	PROCEDURE OpenWriter*( VAR b: Writer;  send: Sender );
-	BEGIN
-		NEW( b, send, DefaultWriterSize )
-	END OpenWriter;
-
-(** Open a reader from the specified stream receiver. *)
-	PROCEDURE OpenReader*( VAR b: Reader;  receive: Receiver );
-	BEGIN
-		NEW( b, receive, DefaultReaderSize )
-	END OpenReader;
-
-(** Copy the contents of a reader to a writer *)
-	PROCEDURE Copy* (r: Reader; w: Writer);
-	VAR char: CHAR;
-	BEGIN
-		WHILE r.res = Ok DO
-			r.Char (char);
-			IF r.res = Ok THEN w.Char (char) END
-		END;
-	END Copy;
-
-	(** from module Reals.Mod *)
-
-
-(*** the following procedures stem from Reals.Mod and are needed for Writer.Float and Writer.FloatFix *)
-
-(** Returns the NaN code (0 <= h < 1048576, MIN(LONGINT) <= l <= MAX(LONGINT)) or (-1,-1) if not NaN/Infinite. *)
-	PROCEDURE NaNCodeL( x: LONGREAL;  VAR h, l: LONGINT );
-	BEGIN
-		SYSTEM.GET( ADDRESSOF( x ) + H, h );  SYSTEM.GET( ADDRESSOF( x ) + L, l );
-		IF ASH( h, -20 ) MOD 2048 = 2047 THEN  (* Infinite or NaN *)
-			h := h MOD 100000H (* lowest 20 bits *)
-		ELSE h := -1;  l := -1
-		END
-	END NaNCodeL;
-
-(** Returns the shifted binary exponent (0 <= e < 2048). *)
-	PROCEDURE ExpoL( x: LONGREAL ): LONGINT;
-	VAR i: LONGINT;
-	BEGIN
-		SYSTEM.GET( ADDRESSOF( x ) + H, i );  RETURN ASH( i, -20 ) MOD 2048
-	END ExpoL;
-
-(** Convert hexadecimal to LONGREAL. h and l are the high and low parts.*)
-	PROCEDURE RealL( h, l: LONGINT ): LONGREAL;
-	VAR x: LONGREAL;
-	BEGIN
-		SYSTEM.PUT( ADDRESSOF( x ) + H, h );  SYSTEM.PUT( ADDRESSOF( x ) + L, l );  RETURN x
-	END RealL;
-
-(** Returns 10^e (e <= 308, 308 < e delivers IEEE-code +INF). *)
-	PROCEDURE Ten( e: LONGINT ): LONGREAL;   (* naiive version *)
-	VAR r: LONGREAL;
-	BEGIN
-		IF e < -307 THEN RETURN 0
-		ELSIF 308 < e THEN RETURN RealL( 2146435072, 0 )
-		END;
-		r := 1;
-		WHILE (e > 0) DO r := r * 10;  DEC( e );  END;
-		WHILE (e < 0) DO r := r / 10;  INC( e );  END;
-		RETURN r;
-	END Ten;
-
-	PROCEDURE InitHL;
-	VAR i: ADDRESS;  dmy: INTEGER;  littleEndian: BOOLEAN;
-	BEGIN
-		dmy := 1;  i := ADDRESSOF( dmy );
-		SYSTEM.GET( i, littleEndian );   (* indirection via i avoids warning on SUN cc -O *)
-		IF littleEndian THEN H := 4;  L := 0 ELSE H := 0;  L := 4 END
-	END InitHL;
-
-
-BEGIN
-	months := " Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec";  InitHL;
-END Streams64.
-
-(**
-Notes:
-o	Any single buffer instance must not be accessed by more than one process concurrently.
-o 	The interface is blocking (synchronous).  If an output buffer is full, it is written with a synchronous write, which returns
-	only when all the data has been written.   If an input buffer is empty, it is read with a synchronous read, which only returns
-	once some data has been read.  The only exception is the Available() procedure, which "peeks" at the input stream
-	and returns 0 if no data is currently available.
-o 	All procedures set res to the error code reported by the lower-level I/O operation (non-zero indicates error).
-	 E.g. closing an underlying TCP connection will result in the Read* procedures returning a non-zero error code.
-o 	res is sticky.  Once it becomes non-zero, it remains non-zero.
-o 	The only way to detect end of file is to attempt to read past the end of file, which returns a non-zero error code.
-o 	All output written to an erroneous buffer is ignored.
-o 	The value returned when reading from an erroneous buffer is undefined, except for the Read procedure, which returns 0X.
-o 	ReadBytes sets the len parameter to the number of bytes that were actually read, e.g. if size = 10, and only 8 bytes are read, len is 8.
-o 	Raw format is little-endian 2's complement integers, IEEE reals and 0X-terminated strings.
-o 	Syntax for ReadInt with hex = FALSE: num = ["-"] digit {digit}. digit = "0".."9".
-o 	Syntax for ReadInt with hex = TRUE: ["-"] hexdigit {hexdigit} ["H"|"h"]. hexdigit = digit | "A".."F" | "a".."f".
-o 	ReadInt with hex = TRUE allows "A".."F" as digits, and looks for a "H" character after the number.
-	If present, the number is interpreted as hexadecimal.  If hexadecimal digits are present, but no "H" flag,
-	the resulting decimal value is undefined.
-o 	ReadInt ignores overflow.
-o 	A Sender sends len bytes from buf at ofs to output and returns res non-zero on error.  It waits until all the data is written,
-	or an error occurs.
-o 	A Receiver receives up to size bytes from input into buf at ofs and returns the number of bytes read in len.
-	It returns res non-zero on error.  It waits until at least min bytes (possibly zero) are available, or an error occurs.
-o 	EOLN and ReadLn recognize the following end-of-line characters: CR, LF and CR/LF.
-o 	To read an unstructured file token-by-token: WHILE (r.res = 0) DO SkipWhitespace; ReadToken END
-o 	To read a line structured file token-by-token: WHILE r.res = 0 DO SkipSpaces; WHILE ~EOLN DO ReadToken; SkipSpaces END END
-o 	A string writer is not flushed when it becomes full, but res is set to a non-zero value.
-o 	Update has no effect on a string writer.
-o 	GetString can be called on a string writer to return the buffer contents and reset it to empty.
-o 	GetString always appends a 0X character to the buffer, but returns the true length (excluding the added 0X) in the len parameter,
-	so it can also be used for binary data that includes 0X characters.
-o 	Receive procedure should set res to EOF when attempting to read past the end of file.
-*)
-
-
-(*
-to do:
-o stream byte count
-o read formatted data
-o reads for all formatted writes
-o write reals
-o low-level version that can be used in kernel (below KernelLog)
-*)
+(* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
+
+MODULE Streams64;   (** AUTHOR "pjm/be"; PURPOSE "I/O buffering and formatted writing and reading"; *)
+
+IMPORT SYSTEM;
+
+CONST
+	Ok* = 0;   (** zero result code means no error occurred *)
+	EOF* = 4201;   (** error returned when Receive reads past end of file or stream *)
+
+	EOT* = 1AX;   (** EOT character *)
+
+	StringFull = 4202;
+	FormatError* = 4203;   (** error returned when ReadInt fails *)
+
+	DefaultWriterSize* = 4096;
+	DefaultReaderSize* = 4096;
+
+CONST
+	CR = 0DX;  LF = 0AX;  TAB = 9X;  SP = 20X;
+
+VAR
+	H, L: INTEGER;
+TYPE
+	(** Any stream output procedure or method. *)
+	Sender* = PROCEDURE {DELEGATE} ( CONST buf: ARRAY OF CHAR;  ofs, len: LONGINT;  propagate: BOOLEAN;  VAR res: LONGINT );
+
+	(** Any stream input procedure or method. *)
+	Receiver* = PROCEDURE {DELEGATE} ( VAR buf: ARRAY OF CHAR;  ofs, size, min: LONGINT;  VAR len, res: LONGINT );
+
+	Connection* = OBJECT
+
+		PROCEDURE Send*( CONST data: ARRAY OF CHAR;  ofs, len: LONGINT;  propagate: BOOLEAN;  VAR res: LONGINT );
+		END Send;
+
+		PROCEDURE Receive*( VAR data: ARRAY OF CHAR;  ofs, size, min: LONGINT;  VAR len, res: LONGINT );
+		END Receive;
+
+		PROCEDURE Close*;
+		END Close;
+
+	END Connection;
+
+	(** A writer buffers output before it is sent to a Sender.  Must not be shared between processes. *)
+TYPE
+	Writer* = OBJECT
+	VAR
+		tail: LONGINT;
+		buf: POINTER TO ARRAY OF CHAR;
+		res*: LONGINT; (** result of last output operation. *)
+		send: Sender;
+		sent*: HUGEINT;  (** count of sent bytes *)
+		(* buf[0..tail-1] contains data to write. *)
+
+		PROCEDURE & InitWriter*( send: Sender;  size: LONGINT );
+		BEGIN
+			ASSERT ( send # NIL );
+			NEW( buf, size );  SELF.send := send;  Reset
+		END InitWriter;
+
+		PROCEDURE Reset*;
+		BEGIN
+			tail := 0;  res := Ok;  sent := 0
+		END Reset;
+
+		PROCEDURE CanSetPos*( ): BOOLEAN;
+		BEGIN
+			RETURN FALSE
+		END CanSetPos;
+
+		PROCEDURE SetPos*( pos: HUGEINT );
+		BEGIN
+			HALT( 1234 )
+		END SetPos;
+
+		PROCEDURE Update*;
+		BEGIN
+			IF (res = Ok) THEN
+				send( buf^, 0, tail, TRUE , res );
+				IF res = Ok THEN INC( sent, tail );  tail := 0 END
+			END
+		END Update;
+
+	(** Current write position. *)
+		PROCEDURE Pos*( ): HUGEINT;
+		BEGIN
+			RETURN sent + tail;
+		END Pos;
+
+		(** -- Write raw binary data -- *)
+
+	(** Write one byte. *)
+		PROCEDURE Char*( x: CHAR );
+		BEGIN
+			IF (tail = LEN( buf )) & (res = Ok) THEN
+				send( buf^, 0, tail, FALSE , res );
+				IF res = Ok THEN INC( sent, tail );  tail := 0 END
+			END;
+			IF res = Ok THEN buf[tail] := x;  INC( tail ) END
+		END Char;
+
+	(** Write len bytes from x, starting at ofs. *)
+		PROCEDURE Bytes*(CONST x: ARRAY OF CHAR;  ofs, len: LONGINT );
+		VAR n: LONGINT;
+		BEGIN
+			ASSERT ( len >= 0 );
+			LOOP
+				n := LEN( buf ) - tail;   (* space available *)
+				IF n = 0 THEN
+					IF res = Ok THEN  (* send current buffer *)
+						send( buf^, 0, tail, FALSE , res );
+						IF res = Ok THEN INC( sent, tail );  tail := 0 ELSE EXIT END
+					ELSE
+						EXIT  (* should not be writing on an erroneous rider *)
+					END;
+					n := LEN( buf )
+				END;
+				IF n > len THEN n := len END;
+				ASSERT ( tail + n <= LEN( buf ) );   (* index check *)
+				SYSTEM.MOVE( ADDRESSOF( x[ofs] ), ADDRESSOF( buf[tail] ), n );  INC( tail, n );
+				IF len = n THEN EXIT END;   (* done *)
+				INC( ofs, n );  DEC( len, n )
+			END
+		END Bytes;
+
+	(** Write a SHORTINT. *)
+		PROCEDURE RawSInt*( x: SHORTINT );
+		BEGIN
+			Char( SYSTEM.VAL( CHAR, x ) )
+		END RawSInt;
+
+	(** Write an INTEGER. *)
+		PROCEDURE RawInt*( x: INTEGER );
+		BEGIN
+			Bytes( SYSTEM.VAL( Bytes2, x ), 0, 2 )
+		END RawInt;
+
+	(** Write a LONGINT. *)
+		PROCEDURE RawLInt*( x: LONGINT );
+		BEGIN
+			Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4 )
+		END RawLInt;
+
+	(** Write a HUGEINT. *)
+		PROCEDURE RawHInt*( x: HUGEINT );
+		BEGIN
+			Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8 )
+		END RawHInt;
+
+	(** Write a 64 bit value in network byte order (most significant byte first) *)
+		PROCEDURE Net64*( x: HUGEINT );
+		BEGIN
+			Net32(LONGINT( x DIV 100000000H MOD 100000000H ));
+			Net32(LONGINT( x MOD 100000000H ));
+		END Net64;
+
+	(** Write a 32 bit value in network byte order (most significant byte first) *)
+		PROCEDURE Net32*( x: LONGINT );
+		BEGIN
+			Char( CHR( x DIV 1000000H MOD 100H ) );  Char( CHR( x DIV 10000H MOD 100H ) );  Char( CHR( x DIV 100H MOD 100H ) );
+			Char( CHR( x MOD 100H ) )
+		END Net32;
+
+	(** Write a 16 bit value in network byte order (most significant byte first) *)
+		PROCEDURE Net16*( x: LONGINT );
+		BEGIN
+			Char( CHR( x DIV 100H MOD 100H ) );  Char( CHR( x MOD 100H ) )
+		END Net16;
+
+	(** write unsigned byte *)
+		PROCEDURE Net8*( x: LONGINT );
+		BEGIN
+			Char( CHR( x MOD 100H ) )
+		END Net8;
+
+	(** Write a SET. *)
+		PROCEDURE RawSet*( x: SET );
+		BEGIN
+			Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4 )
+		END RawSet;
+
+	(** Write a BOOLEAN. *)
+		PROCEDURE RawBool*( x: BOOLEAN );
+		BEGIN
+			IF x THEN Char( 1X ) ELSE Char( 0X ) END
+		END RawBool;
+
+	(** Write a REAL. *)
+		PROCEDURE RawReal*( x: REAL );
+		BEGIN
+			Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4 )
+		END RawReal;
+
+	(** Write a LONGREAL. *)
+		PROCEDURE RawLReal*( x: LONGREAL );
+		BEGIN
+			Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8 )
+		END RawLReal;
+
+	(** Write a 0X-terminated string, including the 0X terminator. *)
+		PROCEDURE RawString*(CONST x: ARRAY OF CHAR );
+		VAR i: LONGINT;
+		BEGIN
+			i := 0;
+			WHILE x[i] # 0X DO Char( x[i] );  INC( i ) END;
+			Char( 0X )
+		END RawString;
+
+	(** Write a number in a compressed format. *)
+		PROCEDURE RawNum*( x: LONGINT );
+		BEGIN
+			WHILE (x < -64) OR (x > 63) DO Char( CHR( x MOD 128 + 128 ) );  x := x DIV 128 END;
+			Char( CHR( x MOD 128 ) )
+		END RawNum;
+
+		(** -- Write formatted data -- *)
+
+	(** Write an ASCII end-of-line (CR/LF). *)
+		PROCEDURE Ln*;
+		BEGIN
+			Char( CR );  Char( LF )
+		END Ln;
+
+	(** Write a 0X-terminated string, excluding the 0X terminator. *)
+		PROCEDURE String*(CONST x: ARRAY OF CHAR );
+		VAR i: LONGINT;
+		BEGIN
+			i := 0;
+			WHILE x[i] # 0X DO Char( x[i] );  INC( i ) END
+		END String;
+
+	(** Write an integer in decimal right-justified in a field of at least w characters. *)
+		PROCEDURE Int*( x: HUGEINT; w: SIZE );
+		VAR i: SIZE; x0: HUGEINT;
+			a: ARRAY 21 OF CHAR;
+		BEGIN
+			IF x < 0 THEN
+				IF x = MIN( HUGEINT ) THEN
+					DEC( w, 20 );
+					WHILE w > 0 DO Char( " " );  DEC( w ) END;
+					String( "-9223372036854775808" );  RETURN
+				ELSE DEC( w );  x0 := -x
+				END
+			ELSE x0 := x
+			END;
+			i := 0;
+			REPEAT a[i] := CHR( x0 MOD 10 + 30H );  x0 := x0 DIV 10;  INC( i ) UNTIL x0 = 0;
+			WHILE w > i DO Char( " " );  DEC( w ) END;
+			IF x < 0 THEN Char( "-" ) END;
+			REPEAT DEC( i );  Char( a[i] ) UNTIL i = 0
+		END Int;
+
+	(** Write a SET in Oberon notation. *)
+	(*	PROCEDURE Set*( s: SET );   (* from P. Saladin *)
+		VAR i, last: LONGINT;  dots: BOOLEAN;
+		BEGIN
+			Char( "{" );  last := MIN( LONGINT );  dots := FALSE;
+			FOR i := MIN( SET ) TO MAX( SET ) DO
+				IF i IN s THEN
+					IF last = (i - 1) THEN
+						IF dots THEN String( ".." );  dots := FALSE END;
+						IF (i = MAX( SET )) OR ~((i + 1) IN s) THEN Int( i, 1 ) END
+					ELSE
+						IF last >= MIN( SET ) THEN String( ", " ) END;
+						Int( i, 1 );  dots := TRUE
+					END;
+					last := i
+				END
+			END;
+			Char( "}" )
+		END Set;	*)
+
+		PROCEDURE Set*( s: SET );   (* from P. Saladin *)
+		VAR i, last: LONGINT;  dots: BOOLEAN;
+		BEGIN
+			Char( "{" );  last := MAX( LONGINT );  dots := FALSE;
+			FOR i := MAX( SET ) TO 0 BY -1 DO
+				IF i IN s THEN
+					IF last = (i + 1) THEN
+						IF dots THEN String( ".." );  dots := FALSE END;
+						IF (i = 0) OR ~((i - 1) IN s) THEN Int( i, 1 ) END
+					ELSE
+						IF last <= MAX( SET ) THEN String( ", " ) END;
+						Int( i, 1 );  dots := TRUE
+					END;
+					last := i
+				END
+			END;
+			Char( "}" )
+		END Set;
+
+		(**
+			Write an integer in hexadecimal right-justified in a field of at least ABS(w) characters.
+			If w < 0 THEN w least significant hex digits of x are written (potentially including leading zeros)
+		*)
+		PROCEDURE Hex*(x: HUGEINT; w: SIZE);
+		VAR filler: CHAR; i,maxw: SIZE; a: ARRAY 20 OF CHAR; y: HUGEINT;
+		BEGIN
+			IF w < 0 THEN filler := '0'; w := -w; maxw := w ELSE filler := ' '; maxw := 16 END;
+			i := 0;
+			REPEAT
+				y := x MOD 10H;
+				IF y < 10 THEN a[i] := CHR(y+ORD('0')) ELSE a[i] := CHR(y-10+ORD('A')) END;
+				x := x DIV 10H;
+				INC(i);
+			UNTIL (x=0) OR (i=maxw);
+			WHILE w > i DO Char(filler);  DEC( w ) END;
+			REPEAT DEC( i ); Char( a[i] ) UNTIL i = 0
+		END Hex;
+
+		(** Write "x" as a hexadecimal address. Do not use Hex because of arithmetic shift of the sign !*)
+		PROCEDURE Address* (x: ADDRESS);
+		BEGIN
+			Hex(x,-2*SIZEOF(ADDRESS));
+		END Address;
+
+		PROCEDURE Pair( ch: CHAR;  x: LONGINT );
+		BEGIN
+			IF ch # 0X THEN Char( ch ) END;
+			Char( CHR( ORD( "0" ) + x DIV 10 MOD 10 ) );  Char( CHR( ORD( "0" ) + x MOD 10 ) )
+		END Pair;
+
+	(** Write the date and time in ISO format (yyyy-mm-dd hh:mm:ss).  The t and d parameters are in Oberon time and date format.
+			If all parameters are within range, the output string is exactly 19 characters wide.  The t or d parameter can be -1, in which
+			case the time or date respectively are left out. *)
+		PROCEDURE Date*( t, d: LONGINT );
+		VAR ch: CHAR;
+		BEGIN
+			IF d # -1 THEN
+				Int( 1900 + d DIV 512, 4 );   (* year *)
+				Pair( "-", d DIV 32 MOD 16 );   (* month *)
+				Pair( "-", d MOD 32 );   (* day *)
+				ch := " " (* space between date and time *)
+			ELSE
+				ch := 0X (* no space before time *)
+			END;
+			IF t # -1 THEN
+				Pair( ch, t DIV 4096 MOD 32 );   (* hour *)
+				Pair( ":", t DIV 64 MOD 64 );   (* min *)
+				Pair( ":", t MOD 64 ) (* sec *)
+			END
+		END Date;
+
+	(** Write the date and time in RFC 822/1123 format without the optional day of the week (dd mmm yyyy hh:mm:ss SZZZZ) .
+			The t and d parameters are in Oberon time and date format.  The tz parameter specifies the time zone offset in minutes
+			(from -720 to 720 in steps of 30).  If all parameters are within range, the output string is exactly 26 characters wide.
+			The t, d or tz parameter can be -1, in which case the time, date or timezone respectively are left out. *)
+		PROCEDURE Date822*( t, d, tz: LONGINT );
+		VAR i, m: LONGINT;  ch: CHAR;
+		BEGIN
+			IF d # -1 THEN
+				Int( d MOD 32, 2 );   (* day *)
+				m := (d DIV 32 MOD 16 - 1) * 4;   (* month *)
+				FOR i := m TO m + 3 DO Char( months[i] ) END;
+				Int( 1900 + d DIV 512, 5 );   (* year *)
+				ch := " " (* space *)
+			ELSE
+				ch := 0X (* no space *)
+			END;
+			IF t # -1 THEN
+				Pair( ch, t DIV 4096 MOD 32 );   (* hour *)
+				Pair( ":", t DIV 64 MOD 64 );   (* min *)
+				Pair( ":", t MOD 64 );   (* sec *)
+				ch := " " (* space *)
+			ELSE
+				(* leave ch as before *)
+			END;
+			IF tz # -1 THEN
+				IF ch # 0X THEN Char( ch ) END;
+				IF tz >= 0 THEN Pair( "+", tz DIV 60 ) ELSE Pair( "-", (-tz) DIV 60 ) END;
+				Pair( 0X, ABS( tz ) MOD 60 )
+			END
+		END Date822;
+
+
+	(** Write LONGREAL x  using n character positions. *)
+		PROCEDURE Float*( x: LONGREAL;  n: LONGINT );
+		(* BM 1993.4.22. Do not simplify rounding! *)
+		VAR e, h, l, i: LONGINT;  z: LONGREAL;
+			d: ARRAY 16 OF CHAR;
+		BEGIN
+			e := ExpoL( x );
+			IF e = 2047 THEN
+				WHILE n > 5 DO Char( " " );  DEC( n ) END;
+				NaNCodeL( x, h, l );
+				IF (h # 0) OR (l # 0) THEN String( "      NaN" )
+				ELSIF x < 0 THEN String( "     -INF" )
+				ELSE String( "      INF" )
+				END
+			ELSE
+				IF n <= 9 THEN n := 1 ELSE DEC( n, 8 ) END;
+				REPEAT Char( " " );  DEC( n ) UNTIL n <= 15;   (* 0 <= n <= 15 fraction digits *)
+				IF (e # 0) & (x < 0) THEN Char( "-" );  x := -x ELSE Char( " " ) END;
+				IF e = 0 THEN
+					h := 0;  l := 0 (* no denormals *)
+				ELSE
+					e := (e - 1023) * 301029 DIV 1000000;   (* ln(2)/ln(10) = 0.301029996 *)
+					z := Ten( e + 1 );
+					IF x >= z THEN x := x / z;  INC( e ) ELSE x := x * Ten( -e ) END;
+					IF x >= 10 THEN x := x * Ten( -1 ) + 0.5D0 / Ten( n );  INC( e )
+					ELSE
+						x := x + 0.5D0 / Ten( n );
+						IF x >= 10 THEN x := x * Ten( -1 );  INC( e ) END
+					END;
+					x := x * Ten( 7 );  h := ENTIER( x );  x := (x - h) * Ten( 8 );  l := ENTIER( x )
+				END;
+				i := 15;
+				WHILE i > 7 DO d[i] := CHR( l MOD 10 + ORD( "0" ) );  l := l DIV 10;  DEC( i ) END;
+				WHILE i >= 0 DO d[i] := CHR( h MOD 10 + ORD( "0" ) );  h := h DIV 10;  DEC( i ) END;
+				Char( d[0] );  Char( "." );  i := 1;
+				WHILE i <= n DO Char( d[i] );  INC( i ) END;
+				IF e < 0 THEN String( "E-" );  e := -e ELSE String( "E+" ) END;
+				Char( CHR( e DIV 100 + ORD( "0" ) ) );  e := e MOD 100;  Char( CHR( e DIV 10 + ORD( "0" ) ) );  Char( CHR( e MOD 10 + ORD( "0" ) ) )
+			END
+		END Float;
+
+	(** Write LONGREAL x in a fixed point notation. n is the overall minimal length for the output field, f the number of fraction digits following the decimal point, D the fixed exponent (printed only when D # 0). *)
+		PROCEDURE FloatFix*( x: LONGREAL;  n, f, D: LONGINT );
+		(* BM 1993.4.22. Do not simplify rounding! / JG formatting adjusted *)
+		VAR e, h, l, i: LONGINT;  r, z: LONGREAL;
+			d: ARRAY 16 OF CHAR;
+			s: CHAR;  dot: BOOLEAN;
+		BEGIN
+			e := ExpoL( x );
+			IF (e = 2047) OR (ABS( D ) > 308) THEN
+				WHILE n > 5 DO Char( " " );  DEC( n ) END;
+				NaNCodeL( x, h, l );
+				IF (h # 0) OR (l # 0) THEN String( "      NaN" )
+				ELSIF x < 0 THEN String( "     -INF" )
+				ELSE String( "      INF" )
+				END
+			ELSE
+				IF D = 0 THEN IF (f=0) THEN dot := FALSE; DEC( n, 1 ) ELSE dot := TRUE; DEC(n,2);  END;  ELSE dot := TRUE; DEC( n, 7 ) END;
+				IF n < 2 THEN n := 2 END;
+				IF f < 0 THEN f := 0 END;
+				IF n < f + 2 THEN n := f + 2 END;
+				DEC( n, f );
+				IF (e # 0) & (x < 0) THEN s := "-";  x := -x ELSE s := " " END;
+				IF e = 0 THEN
+					h := 0;  l := 0;  DEC( e, D - 1 ) (* no denormals *)
+				ELSE
+					e := (e - 1023) * 301029 DIV 1000000;   (* ln(2)/ln(10) = 0.301029996 *)
+					z := Ten( e + 1 );
+					IF x >= z THEN x := x / z;  INC( e ) ELSE x := x * Ten( -e ) END;
+					DEC( e, D - 1 );  i := -(e + f);
+					IF i <= 0 THEN r := 5 * Ten( i ) ELSE r := 0 END;
+					IF x >= 10 THEN x := x * Ten( -1 ) + r;  INC( e )
+					ELSE
+						x := x + r;
+						IF x >= 10 THEN x := x * Ten( -1 );  INC( e ) END
+					END;
+					x := x * Ten( 7 );  h := ENTIER( x );  x := (x - h) * Ten( 8 );  l := ENTIER( x )
+				END;
+				i := 15;
+				WHILE i > 7 DO d[i] := CHR( l MOD 10 + ORD( "0" ) );  l := l DIV 10;  DEC( i ) END;
+				WHILE i >= 0 DO d[i] := CHR( h MOD 10 + ORD( "0" ) );  h := h DIV 10;  DEC( i ) END;
+				IF n <= e THEN n := e + 1 END;
+				IF e > 0 THEN
+					WHILE n > e DO Char( " " );  DEC( n ) END;
+					Char( s );  e := 0;
+					WHILE n > 0 DO
+						DEC( n );
+						IF e < 16 THEN Char( d[e] );  INC( e ) ELSE Char( "0" ) END
+					END;
+					IF dot THEN
+					Char( "." )
+					END;
+				ELSE
+					WHILE n > 1 DO Char( " " );  DEC( n ) END;
+					Char( s );  Char( "0" );  IF dot THEN Char( "." );  END;
+					WHILE (0 < f) & (e < 0) DO Char( "0" );  DEC( f );  INC( e ) END
+				END;
+				WHILE f > 0 DO
+					DEC( f );
+					IF e < 16 THEN Char( d[e] );  INC( e ) ELSE Char( "0" ) END
+				END;
+				IF D # 0 THEN
+					IF D < 0 THEN String( "E-" );  D := -D ELSE String( "E+" ) END;
+					Char( CHR( D DIV 100 + ORD( "0" ) ) );  D := D MOD 100;  Char( CHR( D DIV 10 + ORD( "0" ) ) );  Char( CHR( D MOD 10 + ORD( "0" ) ) )
+				END
+			END
+		END FloatFix;
+
+	END Writer;
+
+	(** A special writer that buffers output to be fetched by GetString or GetRawString. *)
+	StringWriter* = OBJECT (Writer)
+
+		PROCEDURE & InitStringWriter*( size: LONGINT );
+		BEGIN
+			InitWriter( Send, size )
+		END InitStringWriter;
+
+		PROCEDURE Send( CONST buf: ARRAY OF CHAR;  ofs, len: LONGINT;  propagate: BOOLEAN;  VAR res: LONGINT );
+		BEGIN
+			res := StringFull
+		END Send;
+
+		PROCEDURE CanSetPos*( ): BOOLEAN;
+		BEGIN
+			RETURN TRUE;
+		END CanSetPos;
+
+	(* Set the position for the writer *)
+		PROCEDURE SetPos*( pos: HUGEINT );
+		BEGIN
+			IF pos > LEN( buf ) THEN pos := LEN( buf ) END;
+			tail := LONGINT( pos );  sent := 0;  res := Ok;
+		END SetPos;
+
+		PROCEDURE Update;
+		(* nothing to do *)
+		END Update;
+
+	(** Return the contents of the string writer (0X-terminated). *)
+		PROCEDURE Get*( VAR s: ARRAY OF CHAR );
+		VAR i, m: LONGINT;
+		BEGIN
+			m := LEN( s ) - 1;  i := 0;
+			WHILE (i # tail) & (i < m) DO s[i] := buf[i];  INC( i ) END;
+			s[i] := 0X;  tail := 0;  res := Ok
+		END Get;
+
+	(** Return the contents of the string writer (not 0X-terminated).  The len parameters returns the string length. *)
+		PROCEDURE GetRaw*( VAR s: ARRAY OF CHAR;  VAR len: LONGINT );
+		VAR i, m: LONGINT;
+		BEGIN
+			m := LEN( s );  i := 0;
+			WHILE (i # tail) & (i < m) DO s[i] := buf[i];  INC( i ) END;
+			len := i;  tail := 0;  res := Ok
+		END GetRaw;
+
+	END StringWriter;
+
+TYPE
+	(** A reader buffers input received from a Receiver.  Must not be shared between processes. *)
+	Reader* = OBJECT
+	VAR
+		head, tail: LONGINT;
+		buf: POINTER TO ARRAY OF CHAR;
+		res*: LONGINT;   (** result of last input operation. *)
+		receive: Receiver;
+		received*: HUGEINT;   (** count of received bytes *)
+		(* buf[buf.head..buf.tail-1] contains data to read. *)
+
+		PROCEDURE & InitReader*( receive: Receiver;  size: LONGINT );
+		BEGIN
+			ASSERT ( receive # NIL );
+			IF (buf = NIL) OR (LEN(buf) # size) THEN
+				NEW( buf, size );
+			END;
+			SELF.receive := receive;  Reset
+		END InitReader;
+
+	(** reset the reader by dropping the bytes in the buffer, resetting the result code and setting received to 0.
+			This is used by seekable extensions of the reader *)
+		PROCEDURE Reset*;
+		BEGIN
+			head := 0;  tail := 0;  res := Ok;  received := 0
+		END Reset;
+
+		PROCEDURE CanSetPos*( ): BOOLEAN;
+		BEGIN
+			RETURN FALSE
+		END CanSetPos;
+
+		PROCEDURE SetPos*( pos: HUGEINT );
+		BEGIN
+			HALT( 1234 )
+		END SetPos;
+
+	(** Return bytes currently available in input buffer. *)
+		PROCEDURE Available*( ): LONGINT;
+		VAR n: LONGINT;
+		BEGIN
+			IF (res = Ok) THEN
+				IF (head = tail) THEN head := 0;  receive( buf^, 0, LEN( buf ), 0, tail, res );  INC( received, tail );
+				ELSIF (tail # LEN( buf )) THEN
+					receive( buf^, tail, LEN( buf ) - tail, 0, n, res );   (* poll *)
+					INC( tail, n );  INC( received, n )
+				END;
+				IF res = EOF THEN res := Ok END  (* ignore EOF here *)
+			END;
+			RETURN tail - head
+		END Available;
+
+	(** Current read position. *)
+		PROCEDURE Pos*( ): HUGEINT;
+		BEGIN
+			RETURN received - HUGEINT(tail) - HUGEINT(head)
+		END Pos;
+
+		(** -- Read raw binary data -- *)
+
+	(** Read one byte. x=0X if no success (e.g. file ended) *)
+		PROCEDURE Char*( VAR x: CHAR );
+		BEGIN
+			IF (head = tail) & (res = Ok) THEN head := 0;  receive( buf^, 0, LEN( buf ), 1, tail, res );  INC( received, tail ) END;
+			IF res = Ok THEN x := buf[head];  INC( head ) ELSE x := 0X END
+		END Char;
+
+	(** Like Read, but return result. Return 0X if no success (e.g. file ended) *)
+		PROCEDURE Get*( ): CHAR;
+		BEGIN
+			IF (head = tail) & (res = Ok) THEN head := 0;  receive( buf^, 0, LEN( buf ), 1, tail, res );  INC( received, tail ) END;
+			IF res = Ok THEN INC( head );  RETURN buf[head - 1] ELSE RETURN 0X END
+		END Get;
+
+	(** Like Get, but leave the byte in the input buffer. *)
+		PROCEDURE Peek*( ): CHAR;
+		BEGIN
+			IF (head = tail) & (res = Ok) THEN
+				head := 0;  receive( buf^, 0, LEN( buf ), 1, tail, res );  INC( received, tail );
+				IF res = EOF THEN  (* ignore EOF here *)
+					res := Ok;  tail := 0; RETURN 0X (* Peek returns 0X at eof *)
+				END
+			END;
+			IF res = Ok THEN RETURN buf[head] ELSE RETURN 0X END
+		END Peek;
+
+	(** Read size bytes into x, starting at ofs.  The len parameter returns the number of bytes that were actually read. *)
+		PROCEDURE Bytes*( VAR x: ARRAY OF CHAR;  ofs, size: LONGINT;  VAR len: LONGINT );
+		VAR n: LONGINT;
+		BEGIN
+			ASSERT ( size >= 0 );
+			len := 0;
+			LOOP
+				n := tail - head;   (* bytes available *)
+				IF n = 0 THEN  (* no data available *)
+					head := 0;
+					IF res = Ok THEN  (* fill buffer *)
+						receive( buf^, 0, LEN( buf ), 1, tail, res );  INC( received, tail )
+					END;
+					IF res # Ok THEN  (* should not be reading from erroneous rider *)
+						WHILE size # 0 DO x[ofs] := 0X;  INC( ofs );  DEC( size ) END;   (* clear rest of buffer *)
+						IF (res = EOF) & (len # 0) THEN res := Ok END;   (* ignore EOF if some data being returned *)
+						EXIT
+					END;
+					n := tail
+				END;
+				IF n > size THEN n := size END;
+				ASSERT ( ofs + n <= LEN( x ) );   (* index check *)
+				SYSTEM.MOVE( ADDRESSOF( buf[head] ), ADDRESSOF( x[ofs] ), n );  INC( head, n );  INC( len, n );
+				IF size = n THEN EXIT END;   (* done *)
+				INC( ofs, n );  DEC( size, n )
+			END
+		END Bytes;
+
+	(** Skip n bytes on the reader. *)
+		PROCEDURE SkipBytes*( n: LONGINT );
+		VAR ch: CHAR;
+		BEGIN
+			WHILE n > 0 DO ch := Get();  DEC( n ) END
+		END SkipBytes;
+
+	(** Read a SHORTINT. *)
+		PROCEDURE RawSInt*( VAR x: SHORTINT );
+		BEGIN
+			x := SYSTEM.VAL( SHORTINT, Get() )
+		END RawSInt;
+
+	(** Read an INTEGER. *)
+		PROCEDURE RawInt*( VAR x: INTEGER );
+		VAR x0, x1: CHAR;
+		BEGIN
+			x0 := Get();  x1 := Get();   (* defined order *)
+			x := ORD( x1 ) * 100H + ORD( x0 )
+		END RawInt;
+
+	(** Read a LONGINT. *)
+		PROCEDURE RawLInt*( VAR x: LONGINT );
+		VAR ignore: LONGINT;
+		BEGIN
+			Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
+		END RawLInt;
+
+	(** Read a HUGEINT. *)
+		PROCEDURE RawHInt*( VAR x: HUGEINT );
+		VAR ignore: LONGINT;
+		BEGIN
+			Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8, ignore )
+		END RawHInt;
+
+		(** Read a 64 bit value in network byte order (most significant byte first) *)
+		PROCEDURE Net64*( ): HUGEINT;
+		BEGIN
+			RETURN Net32() * 100000000H + Net32()
+		END Net64;
+
+	(** Read a 32 bit value in network byte order (most significant byte first) *)
+		PROCEDURE Net32*( ): LONGINT;
+		BEGIN
+			RETURN LONG( ORD( Get() ) ) * 1000000H + LONG( ORD( Get() ) ) * 10000H + LONG( ORD( Get() ) ) * 100H + LONG( ORD( Get() ) )
+		END Net32;
+
+	(** Read an unsigned 16bit value in network byte order (most significant byte first) *)
+		PROCEDURE Net16*( ): LONGINT;
+		BEGIN
+			RETURN LONG( ORD( Get() ) ) * 100H + LONG( ORD( Get() ) )
+		END Net16;
+
+	(** Read an unsigned byte *)
+		PROCEDURE Net8*( ): LONGINT;
+		BEGIN
+			RETURN LONG( ORD( Get() ) )
+		END Net8;
+
+	(** Read a SET. *)
+		PROCEDURE RawSet*( VAR x: SET );
+		VAR ignore: LONGINT;
+		BEGIN
+			Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
+		END RawSet;
+
+	(** Read a BOOLEAN. *)
+		PROCEDURE RawBool*( VAR x: BOOLEAN );
+		BEGIN
+			x := (Get() # 0X)
+		END RawBool;
+
+	(** Read a REAL. *)
+		PROCEDURE RawReal*( VAR x: REAL );
+		VAR ignore: LONGINT;
+		BEGIN
+			Bytes( SYSTEM.VAL( Bytes4, x ), 0, 4, ignore )
+		END RawReal;
+
+	(** Read a LONGREAL. *)
+		PROCEDURE RawLReal*( VAR x: LONGREAL );
+		VAR ignore: LONGINT;
+		BEGIN
+			Bytes( SYSTEM.VAL( Bytes8, x ), 0, 8, ignore )
+		END RawLReal;
+
+	(** Read a 0X-terminated string.  If the input string is larger than x, read the full string and assign the truncated 0X-terminated value to x. *)
+		PROCEDURE RawString*( VAR x: ARRAY OF CHAR );
+		VAR i, m: LONGINT;  ch: CHAR;
+		BEGIN
+			i := 0;  m := LEN( x ) - 1;
+			LOOP
+				ch := Get();   (* also returns 0X on error *)
+				IF ch = 0X THEN EXIT END;
+				IF i < m THEN x[i] := ch;  INC( i ) END
+			END;
+			x[i] := 0X
+		END RawString;
+
+	(** Read a number in a compressed format. *)
+		PROCEDURE RawNum*( VAR x: LONGINT );
+		VAR ch: CHAR;  n, y: LONGINT;
+		BEGIN
+			n := 0;  y := 0;  ch := Get();
+			WHILE ch >= 80X DO INC( y, LSH( LONGINT( ORD( ch ) ) - 128, n ) );  INC( n, 7 );  ch := Get() END;
+			x := ASH( LSH( LONGINT( ORD( ch ) ), 25 ), n - 25 ) + y
+		END RawNum;
+
+		(** -- Read formatted data (uses Peek for one character lookahead) -- *)
+
+	 (** Read an integer value in decimal or hexadecimal.  If hex = TRUE, recognize the "H" postfix for hexadecimal numbers. *)
+
+		PROCEDURE Int*( VAR x: LONGINT;  hex: BOOLEAN );
+		VAR vd, vh, sgn, d: LONGINT;  ch: CHAR;  ok: BOOLEAN;
+		BEGIN
+			vd := 0;  vh := 0;  sgn := 1;  ok := FALSE;
+			IF Peek() = "-" THEN sgn := -1;  ch := Get() END;
+			LOOP
+				ch := Peek();
+				IF (ch >= "0") & (ch <= "9") THEN d := ORD( ch ) - ORD( "0" )
+				ELSIF hex & (CAP( ch ) >= "A") & (CAP( ch ) <= "F") THEN d := ORD( CAP( ch ) ) - ORD( "A" ) + 10
+				ELSE EXIT
+				END;
+				vd := 10 * vd + d;  vh := 16 * vh + d;   (* ignore overflow *)
+				ch := Get();  ok := TRUE
+			END;
+			IF hex & (CAP( ch ) = "H") THEN  (* optional "H" present *)
+				vd := vh;   (* use the hex value *)
+				ch := Get()
+			END;
+			x := sgn * vd;
+			IF (res = 0) & ~ok THEN res := FormatError END
+		END Int;
+
+	(** Read a floating-point number. EBNF: Real = Digit {Digit} '.' Digit {Digit} ['e'|'E' ['+'|'-'] Digit {Digit}]. *)
+		PROCEDURE Real* (VAR real: LONGREAL);
+		VAR e: INTEGER; y, g: LONGREAL; neg, negE: BOOLEAN; ch: CHAR;
+		BEGIN
+			ch := Get();
+			WHILE (ch = "0") DO ch := Get() END;
+			IF ch = "-" THEN neg := TRUE; ch := Get(); ELSE neg := FALSE END;
+			WHILE (ch = " ") OR (ch = "0") DO ch := Get(); END;
+			y := 0;
+			WHILE ("0" <= ch) & (ch <= "9") DO
+				y := y * 10 + (ORD(ch) - ORD("0"));
+				ch := Get();
+			END;
+			IF ch = "." THEN
+				ch := Get();
+				g := 1;
+				WHILE ("0" <= ch) & (ch <= "9") DO
+					g := g / 10; y := y + g * (ORD(ch) - ORD("0"));
+					ch := Get();
+				END;
+			END;
+			IF (ch = "d") OR (ch = "D") OR (ch = "e") OR (ch = "E") THEN
+				ch := Get(); e := 0;
+				IF ch = "-" THEN negE := TRUE; ch := Get()
+				ELSIF ch = "+" THEN negE := FALSE; ch := Get()
+				ELSE negE := FALSE
+				END;
+				WHILE (ch = "0") DO ch := Get() END;
+				WHILE ("0" <= ch) & (ch <= "9") DO
+					e := e * 10 + (ORD(ch) - ORD("0"));
+					ch := Get();
+				END;
+				IF negE THEN y := y / Ten(e)
+				ELSE y := y * Ten(e) 
+				END;
+			END;
+			IF neg THEN y := -y END;
+			real := y
+		END Real;
+
+	(** Return TRUE iff at the end of a line (or file). *)
+		PROCEDURE EOLN*( ): BOOLEAN;
+		VAR ch: CHAR;
+		BEGIN
+			ch := Peek();  RETURN (ch = CR) OR (ch = LF) OR (res # Ok)
+		END EOLN;
+
+	(** Read all characters until the end of the line (inclusive).  If the input string is larger than x, read the full string and assign
+			the truncated 0X-terminated value to x. *)
+		PROCEDURE Ln*( VAR x: ARRAY OF CHAR );
+		VAR i, m: LONGINT;  ch: CHAR;
+		BEGIN
+			i := 0;  m := LEN( x ) - 1;
+			LOOP
+				ch := Peek();
+				IF (ch = CR) OR (ch = LF) OR (res # Ok) THEN EXIT END;
+				IF i < m THEN x[i] := ch;  INC( i ) END;
+				ch := Get()
+			END;
+			x[i] := 0X;
+			IF ch = CR THEN ch := Get() END;
+			IF Peek() = LF THEN ch := Get() END
+		END Ln;
+
+	(** Read all characters until the end of the line (inclusive) or an <EOT> character.
+			If the input string is larger than x, read the full string and assign the truncated 0X-terminated
+			value to x. *)
+		PROCEDURE LnEOT*( VAR x: ARRAY OF CHAR );
+		VAR i, m: LONGINT;  ch: CHAR;
+		BEGIN
+			i := 0;  m := LEN( x ) - 1;
+			LOOP
+				ch := Peek();
+				IF (ch = CR) OR (ch = LF) OR (ch = EOT) OR (res # Ok) THEN EXIT END;
+				IF i < m THEN x[i] := ch;  INC( i ) END;
+				ch := Get()
+			END;
+			x[i] := 0X;
+			IF ch = CR THEN ch := Get() END;
+			IF Peek() = LF THEN ch := Get() END;
+			IF ch = EOT THEN ch := Get() END
+		END LnEOT;
+
+	(** Skip over all characters until the end of the line (inclusive). *)
+		PROCEDURE SkipLn*;
+		VAR ch: CHAR;
+		BEGIN
+			LOOP
+				ch := Peek();
+				IF (ch = CR) OR (ch = LF) OR (res # Ok) THEN EXIT END;
+				ch := Get()
+			END;
+			IF ch = CR THEN ch := Get() END;
+			IF Peek() = LF THEN ch := Get() END
+		END SkipLn;
+
+	(** Skip over space and TAB characters. *)
+		PROCEDURE SkipSpaces*;
+		VAR ch: CHAR;
+		BEGIN
+			LOOP
+				ch := Peek();
+				IF (ch # TAB) & (ch # SP) THEN EXIT END;
+				ch := Get()
+			END
+		END SkipSpaces;
+
+	(** Skip over space, TAB and EOLN characters. *)
+		PROCEDURE SkipWhitespace*;
+		VAR ch: CHAR;
+		BEGIN
+			LOOP
+				ch := Peek();
+				IF (ch # SP) & (ch # CR) & (ch # LF) & (ch # TAB) THEN EXIT END;
+				ch := Get()
+			END
+		END SkipWhitespace;
+
+	(** Read a token, consisting of any string of characters terminated by space, TAB or EOLN. *)
+		PROCEDURE Token*( VAR token: ARRAY OF CHAR );
+		VAR j, max: LONGINT;  ch: CHAR;
+		BEGIN
+			j := 0;  max := LEN( token ) - 1;
+			LOOP
+				ch := Peek();
+				IF (ch = SP) OR (ch = CR) OR (ch = LF) OR (ch = TAB) OR (res # Ok) THEN EXIT END;
+				IF j < max THEN token[j] := ch;  INC( j ) END;
+				ch := Get()
+			END;
+			token[j] := 0X
+		END Token;
+
+	(** Read an optionally "" or '' enquoted string.  Will not read past the end of a line. *)
+		PROCEDURE String*( VAR string: ARRAY OF CHAR );
+		VAR c, delimiter: CHAR;  i, len: LONGINT;
+		BEGIN
+			c := Peek();
+			IF (c # "'") & (c # '"') THEN Token( string )
+			ELSE
+				delimiter := Get();  c := Peek();  i := 0;  len := LEN( string ) - 1;
+				WHILE (i < len) & (c # delimiter) & (c # CR) & (c # LF) & (res = Ok) DO string[i] := Get();  INC( i );  c := Peek() END;
+				IF (c = delimiter) THEN c := Get() END;
+				string[i] := 0X
+			END
+		END String;
+
+		(** First skip whitespace, then read string *)
+		PROCEDURE GetString*(VAR string : ARRAY OF CHAR): BOOLEAN;
+		VAR c: CHAR;
+		BEGIN
+			SkipWhitespace;
+			c := Peek();
+			String(string);
+			RETURN (string[0] # 0X) OR (c = "'") OR (c = '"');
+		END GetString;
+
+		(** First skip whitespace, then read integer *)
+		PROCEDURE GetInteger*(VAR integer : LONGINT; isHexadecimal : BOOLEAN): BOOLEAN;
+		BEGIN
+			SkipWhitespace;
+			Int(integer, isHexadecimal);
+			RETURN res = Ok;
+		END GetInteger;
+
+		(** First skip whitespace, then read a real *)
+		PROCEDURE GetReal*(VAR real: LONGREAL): BOOLEAN;
+		BEGIN
+			SkipWhitespace;
+			Real(real);
+			RETURN res = Ok
+		END GetReal;
+
+		(** First skip whitespace, then read 1 byte character *)
+		PROCEDURE GetChar*(VAR ch : CHAR): BOOLEAN;
+		BEGIN
+			SkipWhitespace;
+			Char(ch);
+			RETURN ch # 0X;
+		END GetChar;
+
+	END Reader;
+
+TYPE
+	(** A special reader that buffers input set by SetString or SetRawString. *)
+	StringReader* = OBJECT (Reader)
+
+		PROCEDURE & InitStringReader*( size: LONGINT );
+		BEGIN
+			InitReader( Receive, size )
+		END InitStringReader;
+
+		PROCEDURE CanSetPos*( ): BOOLEAN;
+		BEGIN
+			RETURN TRUE
+		END CanSetPos;
+
+	(** Set the reader position *)
+		PROCEDURE SetPos*( pos: HUGEINT );
+		BEGIN
+			IF pos > LEN( buf ) THEN pos := LEN( buf ) END;
+			head := LONGINT( pos );  tail := LEN( buf );  received := LEN( buf );  res := Ok;
+		END SetPos;
+
+		PROCEDURE Receive( VAR buf: ARRAY OF CHAR;  ofs, size, min: LONGINT;  VAR len, res: LONGINT );
+		BEGIN
+			IF min = 0 THEN res := Ok ELSE res := EOF END;
+			len := 0;
+		END Receive;
+
+	(** Set the contents of the string buffer.  The s parameter is a 0X-terminated string. *)
+		PROCEDURE Set*(CONST  s: ARRAY OF CHAR );
+		VAR len: LONGINT;
+		BEGIN
+			len := 0;
+			WHILE s[len] # 0X DO INC( len ) END;
+			IF len > LEN( buf ) THEN len := LEN( buf ) END;
+			head := 0;  tail := len;  received := len;  res := Ok;
+			IF len > 0 THEN
+				SYSTEM.MOVE( ADDRESSOF( s[0] ), ADDRESSOF( buf[0] ), len )
+			END;
+		END Set;
+
+	(** Set the contents of the string buffer.  The len parameter specifies the size of the buffer s. *)
+		PROCEDURE SetRaw*(CONST s: ARRAY OF CHAR;  ofs, len: LONGINT );
+		BEGIN
+			IF len > LEN( buf ) THEN len := LEN( buf ) END;
+			head := 0;  tail := len;  received := len;  res := Ok;
+			ASSERT ( (len >= 0) & (ofs + len <= LEN( s )) );   (* index check *)
+			IF len > 0 THEN
+				SYSTEM.MOVE( ADDRESSOF( s[ofs] ), ADDRESSOF( buf[0] ), len )
+			END;
+		END SetRaw;
+
+	END StringReader;
+
+	Bytes2 = ARRAY 2 OF CHAR;
+	Bytes4 = ARRAY 4 OF CHAR;
+	Bytes8 = ARRAY 8 OF CHAR;
+
+VAR
+	months: ARRAY 12 * 4 + 1 OF CHAR;
+
+
+	(** Open a writer to the specified stream sender.  Update must be called after writing to ensure the buffer is written to the stream. *)
+	PROCEDURE OpenWriter*( VAR b: Writer;  send: Sender );
+	BEGIN
+		NEW( b, send, DefaultWriterSize )
+	END OpenWriter;
+
+(** Open a reader from the specified stream receiver. *)
+	PROCEDURE OpenReader*( VAR b: Reader;  receive: Receiver );
+	BEGIN
+		NEW( b, receive, DefaultReaderSize )
+	END OpenReader;
+
+(** Copy the contents of a reader to a writer *)
+	PROCEDURE Copy* (r: Reader; w: Writer);
+	VAR char: CHAR;
+	BEGIN
+		WHILE r.res = Ok DO
+			r.Char (char);
+			IF r.res = Ok THEN w.Char (char) END
+		END;
+	END Copy;
+
+	(** from module Reals.Mod *)
+
+
+(*** the following procedures stem from Reals.Mod and are needed for Writer.Float and Writer.FloatFix *)
+
+(** Returns the NaN code (0 <= h < 1048576, MIN(LONGINT) <= l <= MAX(LONGINT)) or (-1,-1) if not NaN/Infinite. *)
+	PROCEDURE NaNCodeL( x: LONGREAL;  VAR h, l: LONGINT );
+	BEGIN
+		SYSTEM.GET( ADDRESSOF( x ) + H, h );  SYSTEM.GET( ADDRESSOF( x ) + L, l );
+		IF ASH( h, -20 ) MOD 2048 = 2047 THEN  (* Infinite or NaN *)
+			h := h MOD 100000H (* lowest 20 bits *)
+		ELSE h := -1;  l := -1
+		END
+	END NaNCodeL;
+
+(** Returns the shifted binary exponent (0 <= e < 2048). *)
+	PROCEDURE ExpoL( x: LONGREAL ): LONGINT;
+	VAR i: LONGINT;
+	BEGIN
+		SYSTEM.GET( ADDRESSOF( x ) + H, i );  RETURN ASH( i, -20 ) MOD 2048
+	END ExpoL;
+
+(** Convert hexadecimal to LONGREAL. h and l are the high and low parts.*)
+	PROCEDURE RealL( h, l: LONGINT ): LONGREAL;
+	VAR x: LONGREAL;
+	BEGIN
+		SYSTEM.PUT( ADDRESSOF( x ) + H, h );  SYSTEM.PUT( ADDRESSOF( x ) + L, l );  RETURN x
+	END RealL;
+
+(** Returns 10^e (e <= 308, 308 < e delivers IEEE-code +INF). *)
+	PROCEDURE Ten( e: LONGINT ): LONGREAL;   (* naiive version *)
+	VAR r: LONGREAL;
+	BEGIN
+		IF e < -307 THEN RETURN 0
+		ELSIF 308 < e THEN RETURN RealL( 2146435072, 0 )
+		END;
+		r := 1;
+		WHILE (e > 0) DO r := r * 10;  DEC( e );  END;
+		WHILE (e < 0) DO r := r / 10;  INC( e );  END;
+		RETURN r;
+	END Ten;
+
+	PROCEDURE InitHL;
+	VAR i: ADDRESS;  dmy: INTEGER;  littleEndian: BOOLEAN;
+	BEGIN
+		dmy := 1;  i := ADDRESSOF( dmy );
+		SYSTEM.GET( i, littleEndian );   (* indirection via i avoids warning on SUN cc -O *)
+		IF littleEndian THEN H := 4;  L := 0 ELSE H := 0;  L := 4 END
+	END InitHL;
+
+
+BEGIN
+	months := " Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec";  InitHL;
+END Streams64.
+
+(**
+Notes:
+o	Any single buffer instance must not be accessed by more than one process concurrently.
+o 	The interface is blocking (synchronous).  If an output buffer is full, it is written with a synchronous write, which returns
+	only when all the data has been written.   If an input buffer is empty, it is read with a synchronous read, which only returns
+	once some data has been read.  The only exception is the Available() procedure, which "peeks" at the input stream
+	and returns 0 if no data is currently available.
+o 	All procedures set res to the error code reported by the lower-level I/O operation (non-zero indicates error).
+	 E.g. closing an underlying TCP connection will result in the Read* procedures returning a non-zero error code.
+o 	res is sticky.  Once it becomes non-zero, it remains non-zero.
+o 	The only way to detect end of file is to attempt to read past the end of file, which returns a non-zero error code.
+o 	All output written to an erroneous buffer is ignored.
+o 	The value returned when reading from an erroneous buffer is undefined, except for the Read procedure, which returns 0X.
+o 	ReadBytes sets the len parameter to the number of bytes that were actually read, e.g. if size = 10, and only 8 bytes are read, len is 8.
+o 	Raw format is little-endian 2's complement integers, IEEE reals and 0X-terminated strings.
+o 	Syntax for ReadInt with hex = FALSE: num = ["-"] digit {digit}. digit = "0".."9".
+o 	Syntax for ReadInt with hex = TRUE: ["-"] hexdigit {hexdigit} ["H"|"h"]. hexdigit = digit | "A".."F" | "a".."f".
+o 	ReadInt with hex = TRUE allows "A".."F" as digits, and looks for a "H" character after the number.
+	If present, the number is interpreted as hexadecimal.  If hexadecimal digits are present, but no "H" flag,
+	the resulting decimal value is undefined.
+o 	ReadInt ignores overflow.
+o 	A Sender sends len bytes from buf at ofs to output and returns res non-zero on error.  It waits until all the data is written,
+	or an error occurs.
+o 	A Receiver receives up to size bytes from input into buf at ofs and returns the number of bytes read in len.
+	It returns res non-zero on error.  It waits until at least min bytes (possibly zero) are available, or an error occurs.
+o 	EOLN and ReadLn recognize the following end-of-line characters: CR, LF and CR/LF.
+o 	To read an unstructured file token-by-token: WHILE (r.res = 0) DO SkipWhitespace; ReadToken END
+o 	To read a line structured file token-by-token: WHILE r.res = 0 DO SkipSpaces; WHILE ~EOLN DO ReadToken; SkipSpaces END END
+o 	A string writer is not flushed when it becomes full, but res is set to a non-zero value.
+o 	Update has no effect on a string writer.
+o 	GetString can be called on a string writer to return the buffer contents and reset it to empty.
+o 	GetString always appends a 0X character to the buffer, but returns the true length (excluding the added 0X) in the len parameter,
+	so it can also be used for binary data that includes 0X characters.
+o 	Receive procedure should set res to EOF when attempting to read past the end of file.
+*)
+
+
+(*
+to do:
+o stream byte count
+o read formatted data
+o reads for all formatted writes
+o write reals
+o low-level version that can be used in kernel (below KernelLog)
+*)

+ 71 - 55
source/Win32.WinFiles64.Mod

@@ -1,7 +1,6 @@
 MODULE WinFiles64;   (*AUTHOR "fof,ejz"; PURPOSE "Windows file system  for WinAos"; *)
 
 IMPORT SYSTEM, Machine, Kernel32, KernelLog, Modules, Kernel, Files := Files64, Commands;
-(* orange marked lines denote overloaded methods *)
 
 CONST
 	PathDelimiter = "\";  BufferSize = 4096;
@@ -20,8 +19,6 @@ CONST
 
 
 TYPE
-	BESTSIZE = Files.BESTSIZE;
-	
 	FileName = ARRAY Kernel32.MaxPath OF CHAR;
 	PFileName = POINTER TO FileName;
 	NotificationProc* = PROCEDURE ( type: LONGINT;  drives: SET );
@@ -199,13 +196,13 @@ TYPE
 		BEGIN
 			Prefix( name, fname );  fs.RemoveDirectory0( fname, force, key, res );
 		END RemoveDirectory0;
-		
+
 		PROCEDURE Has(CONST name: ARRAY OF CHAR; VAR fullName: ARRAY OF CHAR; VAR flags: SET): BOOLEAN;
 		VAR fname: FileName;
 		BEGIN
 			Prefix(name, fname );  RETURN fs.Has(fname, fullName, flags);
 		END Has;
-		
+
 
 	END AliasFileSystem;
 
@@ -239,7 +236,7 @@ TYPE
 			IF TraceFileSystem IN Trace THEN KernelLog.String( "Old0 " );  KernelLog.String( name );  KernelLog.Ln;  END;
 
 			IF (name # "") & FindFile( name, fname ) THEN
-				hfile := Kernel32.CreateFile( fname, {Kernel32.GenericRead}, {Kernel32.FileShareRead, Kernel32.FileShareWrite}, NIL , Kernel32.OpenExisting, {Kernel32.FileAttributeNormal}, 0 );
+				hfile := Kernel32.CreateFile( fname, Kernel32.SetToDW({Kernel32.GenericRead}), Kernel32.SetToDW({Kernel32.FileShareRead, Kernel32.FileShareWrite}), NIL , Kernel32.OpenExisting, Kernel32.SetToDW({Kernel32.FileAttributeNormal}), 0 );
 				IF hfile # Kernel32.InvalidHandleValue THEN NEW( F, fname, hfile, collection.GetNextFileKey() , SELF);    collection.AddOld( F );  RETURN F END
 			END;
 			IF TraceFileSystem IN Trace THEN KernelLog.String( "failed" );  KernelLog.Ln;  END;
@@ -276,7 +273,7 @@ TYPE
 					Fo := fold( File );
 					IF ~Fo.ToTemp() THEN RETURN END;
 					ret := Kernel32.CopyFile( Fo.tfname^, fnnew, 0 )
-				ELSE ret := Kernel32.MoveFileEx( fnold, fnnew, {Kernel32.MoveFileReplaceExisting, Kernel32.MoveFileCopyAllowed} )
+				ELSE ret := Kernel32.MoveFileEx( fnold, fnnew, Kernel32.SetToDW({Kernel32.MoveFileReplaceExisting, Kernel32.MoveFileCopyAllowed}) )
 				END;
 				IF ret # 0 THEN res := 0 END
 			ELSIF TraceFileSystem IN Trace THEN KernelLog.String( "Rename failed :" );  KernelLog.String( fnold );  KernelLog.String( " => " );  KernelLog.String( fnnew );  KernelLog.Ln;
@@ -308,10 +305,10 @@ TYPE
 							d := LONG( st.wYear - 1900 ) * 200H + LONG( st.wMonth ) * 20H + LONG( st.wDay );  t := LONG( st.wHour ) * 1000H + LONG( st.wMinute ) * 40H + LONG( st.wSecond );
 						END;
 						Join( curPath, "/", FD.cFileName, longname );
-						IF ~(Kernel32.FileAttributeDirectory IN FD.dwFileAttributes) THEN
-							enum.PutEntry( longname, {}, t, d, BESTSIZE(UNSIGNED64( FD.nFileSizeHigh ) * UNSIGNED64( 0x100000000 ) + UNSIGNED64( FD.nFileSizeLow )))
+						IF ~(Kernel32.FileAttributeDirectory IN Kernel32.DWToSet(FD.dwFileAttributes)) THEN
+							enum.PutEntry( longname, {}, t, d, HUGEINT(UNSIGNED64( FD.nFileSizeHigh ) * UNSIGNED64( 0x100000000 ) + UNSIGNED64( FD.nFileSizeLow )))
 						ELSIF (FD.cFileName # ".") & (FD.cFileName # "..") THEN
-							enum.PutEntry( longname, {Files.Directory}, t, d, BESTSIZE(UNSIGNED64( FD.nFileSizeHigh ) * UNSIGNED64( 0x100000000 ) + UNSIGNED64( FD.nFileSizeLow )))
+							enum.PutEntry( longname, {Files.Directory}, t, d, HUGEINT(UNSIGNED64( FD.nFileSizeHigh ) * UNSIGNED64( 0x100000000 ) + UNSIGNED64( FD.nFileSizeLow )))
 						END;
 					UNTIL Kernel32.FindNextFile( h, FD ) = Kernel32.False;
 					Kernel32.FindClose( h )
@@ -319,7 +316,7 @@ TYPE
 			END EnumeratePath;
 
 		BEGIN {EXCLUSIVE}
-			COPY( mask, path );  ConvertChar( path, Files.PathDelimiter, PathDelimiter );  attr := Kernel32.GetFileAttributes( path );  path := "";
+			COPY( mask, path );  ConvertChar( path, Files.PathDelimiter, PathDelimiter );  attr := ToSet(Kernel32.GetFileAttributes( path ));  path := "";
 			IF (Kernel32.FileAttributeDirectory IN attr) & (~(Kernel32.FileAttributeTemporary IN attr)) THEN COPY( mask, path );  COPY( "*", pattern );  ELSE Files.SplitPath( mask, path, pattern );  END;
 			IF TraceFileSystem IN Trace THEN
 				KernelLog.String( "Enumerate0: " );   KernelLog.String( mask );  KernelLog.String( " :: " );  KernelLog.String( path );  KernelLog.String( " :: " );  KernelLog.String( pattern );  KernelLog.Ln;
@@ -393,9 +390,9 @@ TYPE
 		VAR ret: Kernel32.BOOL;
 		BEGIN {EXCLUSIVE}
 			ConvertChar( name, Files.PathDelimiter, PathDelimiter );  ret := Kernel32.CreateDirectory( name, NIL );
-			IF ret # 0 THEN 
+			IF ret # 0 THEN
 				res := 0;
-			ELSIF Kernel32.GetLastError() = 183 (*ERROR_ALREADY_EXISTS*) THEN 
+			ELSIF Kernel32.GetLastError() = 183 (*ERROR_ALREADY_EXISTS*) THEN
 				res := Files.FileAlreadyExists;
 			ELSE
 				res := 1
@@ -422,10 +419,10 @@ TYPE
 		VAR name: FileName;
 		BEGIN
 			COPY(fileName, name);
-			ConvertChar(name, Files.PathDelimiter, PathDelimiter );  
-			IF FindFile(fileName, fullName) THEN 
-				flags := FileFlags(Kernel32.GetFileAttributes(fullName));
-				ConvertChar(fullName, PathDelimiter,Files.PathDelimiter);  
+			ConvertChar(name, Files.PathDelimiter, PathDelimiter );
+			IF FindFile(fileName, fullName) THEN
+				flags := FileFlags(ToSet(Kernel32.GetFileAttributes(fullName)));
+				ConvertChar(fullName, PathDelimiter,Files.PathDelimiter);
 				RETURN TRUE
 			ELSE
 				RETURN FALSE
@@ -455,9 +452,9 @@ TYPE
 			IF TraceFile IN Trace THEN KernelLog.String( "Init: " );  KernelLog.String( name );  KernelLog.String( " (" );  KernelLog.Int( key, 1 );  KernelLog.String( ")" );  KernelLog.Ln;  END;
 			SELF.key := key;  fpos := 0;  SELF.hfile := hfile;  COPY( name, SELF.fname );  tfname := NIL;
 			IF hfile # Kernel32.InvalidHandleValue THEN
-				ASSERT(Kernel32.GetFileSizeEx(hfile, fsize) # Kernel32.False);
-				s := Kernel32.GetFileAttributes( name );
-				IF Kernel32.FileAttributeTemporary IN s THEN EXCL( s, Kernel32.FileAttributeTemporary );  res := Kernel32.SetFileAttributes( name, s );  ASSERT( res # 0 );  s := Kernel32.GetFileAttributes( name ) END;
+				ASSERT(Kernel32.GetFileSizeEx(hfile, fsize) # Kernel32.False); 
+				s := ToSet(Kernel32.GetFileAttributes( name ));
+				IF Kernel32.FileAttributeTemporary IN s THEN EXCL( s, Kernel32.FileAttributeTemporary );  res := Kernel32.SetFileAttributes( name, Kernel32.SetToDW(s) );  ASSERT( res # 0 );  s := ToSet(Kernel32.GetFileAttributes( name )) END;
 				flags := FileFlags( s )
 			ELSE flags := {Temporary};  fsize := 0
 			END;
@@ -466,7 +463,7 @@ TYPE
 			fileSystem := fs
 		END Init;
 
-		PROCEDURE Set( VAR r: Files.Rider;  pos: BESTSIZE );
+		PROCEDURE Set( VAR r: Files.Rider;  pos: HUGEINT );
 		VAR size: HUGEINT;
 		BEGIN {EXCLUSIVE}
 			IF hfile # Kernel32.InvalidHandleValue THEN
@@ -480,7 +477,7 @@ TYPE
 			r.apos := pos DIV BufferSize;  r.bpos := LONGINT( pos MOD BufferSize )
 		END Set;
 
-		PROCEDURE Pos( VAR r: Files.Rider ): BESTSIZE;
+		PROCEDURE Pos( VAR r: Files.Rider ): HUGEINT;
 		BEGIN
 			RETURN r.apos * BufferSize + r.bpos
 		END Pos;
@@ -488,11 +485,11 @@ TYPE
 		PROCEDURE WriteBuffer;
 		VAR pos: HUGEINT; n: LONGINT; res, b: Kernel32.BOOL;
 		BEGIN
-			ASSERT( buffer.dirty );  ASSERT( buffer.len > 0 );  
+			ASSERT( buffer.dirty );  ASSERT( buffer.len > 0 );
 			pos := buffer.apos * BufferSize;
 			IF hfile = Kernel32.InvalidHandleValue THEN
 				ASSERT( Temporary IN flags );  NEW( tfname );  TempName( tfname^ );
-				hfile := Kernel32.CreateFile( tfname^, {Kernel32.GenericRead, Kernel32.GenericWrite}, {Kernel32.FileShareRead}, NIL , Kernel32.CreateAlways, {Kernel32.FileAttributeTemporary}, 0 );
+				hfile := Kernel32.CreateFile( tfname^, Kernel32.SetToDW({Kernel32.GenericRead, Kernel32.GenericWrite}), Kernel32.SetToDW({Kernel32.FileShareRead}), NIL , Kernel32.CreateAlways, Kernel32.SetToDW({Kernel32.FileAttributeTemporary}), 0 );
 				ASSERT( hfile # Kernel32.InvalidHandleValue );  fpos := 0
 			END;
 			IF fpos # pos THEN ASSERT( Kernel32.SetFilePointerEx( hfile, pos, fpos , Kernel32.FileBegin ) # Kernel32.False ); END;
@@ -501,16 +498,16 @@ TYPE
 				res := Kernel32.CloseHandle( hfile );
 				IF TraceFile IN Trace THEN KernelLog.String( "closed handle of " );  KernelLog.String( fname );  KernelLog.Ln;  END;
 				hfile :=
-					Kernel32.CreateFile( fname, {Kernel32.GenericRead, Kernel32.GenericWrite}, {Kernel32.FileShareRead, Kernel32.FileShareWrite}, NIL , Kernel32.OpenExisting, {Kernel32.FileAttributeNormal}, 0 );
+					Kernel32.CreateFile( fname, Kernel32.SetToDW({Kernel32.GenericRead, Kernel32.GenericWrite}), Kernel32.SetToDW({Kernel32.FileShareRead, Kernel32.FileShareWrite}), NIL , Kernel32.OpenExisting, Kernel32.SetToDW({Kernel32.FileAttributeNormal}), 0 );
 				ASSERT( hfile # Kernel32.InvalidHandleValue );
 				ASSERT( Kernel32.SetFilePointerEx( hfile, pos, fpos , Kernel32.FileBegin ) # Kernel32.False );
 				res := Kernel32.WriteFile( hfile, buffer.data, buffer.len, n, NIL )
 			END;
-			ASSERT( (res # Kernel32.False) & (n = buffer.len) );  
+			ASSERT( (res # Kernel32.False) & (n = buffer.len) );
 			INC( fpos, n );  buffer.dirty := FALSE
 		END WriteBuffer;
 
-		PROCEDURE ReadBuffer( apos: BESTSIZE );
+		PROCEDURE ReadBuffer( apos: HUGEINT );
 		VAR pos: HUGEINT; n: LONGINT;  res, b: Kernel32.BOOL;
 		BEGIN
 			IF buffer.dirty THEN WriteBuffer() END;
@@ -527,7 +524,7 @@ TYPE
 		END ReadBuffer;
 
 		PROCEDURE Read( VAR r: Files.Rider;  VAR x: CHAR );
-		VAR pos: BESTSIZE;
+		VAR pos: HUGEINT;
 		BEGIN {EXCLUSIVE}
 			pos := r.apos * BufferSize + r.bpos;
 			IF pos < fsize THEN
@@ -539,7 +536,7 @@ TYPE
 		END Read;
 
 		PROCEDURE ReadBytes( VAR r: Files.Rider;  VAR x: ARRAY OF CHAR;  ofs, len: LONGINT );
-		VAR pos: BESTSIZE; n: LONGINT;
+		VAR pos: HUGEINT; n: LONGINT;
 		BEGIN {EXCLUSIVE}
 			ASSERT( (ofs + len) <= LEN( x ) );
 			pos := r.apos * BufferSize + r.bpos;
@@ -559,7 +556,7 @@ TYPE
 		END ReadBytes;
 
 		PROCEDURE Write( VAR r: Files.Rider;  x: CHAR );
-		VAR pos: BESTSIZE;
+		VAR pos: HUGEINT;
 		BEGIN {EXCLUSIVE}
 			pos := r.apos * BufferSize + r.bpos;
 			IF buffer.apos # r.apos THEN ReadBuffer( r.apos ) END;
@@ -571,7 +568,7 @@ TYPE
 		END Write;
 
 		PROCEDURE WriteBytes( VAR r: Files.Rider;  CONST x: ARRAY OF CHAR;  ofs, len: LONGINT );
-		VAR pos: BESTSIZE; n: LONGINT;
+		VAR pos: HUGEINT; n: LONGINT;
 		BEGIN {EXCLUSIVE}
 			IF len = 0 THEN RETURN END;
 			ASSERT( (len > 0) & ((ofs + len) <= LEN( x )) );  pos := r.apos * BufferSize + r.bpos;
@@ -587,7 +584,7 @@ TYPE
 			END
 		END WriteBytes;
 
-		PROCEDURE Length( ): BESTSIZE;
+		PROCEDURE Length( ): HUGEINT;
 		BEGIN {EXCLUSIVE}
 			RETURN fsize
 		END Length;
@@ -616,7 +613,7 @@ TYPE
 		PROCEDURE GetAttributes(): SET;
 		VAR s: SET;
 		BEGIN
-			s := Kernel32.GetFileAttributes( fname );
+			s := ToSet(Kernel32.GetFileAttributes( fname ));
 			RETURN FileFlags(s);
 		END GetAttributes;
 
@@ -644,10 +641,10 @@ TYPE
 			ASSERT( ~(Temporary IN flags) );
 			(*ALEX 2005.12.08*)
 			IF hfile = Kernel32.InvalidHandleValue THEN
-				hfile := Kernel32.CreateFile( fname, {Kernel32.GenericRead}, {Kernel32.FileShareRead, Kernel32.FileShareWrite}, NIL , Kernel32.CreateAlways, {Kernel32.FileAttributeNormal}, 0 );
+				hfile := Kernel32.CreateFile( fname, Kernel32.SetToDW({Kernel32.GenericRead}), Kernel32.SetToDW({Kernel32.FileShareRead, Kernel32.FileShareWrite}), NIL , Kernel32.CreateAlways, Kernel32.SetToDW({Kernel32.FileAttributeNormal}), 0 );
 			END;
 			IF hfile = Kernel32.InvalidHandleValue THEN
-				hfile := Kernel32.CreateFile( fname, {Kernel32.GenericRead}, {Kernel32.FileShareRead, Kernel32.FileShareWrite}, NIL , Kernel32.OpenExisting, {Kernel32.FileAttributeNormal}, 0 );
+				hfile := Kernel32.CreateFile( fname, Kernel32.SetToDW({Kernel32.GenericRead}), Kernel32.SetToDW({Kernel32.FileShareRead, Kernel32.FileShareWrite}), NIL , Kernel32.OpenExisting, Kernel32.SetToDW({Kernel32.FileAttributeNormal}), 0 );
 			END;
 			IF buffer.dirty THEN WriteBuffer() END;
 			(*
@@ -655,7 +652,7 @@ TYPE
 			*)
 			ASSERT(hfile # Kernel32.InvalidHandleValue);
 			ASSERT(Kernel32.GetFileSizeEx(hfile, fsize) # Kernel32.False);
-			res := Kernel32.CloseHandle( hfile ); 
+			res := Kernel32.CloseHandle( hfile );
 			 hfile := Kernel32.InvalidHandleValue;
 			(*
 			END;
@@ -664,9 +661,9 @@ TYPE
 			IF TraceFile IN Trace THEN KernelLog.String( "toTemp: " );  KernelLog.String( fname );  KernelLog.String( " => " );  KernelLog.String( tfname^ );  KernelLog.Ln;  END;
 			IF ~MoveFile( fname, tfname^ ) THEN HALT( 1241 ) (* RETURN FALSE *) END;
 			winFS.collection.Unregister( SELF );
-			hfile := Kernel32.CreateFile( tfname^, {Kernel32.GenericRead, Kernel32.GenericWrite}, {Kernel32.FileShareRead}, NIL , Kernel32.OpenExisting, {Kernel32.FileAttributeTemporary}, 0 );
+			hfile := Kernel32.CreateFile( tfname^, Kernel32.SetToDW({Kernel32.GenericRead, Kernel32.GenericWrite}), Kernel32.SetToDW({Kernel32.FileShareRead}), NIL , Kernel32.OpenExisting, Kernel32.SetToDW({Kernel32.FileAttributeTemporary}), 0 );
 			(* IF hfile = Kernel32.InvalidHandleValue THEN RETURN FALSE END; *)
-			ASSERT( hfile # Kernel32.InvalidHandleValue );  
+			ASSERT( hfile # Kernel32.InvalidHandleValue );
 			ASSERT( Kernel32.GetFileSizeEx( hfile, fsize ) # Kernel32.False );
 			SELF.tfname := tfname;
 			COPY( tfname^, fname );
@@ -694,8 +691,8 @@ TYPE
 					res := 1;  RETURN;
 					(* HALT( 1242 )*)
 				END;
-				hfile := Kernel32.CreateFile( fname, {Kernel32.GenericRead, Kernel32.GenericWrite}, {Kernel32.FileShareRead, Kernel32.FileShareWrite}, NIL , Kernel32.OpenExisting, {Kernel32.FileAttributeNormal}, 0 )
-			ELSE hfile := Kernel32.CreateFile( fname, {Kernel32.GenericRead, Kernel32.GenericWrite}, {Kernel32.FileShareRead, Kernel32.FileShareWrite}, NIL , Kernel32.CreateAlways, {Kernel32.FileAttributeNormal}, 0 )
+				hfile := Kernel32.CreateFile( fname, Kernel32.SetToDW({Kernel32.GenericRead, Kernel32.GenericWrite}), Kernel32.SetToDW({Kernel32.FileShareRead, Kernel32.FileShareWrite}), NIL , Kernel32.OpenExisting, Kernel32.SetToDW({Kernel32.FileAttributeNormal}), 0 )
+			ELSE hfile := Kernel32.CreateFile( fname, Kernel32.SetToDW({Kernel32.GenericRead, Kernel32.GenericWrite}), Kernel32.SetToDW({Kernel32.FileShareRead, Kernel32.FileShareWrite}), NIL , Kernel32.CreateAlways, Kernel32.SetToDW({Kernel32.FileAttributeNormal}), 0 )
 			END;
 			IF hfile = Kernel32.InvalidHandleValue THEN res := 1;  RETURN END;
 			ASSERT( hfile # Kernel32.InvalidHandleValue );  winFS.collection.Register( SELF );  res := 0
@@ -739,6 +736,12 @@ VAR
 	winFS: WinFileSystem;   (* must be unique *)
 
 
+	PROCEDURE ToSet(d: UNSIGNED32): SET;
+	BEGIN
+		RETURN SYSTEM.VAL(SET, ADDRESS(d)); 
+	END ToSet;
+	
+
 	PROCEDURE DebugFile(f: File);
 	BEGIN
 		KernelLog.String("fname = "); KernelLog.String(f.fname); KernelLog.Ln;
@@ -767,7 +770,7 @@ VAR
 
 	PROCEDURE MoveFile( VAR from, to: ARRAY OF CHAR ): BOOLEAN;
 	BEGIN
-		IF Kernel32.MoveFileEx( from, to, {Kernel32.MoveFileReplaceExisting, Kernel32.MoveFileCopyAllowed} ) = Kernel32.False THEN
+		IF Kernel32.MoveFileEx( from, to, Kernel32.SetToDW({Kernel32.MoveFileReplaceExisting, Kernel32.MoveFileCopyAllowed}) ) = Kernel32.False THEN
 			IF Kernel32.CopyFile( from, to, Kernel32.False ) = Kernel32.False THEN
 				IF TraceFile IN Trace THEN KernelLog.String( "could not copy" );  KernelLog.Ln;  END;
 				RETURN FALSE
@@ -987,10 +990,10 @@ VAR
 				i := 2;
 				REPEAT ch := dir[i]; dir[i-2] := ch; INC(i) UNTIL ch = 0X;
 			ELSE
-				Kernel32.SetCurrentDirectory(workPath) 
+				Kernel32.SetCurrentDirectory(workPath)
 			END;
 		END SetSysPath;
-		
+
 
 		PROCEDURE AddDir;
 		BEGIN
@@ -1041,7 +1044,7 @@ VAR
 
 		COPY( temp, tempPath );
 		IF tempPath # "" THEN
-			ConvertChar( tempPath, Files.PathDelimiter, PathDelimiter );  
+			ConvertChar( tempPath, Files.PathDelimiter, PathDelimiter );
 			SetSysPath(tempPath);
 			IF Kernel32.SetCurrentDirectory( tempPath ) # Kernel32.False THEN ret := Kernel32.GetCurrentDirectory( LEN( tempPath ), tempPath ) END
 		END;
@@ -1049,7 +1052,7 @@ VAR
 
 		COPY( work, dir );
 		IF dir # "" THEN
-			ConvertChar( dir, Files.PathDelimiter, PathDelimiter );  
+			ConvertChar( dir, Files.PathDelimiter, PathDelimiter );
 			SetSysPath(dir);
 			IF Kernel32.SetCurrentDirectory( dir ) # Kernel32.False THEN Kernel32.GetCurrentDirectory( LEN( workPath ), workPath ) END
 		END;
@@ -1080,7 +1083,7 @@ VAR
 	END CheckPath;
 
 	PROCEDURE CheckName*( name: ARRAY OF CHAR ): BOOLEAN;
-	VAR fullName: FileName;  fileNamePart: Kernel32.LPSTR;  ret, i: LONGINT;  ch: CHAR;  stream, ok: BOOLEAN;
+	VAR fullName: FileName;  fileNamePart: Kernel32.LPSTR;  ret, i: SIZE;  ch: CHAR;  stream, ok: BOOLEAN;
 	BEGIN
 		ConvertChar( name, Files.PathDelimiter, PathDelimiter );  ret := Kernel32.GetFullPathName( name, Kernel32.MaxPath, fullName, fileNamePart );
 		IF (ret > 0) & CheckPath( fullName ) & (fileNamePart # Kernel32.NULL) THEN
@@ -1102,18 +1105,18 @@ VAR
 	PROCEDURE GetAttributes*( file: ARRAY OF CHAR ): SET;   (** non-portable *)
 	VAR attrs: SET;
 	BEGIN
-		ConvertChar( file, Files.PathDelimiter, PathDelimiter );  attrs := Kernel32.GetFileAttributes( file );
+		ConvertChar( file, Files.PathDelimiter, PathDelimiter );  attrs := ToSet(Kernel32.GetFileAttributes( file ));
 		IF attrs = {0..31} THEN RETURN {} ELSE RETURN attrs END
 	END GetAttributes;
 
 	PROCEDURE SetAttributes*( file: ARRAY OF CHAR;  attrs: SET );   (** non-portable *)
 	BEGIN
-		ConvertChar( file, Files.PathDelimiter, PathDelimiter );  Kernel32.SetFileAttributes( file, attrs )
+		ConvertChar( file, Files.PathDelimiter, PathDelimiter );  Kernel32.SetFileAttributes( file, Kernel32.SetToDW(attrs) )
 	END SetAttributes;
 
 	PROCEDURE SetFileAttributes*( file: ARRAY OF CHAR;  attrs: SET );   (** non-portable *)
 	BEGIN
-		ConvertChar( file, Files.PathDelimiter, PathDelimiter );  Kernel32.SetFileAttributes( file, attrs )
+		ConvertChar( file, Files.PathDelimiter, PathDelimiter );  Kernel32.SetFileAttributes( file, Kernel32.SetToDW(attrs) )
 	END SetFileAttributes;
 
 
@@ -1213,7 +1216,7 @@ VAR
 
 		(* now the file system is installed *)
 
-		drives := Kernel32.GetLogicalDrives();
+		drives := ToSet(Kernel32.GetLogicalDrives());
 		drives := drives - {0,1}; (* do not scan for diskettes *)
 		AutoMountWindowsLogicalDrives( drives );
 
@@ -1228,25 +1231,38 @@ VAR
 
 		notifications := NIL;
 	END Init;
-	
+
 	PROCEDURE AddSearchPath*(context: Commands.Context);
-	VAR name: FileName; i,j: LONGINT; ch : CHAR;
+	VAR name,fullName: FileName; i,j: LONGINT; ch : CHAR; ret: LONGINT; fileNamePart: Kernel32.LPSTR;
 	BEGIN
 		IF context.arg.GetString(name) THEN
 			i := 0; j := 0;
 			ConvertChar( name, Files.PathDelimiter, PathDelimiter );
+			ret := Kernel32.GetFullPathName( name, Kernel32.MaxPath, fullName, fileNamePart );
+
 			WHILE(searchPath[i] # 0X) DO
 				INC(i);
 			END;
+
 			searchPath[i] := ";";INC(i);
 			REPEAT
-				ch := name[j];
-				searchPath[i] := name[j];
+				ch := fullName[j];
+				searchPath[i] := ch;
 				INC(j);INC(i);
 			UNTIL ch = 0X;
 		END;
 	END AddSearchPath;
-	
+
+	PROCEDURE SetWorkPath*(context: Commands.Context);
+	VAR name: FileName; done: BOOLEAN;
+	BEGIN
+		IF context.arg.GetString(name) THEN
+			ChangeDirectory(name, done); 
+			IF ~done THEN 
+				context.error.String("could not change directory to "); context.error.String(name); context.error.Ln; 
+			END
+		END;
+	END SetWorkPath;
 
 	PROCEDURE Mount*(context : Commands.Context);
 	VAR diskname: ARRAY 256 OF CHAR;