Oberon/V2/Files
Appearance
< Oberon
MODULE Files; (*NW 11.1.86 / 22.1.91*)
IMPORT SYSTEM, Kernel, FileDir;
(*A file consists of a sequence of pages. The first page contains the header.
Part of the header is the page table, an array of disk addresses to the pages.
A file is referenced through riders each of which indicate a position.*)
CONST MaxBufs = 4;
HS = FileDir.HeaderSize;
SS = FileDir.SectorSize;
STS = FileDir.SecTabSize;
XS = FileDir.IndexSize;
TYPE DiskAdr = LONGINT;
File* = POINTER TO Handle;
Buffer = POINTER TO BufferRecord;
FileHd = POINTER TO FileDir.FileHeader;
Index = POINTER TO IndexRecord;
Rider* =
RECORD eof*: BOOLEAN;
res*: LONGINT;
file: File;
apos, bpos: INTEGER;
buf: Buffer;
unused: LONGINT
END ;
Handle =
RECORD next: File;
aleng, bleng: INTEGER;
nofbufs: INTEGER;
modH: BOOLEAN;
firstbuf: Buffer;
sechint: DiskAdr;
name: FileDir.FileName;
time, date: LONGINT;
unused: ARRAY 1 OF LONGINT;
ext: ARRAY FileDir.ExTabSize OF Index;
sec: FileDir.SectorTable
END ;
BufferRecord =
RECORD apos, lim: INTEGER;
mod: BOOLEAN;
next: Buffer;
data: FileDir.DataSector
END ;
IndexRecord =
RECORD adr: DiskAdr;
mod: BOOLEAN;
sec: FileDir.IndexSector
END ;
(*aleng * SS + bleng = length (including header)
apos * SS + bpos = current position
0 <= bpos <= lim <= SS
0 <= apos <= aleng < PgTabSize
(apos < aleng) & (lim = SS) OR (apos = aleng) *)
VAR root: File; (*list of open files*)
PROCEDURE Check(VAR s: ARRAY OF CHAR;
VAR name: FileDir.FileName; VAR res: INTEGER);
VAR i: INTEGER; ch: CHAR;
BEGIN ch := s[0]; i := 0;
IF ("A" <= CAP(ch)) & (CAP(ch) <= "Z") THEN
LOOP name[i] := ch; INC(i); ch := s[i];
IF ch = 0X THEN
WHILE i < FileDir.FnLength DO name[i] := 0X; INC(i) END ;
res := 0; EXIT
END ;
IF ~(("A" <= CAP(ch)) & (CAP(ch) <= "Z")
OR ("0" <= ch) & (ch <= "9") OR (ch = ".")) THEN res := 3; EXIT
END ;
IF i = FileDir.FnLength THEN res := 4; EXIT END ;
END
ELSIF ch = 0X THEN name[0] := 0X; res := -1
ELSE res := 3
END
END Check;
PROCEDURE Old*(name: ARRAY OF CHAR): File;
VAR i, k, res: INTEGER;
f: File;
header: DiskAdr;
buf: Buffer;
head: FileHd;
namebuf: FileDir.FileName;
inxpg: Index;
BEGIN f := NIL; Check(name, namebuf, res);
IF res = 0 THEN
FileDir.Search(namebuf, header);
IF header # 0 THEN f := root;
WHILE (f # NIL) & (f.sec[0] # header) DO f := f.next END ;
IF f = NIL THEN
NEW(buf); buf.apos := 0; buf.next := buf; buf.mod := FALSE;
head := SYSTEM.VAL(FileHd, SYSTEM.ADR(buf.data));
Kernel.GetSector(header, head^);
NEW(f); f.aleng := head.aleng; f.bleng := head.bleng;
f.time := head.time; f.date := head.date;
IF f.aleng = 0 THEN buf.lim := f.bleng ELSE buf.lim := SS END ;
f.firstbuf := buf; f.nofbufs := 1; f.name[0] := 0X;
f.sec := head.sec;
k := (f.aleng + (XS-STS)) DIV XS; i := 0;
WHILE i < k DO
NEW(inxpg); inxpg.adr := head.ext[i]; inxpg.mod := FALSE;
Kernel.GetSector(inxpg.adr, inxpg.sec); f.ext[i] := inxpg; INC(i)
END ;
WHILE i < FileDir.ExTabSize DO f.ext[i] := NIL; INC(i) END ;
f.sechint := header; f.modH := FALSE; f.next := root; root := f
END
END
END ;
RETURN f
END Old;
PROCEDURE New*(name: ARRAY OF CHAR): File;
VAR i, res: INTEGER;
f: File;
header: DiskAdr;
buf: Buffer;
head: FileHd;
namebuf: FileDir.FileName;
BEGIN f := NIL; Check(name, namebuf, res);
IF res <= 0 THEN
NEW(buf); buf.apos := 0; buf.mod := FALSE; buf.lim := HS; buf.next := buf;
head := SYSTEM.VAL(FileHd, SYSTEM.ADR(buf.data));
head.mark := FileDir.HeaderMark;
head.aleng := 0; head.bleng := HS; head.name := namebuf;
Kernel.GetClock(head.time, head.date);
NEW(f); f.aleng := 0; f.bleng := HS; f.modH := TRUE;
f.time := head.time; f.date := head.date;
f.firstbuf := buf; f.nofbufs := 1; f.name := namebuf; f.sechint := 0;
i := 0;
REPEAT f.ext[i] := NIL; head.ext[i] := 0; INC(i) UNTIL i = FileDir.ExTabSize;
i := 0;
REPEAT f.sec[i] := 0; head.sec[i] := 0; INC(i) UNTIL i = STS
END ;
RETURN f
END New;
PROCEDURE UpdateHeader(f: File; VAR h: FileDir.FileHeader);
VAR k: INTEGER;
BEGIN h.aleng := f.aleng; h.bleng := f.bleng;
h.sec := f.sec; k := (f.aleng + (XS-STS)) DIV XS;
WHILE k > 0 DO DEC(k); h.ext[k] := f.ext[k].adr END
END UpdateHeader;
PROCEDURE ReadBuf(f: File; buf: Buffer; pos: INTEGER);
VAR sec: DiskAdr;
BEGIN
IF pos < STS THEN sec := f.sec[pos]
ELSE sec := f.ext[(pos-STS) DIV XS].sec.x[(pos-STS) MOD XS]
END ;
Kernel.GetSector(sec, buf.data);
IF pos < f.aleng THEN buf.lim := SS ELSE buf.lim := f.bleng END ;
buf.apos := pos; buf.mod := FALSE
END ReadBuf;
PROCEDURE WriteBuf(f: File; buf: Buffer);
VAR i, k: INTEGER;
secadr: DiskAdr; inx: Index;
BEGIN
IF buf.apos < STS THEN
secadr := f.sec[buf.apos];
IF secadr = 0 THEN
Kernel.AllocSector(f.sechint, secadr);
f.modH := TRUE; f.sec[buf.apos] := secadr; f.sechint := secadr
END ;
IF buf.apos = 0 THEN
UpdateHeader(f, SYSTEM.VAL(FileDir.FileHeader, buf.data)); f.modH := FALSE
END
ELSE i := (buf.apos - STS) DIV XS; inx := f.ext[i];
IF inx = NIL THEN
NEW(inx); inx.adr := 0; inx.sec.x[0] := 0; f.ext[i] := inx; f.modH := TRUE
END ;
k := (buf.apos - STS) MOD XS; secadr := inx.sec.x[k];
IF secadr = 0 THEN
Kernel.AllocSector(f.sechint, secadr);
f.modH := TRUE; inx.mod := TRUE; inx.sec.x[k] := secadr; f.sechint := secadr
END
END ;
Kernel.PutSector(secadr, buf.data); buf.mod := FALSE
END WriteBuf;
PROCEDURE Buf(f: File; pos: INTEGER): Buffer;
VAR buf: Buffer;
BEGIN buf := f.firstbuf;
LOOP
IF buf.apos = pos THEN EXIT END ;
buf := buf.next;
IF buf = f.firstbuf THEN buf := NIL; EXIT END
END ;
RETURN buf
END Buf;
PROCEDURE GetBuf(f: File; pos: INTEGER): Buffer;
VAR buf: Buffer;
BEGIN buf := f.firstbuf;
LOOP
IF buf.apos = pos THEN EXIT END ;
IF buf.next = f.firstbuf THEN
IF f.nofbufs < MaxBufs THEN (*allocate new buffer*)
NEW(buf); buf.next := f.firstbuf.next; f.firstbuf.next := buf;
INC(f.nofbufs)
ELSE (*take one of the buffers*) f.firstbuf := buf;
IF buf.mod THEN WriteBuf(f, buf) END
END ;
buf.apos := pos;
IF pos <= f.aleng THEN ReadBuf(f, buf, pos) END ;
EXIT
END ;
buf := buf.next
END ;
RETURN buf
END GetBuf;
PROCEDURE Unbuffer(f: File);
VAR i, k: INTEGER;
buf: Buffer;
inx: Index;
head: FileDir.FileHeader;
BEGIN buf := f.firstbuf;
REPEAT
IF buf.mod THEN WriteBuf(f, buf) END ;
buf := buf.next
UNTIL buf = f.firstbuf;
k := (f.aleng + (XS-STS)) DIV XS; i := 0;
WHILE i < k DO
inx := f.ext[i]; INC(i);
IF inx.mod THEN
IF inx.adr = 0 THEN
Kernel.AllocSector(f.sechint, inx.adr); f.sechint := inx.adr; f.modH := TRUE
END ;
Kernel.PutSector(inx.adr, inx.sec); inx.mod := FALSE
END
END ;
IF f.modH THEN
IF f.sec[0] = 0 THEN Kernel.AllocSector(0, f.sec[0]) END ;
Kernel.GetSector(f.sec[0], head); UpdateHeader(f, head);
Kernel.PutSector(f.sec[0], head); f.modH := FALSE
END
END Unbuffer;
PROCEDURE Register*(f: File);
BEGIN
IF (f # NIL) & (f.name[0] > 0X) THEN
Unbuffer(f); FileDir.Insert(f.name, f.sec[0]); f.next := root; root := f
END ;
END Register;
PROCEDURE Close*(f: File);
BEGIN
IF f # NIL THEN Unbuffer(f) END ;
END Close;
PROCEDURE Purge*(f: File);
VAR a, i, j, k: INTEGER;
ind: FileDir.IndexSector;
BEGIN
IF f # NIL THEN a := f.aleng + 1; f.aleng := 0;
IF a <= STS THEN i := a
ELSE i := STS; DEC(a, i);
j := (a-1) MOD XS; k := (a-1) DIV XS;
WHILE k >= 0 DO
Kernel.GetSector(f.ext[k].adr, ind);
REPEAT DEC(j); Kernel.FreeSector(ind.x[j])
UNTIL j = 0;
Kernel.FreeSector(f.ext[k].adr); j := XS; DEC(k)
END
END ;
REPEAT DEC(i); Kernel.FreeSector(f.sec[i])
UNTIL i = 0
END
END Purge;
PROCEDURE Length*(f: File): LONGINT;
BEGIN RETURN LONG(f.aleng)*SS + f.bleng - HS
END Length;
PROCEDURE GetDate*(f: File; VAR t, d: LONGINT);
BEGIN t := f.time; d := f.date
END GetDate;
PROCEDURE Set*(VAR r: Rider; f: File; pos: LONGINT);
VAR a, b: INTEGER;
BEGIN r.eof := FALSE; r.res := 0;
IF f # NIL THEN
IF pos < 0 THEN a := 0; b := HS
ELSIF pos < LONG(f.aleng)*SS + f.bleng - HS THEN
a := SHORT((pos + HS) DIV SS); b := SHORT((pos + HS) MOD SS);
ELSE a := f.aleng; b := f.bleng
END ;
r.file := f; r.apos := a; r.bpos := b; r.buf := f.firstbuf
ELSE r.file:= NIL
END
END Set;
PROCEDURE Read*(VAR r: Rider; VAR x: SYSTEM.BYTE);
VAR buf: Buffer;
BEGIN
IF r.apos # r.buf.apos THEN r.buf := GetBuf(r.file, r.apos) END ;
IF r.bpos < r.buf.lim THEN
x := r.buf.data.B[r.bpos]; INC(r.bpos)
ELSIF r.apos < r.file.aleng THEN
INC(r.apos); buf := Buf(r.file, r.apos);
IF buf = NIL THEN
IF r.buf.mod THEN WriteBuf(r.file, r.buf) END ;
ReadBuf(r.file, r.buf, r.apos)
ELSE r.buf := buf
END ;
x := r.buf.data.B[0]; r.bpos := 1
ELSE
x := 0X; r.eof := TRUE
END
END Read;
PROCEDURE ReadBytes*(VAR r: Rider; VAR x: ARRAY OF SYSTEM.BYTE; n: LONGINT);
VAR src, dst, m: LONGINT; buf: Buffer;
BEGIN dst := SYSTEM.ADR(x);
IF LEN(x) < n THEN HALT(25) END ;
IF r.apos # r.buf.apos THEN r.buf := GetBuf(r.file, r.apos) END ;
LOOP
IF n <= 0 THEN EXIT END ;
src := SYSTEM.ADR(r.buf.data.B) + r.bpos; m := r.bpos + n;
IF m <= r.buf.lim THEN
SYSTEM.MOVE(src, dst, n); r.bpos := SHORT(m); r.res := 0; EXIT
ELSIF r.buf.lim = SS THEN
m := r.buf.lim - r.bpos;
IF m > 0 THEN SYSTEM.MOVE(src, dst, m); INC(dst, m); DEC(n, m) END ;
IF r.apos < r.file.aleng THEN
INC(r.apos); r.bpos := 0; buf := Buf(r.file, r.apos);
IF buf = NIL THEN
IF r.buf.mod THEN WriteBuf(r.file, r.buf) END ;
ReadBuf(r.file, r.buf, r.apos)
ELSE r.buf := buf
END
ELSE r.res := n; r.eof := TRUE; EXIT
END
ELSE m := r.buf.lim - r.bpos;
IF m > 0 THEN SYSTEM.MOVE(src, dst, m); r.bpos := r.buf.lim END ;
r.res := n - m; r.eof := TRUE; EXIT
END
END
END ReadBytes;
PROCEDURE NewExt(f: File);
VAR i, k: INTEGER; ext: Index;
BEGIN k := (f.aleng - STS) DIV XS;
IF k = FileDir.ExTabSize THEN HALT(23) END ;
NEW(ext); ext.adr := 0; ext.mod := TRUE; f.ext[k] := ext; i := XS;
REPEAT DEC(i); ext.sec.x[i] := 0 UNTIL i = 0
END NewExt;
PROCEDURE Write*(VAR r: Rider; x: SYSTEM.BYTE);
VAR f: File; buf: Buffer;
BEGIN
IF r.apos # r.buf.apos THEN r.buf := GetBuf(r.file, r.apos) END ;
IF r.bpos >= r.buf.lim THEN
IF r.bpos < SS THEN
INC(r.buf.lim); INC(r.file.bleng); r.file.modH := TRUE
ELSE f := r.file; WriteBuf(f, r.buf); INC(r.apos); buf := Buf(r.file, r.apos);
IF buf = NIL THEN
IF r.apos <= f.aleng THEN ReadBuf(f, r.buf, r.apos)
ELSE r.buf.apos := r.apos; r.buf.lim := 1; INC(f.aleng); f.bleng := 1; f.modH := TRUE;
IF (f.aleng - STS) MOD XS = 0 THEN NewExt(f) END
END
ELSE r.buf := buf
END ;
r.bpos := 0
END
END ;
r.buf.data.B[r.bpos] := x; INC(r.bpos); r.buf.mod := TRUE
END Write;
PROCEDURE WriteBytes*(VAR r: Rider; VAR x: ARRAY OF SYSTEM.BYTE;
n: LONGINT);
VAR src, dst, m: LONGINT; f: File; buf: Buffer;
BEGIN src := SYSTEM.ADR(x);
IF LEN(x) < n THEN HALT(25) END ;
IF r.apos # r.buf.apos THEN r.buf := GetBuf(r.file, r.apos) END ;
LOOP
IF n <= 0 THEN EXIT END ;
r.buf.mod := TRUE; dst := SYSTEM.ADR(r.buf.data.B) + r.bpos; m := r.bpos + n;
IF m <= r.buf.lim THEN
SYSTEM.MOVE(src, dst, n); r.bpos := SHORT(m); EXIT
ELSIF m <= SS THEN
SYSTEM.MOVE(src, dst, n); r.bpos := SHORT(m);
r.file.bleng := SHORT(m); r.buf.lim := SHORT(m); r.file.modH := TRUE; EXIT
ELSE m := SS - r.bpos;
IF m > 0 THEN SYSTEM.MOVE(src, dst, m); INC(src, m); DEC(n, m) END ;
f := r.file; WriteBuf(f, r.buf); INC(r.apos); r.bpos := 0; buf := Buf(f, r.apos);
IF buf = NIL THEN
IF r.apos <= f.aleng THEN ReadBuf(f, r.buf, r.apos)
ELSE r.buf.apos := r.apos; r.buf.lim := 0; INC(f.aleng); f.bleng := 0; f.modH := TRUE;
IF (f.aleng - STS) MOD XS = 0 THEN NewExt(f) END
END
ELSE r.buf := buf
END
END
END
END WriteBytes;
PROCEDURE Pos*(VAR r: Rider): LONGINT;
BEGIN RETURN LONG(r.apos)*SS + r.bpos - HS
END Pos;
PROCEDURE Base*(VAR r: Rider): File;
BEGIN RETURN r.file
END Base;
PROCEDURE Delete*(name: ARRAY OF CHAR; VAR res: INTEGER);
VAR adr: DiskAdr;
namebuf: FileDir.FileName;
BEGIN Check(name, namebuf, res);
IF res = 0 THEN
FileDir.Delete(namebuf, adr);
IF adr = 0 THEN res := 2 END
END
END Delete;
PROCEDURE Rename*(old, new: ARRAY OF CHAR; VAR res: INTEGER);
VAR adr: DiskAdr;
oldbuf, newbuf: FileDir.FileName;
head: FileDir.FileHeader;
BEGIN Check(old, oldbuf, res);
IF res = 0 THEN
Check(new, newbuf, res);
IF res = 0 THEN
FileDir.Delete(oldbuf, adr);
IF adr # 0 THEN
FileDir.Insert(newbuf, adr);
Kernel.GetSector(adr, head); head.name := newbuf; Kernel.PutSector(adr, head)
ELSE res := 2
END
END
END
END Rename;
BEGIN Kernel.FileRoot := SYSTEM.ADR(root)
END Files.