	fileInfo	: ARRAY [0..15] OF RECORD
				ptr : cardinal32;
				block : cardinal32;
				modified : BOOLEAN;
				buffer : ARRAY [0..BLKSZ-1] OF CHAR;
			    END;

PROCEDURE open(
	name : seid;
	mode : setOfOpenModes;
	capability : openDescriptor
	): openDescriptor;
VAR
	e : exceptionType;
	od : openDescriptor;
BEGIN
	IF (name.nsp = terminalNsp) OR
	   (name.nsp = deviceNsp) OR
	   (name.nsp = subtypeNsp) THEN
		e := KsetObjectLevel(name, level);
		IF e # XOK THEN
			typeString(stdout, 'Error ');
			typeBasic(stdout, e, 10);
			typeString(stdout, 'while setting level of SEID ');
			typeBasic(stdout, CARDINAL(name.nsp), 10);
			typeBasic(stdout, name.uniqId0, 10);
			typeBasic(stdout, name.uniqId1, 10);
			typeString(stdout, CRLF);
		END;
		e := KsetDa(name, discrAccess{groupRead,groupWrite,ownerRead,ownerWrite});
		IF e # XOK THEN
			typeString(stdout, 'Error ');
			typeBasic(stdout, e, 10);
			typeString(stdout, 'while setting da of SEID');
			typeBasic(stdout, CARDINAL(name.nsp), 10);
			typeBasic(stdout, name.uniqId0, 10);
			typeBasic(stdout, name.uniqId1, 10);
			typeString(stdout, CRLF);
		END;
	END;
	od := nullOpenDescriptor;
	e := Kopen(name, mode, capability, od);
	IF e = XOK THEN
		fileInfo[od].ptr := 0;
		fileInfo[od].modified := FALSE;
		fileInfo[od].block := maxCardinal32;
		IF stdout # nullOpenDescriptor THEN
			typeString(stdout, 'SEID ');
			typeBasic(stdout, CARDINAL(name.nsp), 10);
			typeBasic(stdout, name.uniqId0, 10);
			typeBasic(stdout, name.uniqId1, 16);
			typeString(stdout, 'open on file ');
			typeBasic(stdout, od, 10);
			typeString(stdout, CRLF);
		END;
	ELSE
		typeString(stdout, 'Error ');
		typeBasic(stdout, e, 10);
		typeString(stdout, 'while opening SEID ');
		typeBasic(stdout, CARDINAL(name.nsp), 10);
		typeBasic(stdout, name.uniqId0, 10);
		typeBasic(stdout, name.uniqId1, 16);
		typeString(stdout, CRLF);
		od := nullOpenDescriptor;
	END;
	RETURN od;
END open;

PROCEDURE flush(
	od : openDescriptor
	);
VAR
	pb : pBlock;
	e : exceptionType;
	iostat : ioStatus;
	bytesDone : cardinal16;
BEGIN
	WITH fileInfo[od] DO
		IF modified THEN
			WITH pb.location DO
				domain := nullDomain;
				space := dSpace;
				address := ADR(buffer);
			END;
			pb.size := SIZE(buffer);
(****
			typeString(stdout, 'test_flush: Writing block ');
			typeBasic(stdout, block, 10);
			typeString(stdout, 'on file ');
			typeBasic(stdout, od, 10);
			typeString(stdout, CRLF);
****)
			e := KwriteBlock(od, fBlockNumber(block), pb, asyncId(0), iostat);
			IF (e # XOK) OR (iostat.devIndep # XOK) THEN
				typeString(stdout, 'Error ');
				typeBasic(stdout, e, 10);
				typeString(stdout, '(iostat = ');
				typeBasic(stdout, iostat.devIndep, 10);
				typeString(stdout, ', ');
				typeBasic(stdout, cardinal32(iostat.devDep), 16);
				typeString(stdout, ') while writing file ');
				typeBasic(stdout, od, 10);
				typeString(stdout, CRLF);
			END;
			modified := FALSE;
		END;
	END;
END flush;

PROCEDURE load(
	od : openDescriptor;
	newBlock : cardinal32
	);
VAR
	pb : pBlock;
	e : exceptionType;
	iostat : ioStatus;
	bytesDone : cardinal16;
BEGIN
	flush(od);
	WITH fileInfo[od] DO
		block := newBlock;
		WITH pb.location DO
			domain := nullDomain;
			space := dSpace;
			address := ADR(buffer);
		END;
		pb.size := SIZE(buffer);
(****
		typeString(stdout, 'test_load: Reading block ');
		typeBasic(stdout, block, 10);
		typeString(stdout, 'on file ');
		typeBasic(stdout, od, 10);
		typeString(stdout, CRLF);
****)
		e := KreadBlock(od, fBlockNumber(block), pb, asyncId(0), iostat, bytesDone);
		IF (e # XOK) OR (iostat.devIndep # XOK) OR (bytesDone # BLKSZ) THEN
			typeString(stdout, 'Error ');
			typeBasic(stdout, e, 10);
			typeString(stdout, '(iostat = ');
			typeBasic(stdout, iostat.devIndep, 10);
			typeString(stdout, ', ');
			typeBasic(stdout, cardinal32(iostat.devDep), 16);
			typeString(stdout, ') (bytesDone = ');
			typeBasic(stdout, bytesDone, 10);
			typeString(stdout, ') while reading file ');
			typeBasic(stdout, od, 10);
			typeString(stdout, CRLF);
			block := maxCardinal32;
		END;
	END;
END load;

PROCEDURE read(
	od : openDescriptor;
	addr : virtAddr;
	len : cardinal16
	): integer32;
VAR
	ret : integer32;
	dest : charPointer;
BEGIN
	ret := 0;
	dest := addr;
	WITH fileInfo[od] DO
		WHILE (len > 0) AND (ret >= 0) DO
			IF ptr DIV BLKSZ # block THEN
				load(od, ptr DIV BLKSZ);
			END;
			IF ptr DIV BLKSZ = block THEN
				dest^ := buffer[ptr MOD BLKSZ];
				dest := charPointer(CARDINAL(dest) + 1);
				INC(ptr);
				INC(ret);
				DEC(len);
			ELSE
				ret := -1;
			END;
		END;
	END;
	RETURN ret;
END read;

PROCEDURE write(
	od : openDescriptor;
	addr : virtAddr;
	len : cardinal16
	): integer32;
VAR
	ret : integer32;
	src : charPointer;
BEGIN
	ret := 0;
	src := addr;
	WITH fileInfo[od] DO
		WHILE (len > 0) AND (ret >= 0) DO
			IF ptr DIV BLKSZ # block THEN
				load(od, ptr DIV BLKSZ);
			END;
			IF ptr DIV BLKSZ = block THEN
				buffer[ptr MOD BLKSZ] := src^;
				modified := TRUE;
				src := charPointer(CARDINAL(src) + 1);
				INC(ptr);
				INC(ret);
				DEC(len);
			ELSE
				ret := -1;
			END;
		END;
	END;
	RETURN ret;
END write;

PROCEDURE seek(
	od : openDescriptor;
	pos : cardinal32
	);
BEGIN
	fileInfo[od].ptr := pos;
END seek;

PROCEDURE close(
	od : openDescriptor
	);
VAR
	e : exceptionType;
BEGIN
	flush(od);
	e := Kclose(od);
	IF e = XOK THEN
		IF od = stdout THEN
			stdout := nullOpenDescriptor;
		END;
		IF stdout # nullOpenDescriptor THEN
			typeString(stdout, 'File ');
			typeBasic(stdout, od, 10);
			typeLine(stdout, 'closed.');
		END;
	ELSE
		IF stdout = nullOpenDescriptor THEN
			userFDT(e);
		ELSE
			typeString(stdout, 'Error ');
			typeBasic(stdout, e, 10);
			typeLine(stdout, 'while closing file ');
			typeBasic(stdout, od, 10);
			typeString(stdout, CRLF);
		END;
	END;
END close;
