Syntax10.Scn.FntInfoElemsAllocSyntax10.Scn.FntSStampElemsAlloc23 Nov 98!Syntax10i.Scn.Fnt"Title": Files "Author": J. Templ, RLI "Abstract": Oberon files mapped onto SVR4 files "Keywords": Files System "Version": 2.0 "From": 1 Dec 89 "Until":  "Changes": RLI, 6 Oct 96: Made Delete, Register and Rename compatible with directories, i.e. a notifier is called when a file is deleted/registered / renamed RLI, 16 Sep 97: ChangeDirectory removed RLI, 7 Oct 98: Delete: Call to Errno patched - yielded wrong results to res "Hints": Edit.Open Directories.Mod Syntax10i.Scn.FntD7)]%$7Syntax10b.Scn.Fnt  ~ 2DHd#?MarkElemsAllocp% Xk"T4FL A W W;i0S .8FoldElemsNew]8 # HX#&$)  ]U$ UMQ^ Q U  [ <-0 =f1 bf0 9/ =0  $'HU?K%"\MODULE Files;  (* J. Templ 1.12. 89/14.05.93 Oberon files mapped onto SVR4 files *) IMPORT SYSTEM, Unix, Kernel, Console, Directories; (* standard data type I/O little endian, Sint:1, Int:2, Lint:4 ORD({0}) = 1, false = 0, true =1 IEEE real format, null terminated strings, compact numbers according to M.Odersky *) CONST nofbufs = 4; bufsize = 1024+12; fileTabSize = 64; noDesc = -1; notDone = -1; (* file states *) open = 0; create = 1; close = 2; (* flag sets *) rdonly = {}; rdwr = {1}; creat = {6}; trunc = {9}; TYPE FileName = ARRAY 101 OF CHAR; File* = POINTER TO Handle; Buffer = POINTER TO BufDesc; Handle = RECORD workName, registerName: FileName; tempFile: BOOLEAN; dev, ino: LONGINT; mtime: LONGINT; fd-, len, pos: LONGINT; bufs: ARRAY nofbufs OF Buffer; swapper, state: INTEGER END; BufDesc = RECORD f: File; chg: BOOLEAN; org, size: LONGINT; data: ARRAY bufsize OF SYSTEM.BYTE END; Rider* = RECORD res*: LONGINT; eof*: BOOLEAN; buf: Buffer; org, offset: LONGINT END; EnvVar = POINTER TO ARRAY 1024 OF CHAR; Time = POINTER TO RECORD sec*, min*, hour*, mday*, mon*, year*, wday*, yday*, isdst*: LONGINT END; VAR fileTab: ARRAY fileTabSize OF LONGINT (* =File *); tempno: INTEGER; localtime: PROCEDURE (VAR clock: LONGINT): Time; getcwd: PROCEDURE (VAR CWD: ARRAY OF CHAR); OBERON, CWD: ARRAY 1024 OF CHAR; PROCEDURE ^ Cleanup(obj: SYSTEM.PTR); PROCEDURE -IdxTrap 0B8H, 08H, 0H, 0H, 0H, (* mov eax, 8 *) 08DH, 0C9H; (* lea ecx, ecx -> illegal operation -> HALT(8) *) PROCEDURE MakeFileName(dir, name: ARRAY OF CHAR; VAR dest: ARRAY OF CHAR); VAR i, j: INTEGER; BEGIN i := 0; j := 0; WHILE dir[i] # 0X DO dest[i] := dir[i]; INC(i) END; IF dest[i-1] # "/" THEN dest[i] := "/"; INC(i) END; WHILE name[j] # 0X DO dest[i] := name[j]; INC(i); INC(j) END; dest[i] := 0X END MakeFileName; PROCEDURE GetTempName(finalName: ARRAY OF CHAR; VAR name: ARRAY OF CHAR); VAR n, i, j: LONGINT; BEGIN INC(tempno); n := tempno; i := 0; IF finalName[0] # "/" THEN (* relative pathname *) WHILE CWD[i] # 0X DO name[i] := CWD[i]; INC(i) END; IF CWD[i-1] # "/" THEN name[i] := "/"; INC(i) END END; j := 0; WHILE finalName[j] # 0X DO name[i] := finalName[j]; INC(i); INC(j) END; DEC(i); WHILE name[i] # "/" DO DEC(i) END; name[i+1] := "."; name[i+2] := "t"; name[i+3] := "m"; name[i+4] := "p"; name[i+5] := "."; INC(i, 6); WHILE n > 0 DO name[i] := CHR(n MOD 10 + ORD("0")); n := n DIV 10; INC(i) END; name[i] := "."; INC(i); n := SHORT(Unix.Getpid()); WHILE n > 0 DO name[i] := CHR(n MOD 10 + ORD("0")); n := n DIV 10; INC(i) END; name[i] := 0X END GetTempName; PROCEDURE ExpandDollar(rel: ARRAY OF CHAR; VAR abs: ARRAY OF CHAR); (* Expands leading $-sign in rel to oberon - startup path in abs *) VAR d: Directories.Directory; i, j: INTEGER; BEGIN NEW(d); d := Directories.Startup(); IF rel[0] = "$" THEN COPY(d.path, abs); i := 0; j := 1; WHILE abs[i] # 0X DO INC(i) END; abs[i] := "/"; INC(i); WHILE rel[j] # 0X DO abs[i] := rel[j]; INC(i); INC(j) END; abs[i] := 0X; ELSE COPY(rel, abs); END; END ExpandDollar; PROCEDURE Create(f: File); VAR stat: Unix.Status; done: BOOLEAN; errno: LONGINT; err, path: ARRAY 256 OF CHAR; BEGIN IF f.fd = noDesc THEN IF f.state = create THEN GetTempName(f.registerName, f.workName); f.tempFile := TRUE ELSIF f.state = close THEN f.workName := f.registerName; f.registerName := ""; f.tempFile := FALSE END; ExpandDollar(f.workName, path); errno := Unix.Unlink(SYSTEM.ADR(path)); (*unlink first to avoid stale NFS handles and to avoid reuse of inodes*) f.fd := Unix.Open(SYSTEM.ADR(path), rdwr + creat + trunc, {2, 4,5, 7,8}); done := f.fd >= 0; errno := Unix.Errno(); IF (~done & ((errno = Unix.ENFILE) OR (errno = Unix.EMFILE))) OR (done & (f.fd >= fileTabSize)) THEN IF done & (f.fd >= fileTabSize) THEN errno := Unix.Close(f.fd) END; Kernel.GC; f.fd := Unix.Open(SYSTEM.ADR(path), rdwr + creat + trunc, {2, 4,5, 7,8}); done := f.fd >= 0 END; IF done THEN IF f.fd >= fileTabSize THEN errno := Unix.Close(f.fd); errno := 0; err := "too many files open"; HALT(99) ELSE fileTab[f.fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Cleanup); f.state := open; f.pos := 0; errno := Unix.Fstat(f.fd, SYSTEM.ADR(stat)); f.dev := stat.dev; f.ino := stat.ino; f.mtime := stat.mtime END ELSE errno := Unix.Errno(); IF errno = Unix.ENOENT THEN err := "no such directory" ELSE err := "create not done" END; HALT(99) END END END Create; PROCEDURE Flush(buf: Buffer); VAR res, errno: LONGINT; f: File; workName, registerName: FileName; err: ARRAY 25 OF CHAR; stat: Unix.Status; BEGIN IF buf.chg THEN f := buf.f; Create(f); IF buf.org # f.pos THEN res := Unix.Lseek(f.fd, buf.org, 0) END; res := Unix.Write(f.fd, SYSTEM.ADR(buf.data), buf.size); IF res < 0 THEN workName := f.workName; registerName := f.registerName; err := "error in writing file"; errno := Unix.Errno(); HALT(99) END; f.pos := buf.org + buf.size; buf.chg := FALSE; res := Unix.Fstat(f.fd, SYSTEM.ADR(stat)); f.mtime := stat.mtime END END Flush; PROCEDURE Close* (f: File); VAR i, res: LONGINT; workName, registerName, err: FileName; BEGIN IF (f.state # create) OR (f.registerName # "") THEN Create(f); i := 0; WHILE (i < nofbufs) & (f.bufs[i] # NIL) DO Flush(f.bufs[i]); INC(i) END; res := Unix.Fsync(f.fd); IF res = -1 THEN workName := f.workName; registerName := f.registerName; err := "error in writing file"; HALT(99) END END END Close; PROCEDURE Length* (f: File): LONGINT; BEGIN RETURN f.len END Length; PROCEDURE New* (name: ARRAY OF CHAR): File; VAR f: File; path: ARRAY 256 OF CHAR; BEGIN ExpandDollar(name, path); NEW(f); f.workName := ""; COPY(path, f.registerName); f.fd := noDesc; f.state := create; f.len := 0; f.pos := 0; f.swapper := -1; (*all f.buf[i] = NIL*) RETURN f END New; PROCEDURE ScanPath(VAR pos: INTEGER; VAR dir: ARRAY OF CHAR); VAR i: INTEGER; ch: CHAR; BEGIN i := 0; ch := OBERON[pos]; WHILE ch = " " DO INC(pos); ch := OBERON[pos] END; WHILE ch > " " DO dir[i] := ch; INC(i); INC(pos); ch := OBERON[pos] END; dir[i] := 0X END ScanPath; PROCEDURE HasDir(VAR name: ARRAY OF CHAR): BOOLEAN; VAR i: INTEGER; ch: CHAR; BEGIN i := 0; ch := name[0]; WHILE (ch # 0X) & (ch # "/") DO INC(i); ch := name[i] END; RETURN ch = "/" END HasDir; PROCEDURE CacheEntry(dev, ino: LONGINT; mtime: LONGINT): File; VAR f: File; i: INTEGER; stat: Unix.Status; res: LONGINT; BEGIN i := 0; WHILE i < fileTabSize DO f := SYSTEM.VAL(File, fileTab[i]); IF (f # NIL) & (ino = f.ino) & (dev = f.dev) THEN IF (mtime # f.mtime) THEN i := 0; WHILE i < nofbufs DO IF f.bufs[i] # NIL THEN f.bufs[i].org := -1; f.bufs[i] := NIL END; INC(i) END; f.swapper := -1; f.mtime := mtime; res := Unix.Fstat(f.fd, SYSTEM.ADR(stat)); f.len := stat.size END; RETURN f END; INC(i) END; RETURN NIL END CacheEntry; PROCEDURE Old* (name: ARRAY OF CHAR): File; VAR f: File; fd, res, errno: LONGINT; pos, i, j: INTEGER; done: BOOLEAN; dir, path, err: ARRAY 256 OF CHAR; stat: Unix.Status; d: Directories.Directory; BEGIN IF name # "" THEN IF name[0] = "$" THEN ExpandDollar(name, path); ELSIF HasDir(name) THEN dir := ""; COPY(name, path) ELSE pos := 0; ScanPath(pos, dir); MakeFileName(dir, name, path); ScanPath(pos, dir) END; LOOP fd := Unix.Open(SYSTEM.ADR(path), rdwr, {}); done := fd >= 0; errno := Unix.Errno(); IF (~done & ((errno = Unix.ENFILE) OR (errno = Unix.EMFILE))) OR (done & (fd >= fileTabSize)) THEN IF done & (fd >= fileTabSize) THEN res := Unix.Close(fd) END; Kernel.GC; fd := Unix.Open(SYSTEM.ADR(path), rdwr, {}); done := fd >= 0; errno := Unix.Errno() END; IF ~done & ((errno = Unix.EACCES) OR (errno = Unix.EROFS)) THEN fd := Unix.Open(SYSTEM.ADR(path), rdonly, {}); done := fd >= 0; errno := Unix.Errno(); IF (done & (fd >= fileTabSize)) THEN res := Unix.Close(fd); Kernel.GC; fd := Unix.Open(SYSTEM.ADR(path), rdonly, {}); done := fd >= 0; errno := Unix.Errno(); END; END; IF done THEN res := Unix.Fstat(fd, SYSTEM.ADR(stat)); f := CacheEntry(stat.dev, stat.ino, stat.mtime); IF f # NIL THEN res := Unix.Close(fd); RETURN f ELSIF fd >= fileTabSize THEN res := Unix.Close(fd); err := "too many files open"; errno := 0; HALT(99) ELSE NEW(f); fileTab[fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Cleanup); f.fd := fd; f.state := open; f.len := stat.size; f.pos := 0; f.swapper := -1; (*all f.buf[i] = NIL*) COPY(path, f.workName); f.registerName := ""; f.tempFile := FALSE; f.dev := stat.dev; f.ino := stat.ino; f.mtime := stat.mtime; RETURN f END ELSIF dir = "" THEN RETURN NIL ELSE MakeFileName(dir, name, path); ScanPath(pos, dir) END END ELSE RETURN NIL END END Old; PROCEDURE Purge* (f: File); VAR i: INTEGER; stat: Unix.Status; res: LONGINT; BEGIN i := 0; WHILE i < nofbufs DO IF f.bufs[i] # NIL THEN f.bufs[i].org := -1; f.bufs[i] := NIL END; INC(i) END; IF f.fd # noDesc THEN res := Unix.Ftruncate(f.fd, 0); res := Unix.Lseek(f.fd, 0, 0) END; f.pos := 0; f.len := 0; f.swapper := -1; res := Unix.Fstat(f.fd, SYSTEM.ADR(stat)); f.mtime := stat.mtime END Purge; PROCEDURE GetDate* (f: File; VAR t, d: LONGINT); VAR stat: Unix.Status; clock, res: LONGINT; time: Time; BEGIN Create(f); res := Unix.Fstat(f.fd, SYSTEM.ADR(stat)); time := localtime(stat.mtime); t := time.sec + ASH(time.min, 6) + ASH(time.hour, 12); d := time.mday + ASH(time.mon+1, 5) + ASH(time.year MOD 100, 9) END GetDate; PROCEDURE Pos* (VAR r: Rider): LONGINT; BEGIN RETURN r.org + r.offset END Pos; PROCEDURE Set* (VAR r: Rider; f: File; pos: LONGINT); VAR org, offset, i, n, res, errno: LONGINT; buf: Buffer; workName, registerName: FileName; err: ARRAY 25 OF CHAR; BEGIN IF f # NIL THEN IF pos > f.len THEN pos := f.len ELSIF pos < 0 THEN pos := 0 END; offset := pos MOD bufsize; org := pos - offset; i := 0; WHILE (i < nofbufs) & (f.bufs[i] # NIL) & (org # f.bufs[i].org) DO INC(i) END; IF i < nofbufs THEN IF f.bufs[i] = NIL THEN NEW(buf); buf.chg := FALSE; buf.org := -1; buf.f := f; f.bufs[i] := buf ELSE buf := f.bufs[i] END ELSE f.swapper := (f.swapper + 1) MOD nofbufs; buf := f.bufs[f.swapper]; Flush(buf) END; IF buf.org # org THEN IF org = f.len THEN buf.size := 0 ELSE Create(f); IF f.pos # org THEN res := Unix.Lseek(f.fd, org, 0) END; n := Unix.Read(f.fd, SYSTEM.ADR(buf.data), LEN(buf.data)); IF n < 0 THEN errno := Unix.Errno(); workName := f.workName; registerName := f.registerName; err := "read not done"; HALT(99) END; f.pos := org + n; buf.size := n END; buf.org := org; buf.chg := FALSE END ELSE buf := NIL; org := 0; offset := 0 END; r.buf := buf; r.org := org; r.offset := offset; r.eof := FALSE; r.res := 0 END Set; PROCEDURE Read* (VAR r: Rider; VAR x: SYSTEM.BYTE); VAR offset: LONGINT; buf: Buffer; BEGIN buf := r.buf; offset := r.offset; IF r.org # buf.org THEN Set(r, buf.f, r.org + offset); buf := r.buf; offset := r.offset END; IF (offset < buf.size) THEN x := buf.data[offset]; r.offset := offset + 1 ELSIF r.org + offset < buf.f.len THEN Set(r, r.buf.f, r.org + offset); x := r.buf.data[0]; r.offset := 1 ELSE x := 0X; r.eof := TRUE END END Read; PROCEDURE ReadBytes* (VAR r: Rider; VAR x: ARRAY OF SYSTEM.BYTE; n: LONGINT); VAR xpos, min, restInBuf, offset: LONGINT; buf: Buffer; BEGIN IF n > LEN(x) THEN IdxTrap END; xpos := 0; buf := r.buf; offset := r.offset; WHILE n > 0 DO IF (r.org # buf.org) OR (offset >= bufsize) THEN Set(r, buf.f, r.org + offset); buf := r.buf; offset := r.offset END; restInBuf := buf.size - offset; IF restInBuf = 0 THEN r.res := n; r.eof := TRUE; RETURN ELSIF n > restInBuf THEN min := restInBuf ELSE min := n END; SYSTEM.MOVE(SYSTEM.ADR(buf.data) + offset, SYSTEM.ADR(x) + xpos, min); INC(offset, min); r.offset := offset; INC(xpos, min); DEC(n, min) END; r.res := 0; r.eof := FALSE END ReadBytes; PROCEDURE Base* (VAR r: Rider): File; BEGIN RETURN r.buf.f END Base; PROCEDURE Write* (VAR r: Rider; x: SYSTEM.BYTE); VAR buf: Buffer; offset: LONGINT; BEGIN buf := r.buf; offset := r.offset; IF (r.org # buf.org) OR (offset >= bufsize) THEN Set(r, buf.f, r.org + offset); buf := r.buf; offset := r.offset END; buf.data[offset] := x; buf.chg := TRUE; IF offset = buf.size THEN INC(buf.size); INC(buf.f.len) END; r.offset := offset + 1; r.res := 0 END Write; PROCEDURE WriteBytes* (VAR r: Rider; VAR x: ARRAY OF SYSTEM.BYTE; n: LONGINT); VAR xpos, min, restInBuf, offset: LONGINT; buf: Buffer; BEGIN IF n > LEN(x) THEN IdxTrap END; xpos := 0; buf := r.buf; offset := r.offset; WHILE n > 0 DO IF (r.org # buf.org) OR (offset >= bufsize) THEN Set(r, buf.f, r.org + offset); buf := r.buf; offset := r.offset END; restInBuf := bufsize - offset; IF n > restInBuf THEN min := restInBuf ELSE min := n END; SYSTEM.MOVE(SYSTEM.ADR(x) + xpos, SYSTEM.ADR(buf.data) + offset, min); INC(offset, min); r.offset := offset; IF offset > buf.size THEN INC(buf.f.len, offset - buf.size); buf.size := offset END; INC(xpos, min); DEC(n, min); buf.chg := TRUE END; r.res := 0 END WriteBytes; (* another solution would be one that is similar to ReadBytes, WriteBytes. No code duplication, more symmetric, only two ifs for Read and Write in buffer, buf.size replaced by bufsize in Write ops, buf.size and len must be made consistent with offset (if offset > buf.size) in a lazy way. PROCEDURE Write* (VAR r: Rider; x: SYSTEM.BYTE); VAR buf: Buffer; offset: LONGINT; BEGIN buf := r.buf; offset := r.offset; IF (offset >= bufsize) OR (r.org # buf.org) THEN Set(r, buf.f, r.org + offset); buf := r.buf; offset := r.offset; END; buf.data[offset] := x; r.offset := offset + 1; buf.chg := TRUE END Write; PROCEDURE WriteBytes ... PROCEDURE Read* (VAR r: Rider; VAR x: SYSTEM.BYTE); VAR offset: LONGINT; buf: Buffer; BEGIN buf := r.buf; offset := r.offset; IF (offset >= buf.size) OR (r.org # buf.org) THEN IF r.org + offset >= buf.f.len THEN x := 0X; r.eof := TRUE; RETURN ELSE Set(r, buf.f, r.org + offset); buf := r.buf; offset := r.offset END END; x := buf.data[offset]; r.offset := offset + 1 END Read; but this would also affect Set, Length, and Flush. Especially Length would become fairly complex. *) PROCEDURE Dir (VAR (* in *) path: ARRAY OF CHAR): Directories.Directory; BEGIN IF path = "" THEN RETURN Directories.Current() ELSE RETURN Directories.This(path) END END Dir; PROCEDURE SplitName (VAR (* in*) name: ARRAY OF CHAR; VAR path, filename: ARRAY OF CHAR); VAR i, j, pos: INTEGER; (* pos = position of first filename character *) absName: ARRAY 140 OF CHAR; startupDir: Directories.Directory; BEGIN IF name[0] = "$" THEN startupDir := Directories.Startup(); COPY(startupDir.path, absName); i := 0; WHILE absName[i] # 0X DO INC(i) END ; IF name[1] # Directories.delimiter THEN absName[i] := Directories.delimiter; INC(i) END ; j := 1; WHILE name[j] # 0X DO absName[i] := name[j]; INC(i); INC(j) END ; absName[i] := 0X ELSE COPY(name, absName) END ; pos := 0; i := 0; WHILE absName[i] # 0X DO IF absName[i] = Directories.delimiter THEN pos := i END ; INC(i) END ; COPY(absName, path); IF pos = 0 THEN (* no path *) path[0] := 0X; COPY(absName, filename) ELSE (* cut last \ *) IF path[pos - 1] = ":" THEN (* keep "\", e.g. "d:\" *) path[pos + 1] := 0X ELSE path[pos] := 0X END ; i := 0; INC(pos); WHILE absName[pos] # 0X DO filename[i] := absName[pos]; INC(pos); INC(i) END ; filename[i] := 0X END END SplitName; PROCEDURE Extend (VAR s, path: ARRAY OF CHAR);  VAR i, j: INTEGER; n: ARRAY 128 OF CHAR; d: Directories.Directory; BEGIN IF s[0] = "$" THEN d := Directories.Startup(); COPY(d.path, path); i := 0; WHILE path[i] # 0X DO INC(i) END; path[i] := Directories.delimiter; j := 1; WHILE s[j] # 0X DO path[i + j] := s[j]; INC(j) END; path[i+j] := 0X; ELSE COPY(s, path); END END Extend;  PROCEDURE Delete* (name: ARRAY OF CHAR; VAR res: INTEGER); VAR dummy: LONGINT; dir: Directories.Directory; path, absPath: ARRAY 256 OF CHAR; filename: ARRAY 32 OF CHAR; BEGIN dir := Directories.This(name); IF dir # NIL THEN Directories.Delete(name); res := Directories.res; RETURN END ; SplitName(name, path, filename); dir := Dir(path); Extend(name, absPath); res := SHORT(Unix.Unlink(SYSTEM.ADR(absPath))); IF res = 0 THEN Directories.notify(Directories.delete, dir.path, filename); END; END Delete; PROCEDURE Rename* (old, new: ARRAY OF CHAR; VAR res: INTEGER); VAR fdold, fdnew, n, errno, r: LONGINT; ostat, nstat: Unix.Status; buf: ARRAY 4096 OF CHAR; newDir, oldDir: Directories.Directory; oldPath, newPath: ARRAY 256 OF CHAR; oldName, newName: ARRAY 32 OF CHAR; BEGIN oldDir := Directories.This(old); IF oldDir # NIL THEN Directories.Rename(old, new); CASE Directories.res OF Directories.noErr: res := 0 | Directories.badName: res := 3 ELSE res := 5 END ; RETURN END ; SplitName(new, newPath, newName); newDir := Dir(newPath); SplitName(old, oldPath, oldName); oldDir := Dir(oldPath); r := Unix.Stat(SYSTEM.ADR(old), SYSTEM.ADR(ostat)); IF r >= 0 THEN r := Unix.Stat(SYSTEM.ADR(new), SYSTEM.ADR(nstat)); IF (r >= 0) & ((ostat.dev # nstat.dev) OR (ostat.ino # nstat.ino)) THEN Delete(new, res); (* work around stale nfs handles *) END; r := Unix.Rename(SYSTEM.ADR(old), SYSTEM.ADR(new)); IF r < 0 THEN res := SHORT(Unix.Errno()); IF res = Unix.EXDEV THEN (* cross device link, move the file *) fdold := Unix.Open(SYSTEM.ADR(old), rdonly, {}); IF fdold < 0 THEN errno := fdold; HALT(99) END; fdnew := Unix.Open(SYSTEM.ADR(new), rdwr + creat + trunc, {2, 4,5, 7,8}); IF fdnew < 0 THEN errno := Unix.Errno(); HALT(99) END; n := Unix.Read(fdold, SYSTEM.ADR(buf), bufsize); WHILE n > 0 DO r := Unix.Write(fdnew, SYSTEM.ADR(buf), n); IF r < 0 THEN errno := Unix.Errno(); r := Unix.Close(fdold); r := Unix.Close(fdnew); HALT(99) END; n := Unix.Read(fdold, SYSTEM.ADR(buf), bufsize) END; r := Unix.Unlink(SYSTEM.ADR(old)); r := Unix.Close(fdold); r := Unix.Close(fdnew); res := 0 ELSE RETURN (* res is Unix.Rename return code *) END END; res := 0 ELSE res := 2 (* old file not found *) END; IF res = 0 THEN Directories.notify(Directories.delete, oldDir.path, oldName); Directories.notify(Directories.insert, newDir.path, newName); END END Rename; PROCEDURE Register* (f: File); VAR idx, errno: INTEGER; f1: File; file: ARRAY 104 OF CHAR; d: Directories.Directory; BEGIN IF (f.state = create) & (f.registerName # "") THEN f.state := close (* shortcut renaming *) END; d := Directories.Current(); Directories.notify(Directories.insert, d.path, f.registerName); Close(f); IF f.registerName # "" THEN Rename(f.workName, f.registerName, errno); IF errno # 0 THEN COPY(f.registerName, file); HALT(99) END; f.workName := f.registerName; f.registerName := ""; f.tempFile := FALSE; END END Register; PROCEDURE FlipBytes(VAR src, dest: ARRAY OF SYSTEM.BYTE); VAR i, j: LONGINT; BEGIN i := LEN(src); j := 0; WHILE i > 0 DO DEC(i); dest[j] := src[i]; INC(j) END END FlipBytes; PROCEDURE ReadBool* (VAR R: Rider; VAR x: BOOLEAN); BEGIN Read(R, SYSTEM.VAL(CHAR, x)) END ReadBool; PROCEDURE ReadInt* (VAR R: Rider; VAR x: INTEGER); VAR b: ARRAY 2 OF CHAR; BEGIN ReadBytes(R, x, 2); (* x := ORD(b[0]) + ORD(b[1])*256 *) END ReadInt; PROCEDURE ReadLInt* (VAR R: Rider; VAR x: LONGINT); VAR b: ARRAY 4 OF CHAR; BEGIN ReadBytes(R, x, 4); (* x := ORD(b[0]) + ORD(b[1])*100H + ORD(b[2])*10000H + ORD(b[3])*1000000H *) END ReadLInt; PROCEDURE ReadSet* (VAR R: Rider; VAR x: SET); VAR b: ARRAY 4 OF CHAR; BEGIN ReadBytes(R, x, 4); (* x := SYSTEM.VAL(SET, ORD(b[0]) + ORD(b[1])*100H + ORD(b[2])*10000H + ORD(b[3])*1000000H) *) END ReadSet; PROCEDURE ReadReal* (VAR R: Rider; VAR x: REAL); VAR b: ARRAY 4 OF CHAR; BEGIN ReadBytes(R, x, 4); (* FlipBytes(b, x) *) END ReadReal; PROCEDURE ReadLReal* (VAR R: Rider; VAR x: LONGREAL); VAR b: ARRAY 8 OF CHAR; BEGIN ReadBytes(R, x, 8); (* FlipBytes(b, x) *) END ReadLReal; PROCEDURE ReadString* (VAR R: Rider; VAR x: ARRAY OF CHAR); VAR i: INTEGER; ch: CHAR; BEGIN i := 0; REPEAT Read(R, ch); x[i] := ch; INC(i) UNTIL ch = 0X END ReadString; PROCEDURE ReadNum* (VAR R: Rider; VAR x: LONGINT); VAR s: SHORTINT; ch: CHAR; n: LONGINT; BEGIN s := 0; n := 0; Read(R, ch); WHILE ORD(ch) >= 128 DO INC(n, ASH(ORD(ch) - 128, s) ); INC(s, 7); Read(R, ch) END; INC(n, ASH(ORD(ch) MOD 64 - ORD(ch) DIV 64 * 64, s) ); x := n END ReadNum; PROCEDURE WriteBool* (VAR R: Rider; x: BOOLEAN); BEGIN Write(R, SYSTEM.VAL(CHAR, x)) END WriteBool; PROCEDURE WriteInt* (VAR R: Rider; x: INTEGER); VAR b: ARRAY 2 OF CHAR; BEGIN (* b[0] := CHR(x); b[1] := CHR(x DIV 256); *) WriteBytes(R, x, 2); END WriteInt; PROCEDURE WriteLInt* (VAR R: Rider; x: LONGINT); VAR b: ARRAY 4 OF CHAR; BEGIN (* b[0] := CHR(x); b[1] := CHR(x DIV 100H); b[2] := CHR(x DIV 10000H); b[3] := CHR(x DIV 1000000H); *) WriteBytes(R, x, 4); END WriteLInt; PROCEDURE WriteSet* (VAR R: Rider; x: SET); VAR b: ARRAY 4 OF CHAR; i: LONGINT; BEGIN i := SYSTEM.VAL(LONGINT, x); (* b[0] := CHR(i); b[1] := CHR(i DIV 100H); b[2] := CHR(i DIV 10000H); b[3] := CHR(i DIV 1000000H); *) WriteBytes(R, x, 4); END WriteSet; PROCEDURE WriteReal* (VAR R: Rider; x: REAL); VAR b: ARRAY 4 OF CHAR; BEGIN (* FlipBytes(x, b); *) WriteBytes(R, x, 4) END WriteReal; PROCEDURE WriteLReal* (VAR R: Rider; x: LONGREAL); VAR b: ARRAY 8 OF CHAR; BEGIN (* FlipBytes(x, b); *) WriteBytes(R, x, 8) END WriteLReal; PROCEDURE WriteString* (VAR R: Rider; x: ARRAY OF CHAR); VAR i: INTEGER; BEGIN i := 0; WHILE x[i] # 0X DO INC(i) END; WriteBytes(R, x, i+1) END WriteString; PROCEDURE WriteNum* (VAR R: Rider; x: LONGINT); BEGIN WHILE (x < - 64) OR (x > 63) DO Write(R, CHR(x MOD 128 + 128)); x := x DIV 128 END; Write(R, CHR(x MOD 128)) END WriteNum; PROCEDURE Cleanup(obj: SYSTEM.PTR); VAR f, res: LONGINT; file: File; BEGIN file := SYSTEM.VAL(File, obj); res := Unix.Close(file.fd); DEC(Kernel.nofiles); fileTab[file.fd] := 0; IF file.tempFile THEN res := Unix.Unlink(SYSTEM.ADR(file.workName)) END END Cleanup; PROCEDURE CloseFiles; VAR idx, f, res: LONGINT; file: File; BEGIN idx := 0; WHILE idx < fileTabSize DO f := fileTab[idx]; IF f # 0 THEN file := SYSTEM.VAL(File, f); IF file.tempFile THEN res := Unix.Unlink(SYSTEM.ADR(file.workName)) END END; INC(idx) END END CloseFiles; PROCEDURE Init; VAR i: LONGINT; var: EnvVar; ch: CHAR; getenv: PROCEDURE (var: ARRAY OF CHAR): EnvVar; atexit: PROCEDURE (p: PROCEDURE); BEGIN i := 0; WHILE i < fileTabSize DO fileTab[i] := 0; INC(i) END; tempno := -1; Kernel.nofiles := 0; Kernel.dlsym(Kernel.libc, "localtime", SYSTEM.VAL(LONGINT, localtime)); Kernel.dlsym(Kernel.libc, "getenv", SYSTEM.VAL(LONGINT, getenv)); Kernel.dlsym(Kernel.libc, "getcwd", SYSTEM.VAL(LONGINT, getcwd)); (* var := getenv("OBERON"); *) Kernel.dlsym(Kernel.libc, "OBERON", SYSTEM.VAL(LONGINT, var)); IF var = NIL THEN OBERON := ". /usr/local/Oberon /usr/local/Oberon/.Fonts" ELSE i := 0; REPEAT ch := var^[i]; IF ch = ":" THEN ch := " " END; OBERON[i] := ch; INC(i) UNTIL ch = 0X END; getcwd(CWD); Kernel.InstallTermHandler(CloseFiles) END Init; BEGIN Init END Files.