From 59e489ea43cd33fe9dc77ed54b01dd24a3f7bec1 Mon Sep 17 00:00:00 2001 From: Norayr Chilingarian Date: Tue, 10 Feb 2015 15:06:04 +0400 Subject: [PATCH] x86_86 gcc/clang low level modules update, types clarified. -- noch --- src/lib/system/linux/clang/Console.Mod | 4 - .../linux/clang/{ => armv6j_hardfp}/Files.Mod | 0 .../clang/{ => armv6j_hardfp}/Files0.Mod | 0 .../linux/clang/armv6j_hardfp/SYSTEM.c0 | 2 +- .../system/linux/clang/armv6j_hardfp/SYSTEM.h | 2 +- .../linux/{gcc => clang/powerpc}/Files.Mod | 0 .../linux/{gcc => clang/powerpc}/Files0.Mod | 0 src/lib/system/linux/clang/powerpc/SYSTEM.c0 | 2 +- src/lib/system/linux/clang/powerpc/SYSTEM.h | 2 +- src/lib/system/linux/clang/x86/Files.Mod | 663 +++++++++++++++++ src/lib/system/linux/clang/x86/Files0.Mod | 635 +++++++++++++++++ src/lib/system/linux/clang/x86/SYSTEM.c0 | 2 +- src/lib/system/linux/clang/x86/SYSTEM.h | 2 +- src/lib/system/linux/clang/x86_64/Files.Mod | 664 ++++++++++++++++++ src/lib/system/linux/clang/x86_64/Files0.Mod | 636 +++++++++++++++++ src/lib/system/linux/clang/x86_64/Unix.Mod | 112 +-- .../system/linux/gcc/armv6j_hardfp/Files.Mod | 663 +++++++++++++++++ .../system/linux/gcc/armv6j_hardfp/Files0.Mod | 635 +++++++++++++++++ src/lib/system/linux/gcc/powerpc/Files.Mod | 663 +++++++++++++++++ src/lib/system/linux/gcc/powerpc/Files0.Mod | 635 +++++++++++++++++ src/lib/system/linux/gcc/x86/Files.Mod | 663 +++++++++++++++++ src/lib/system/linux/gcc/x86/Files0.Mod | 635 +++++++++++++++++ src/lib/system/linux/gcc/x86_64/Files.Mod | 664 ++++++++++++++++++ src/lib/system/linux/gcc/x86_64/Files0.Mod | 636 +++++++++++++++++ src/lib/system/linux/gcc/x86_64/Unix.Mod | 112 +-- src/lib/ulm/armv6j/ulmSysConversions.Mod | 574 --------------- src/lib/ulm/armv6j/ulmSysStat.Mod | 201 ------ src/lib/ulm/armv6j/ulmSysTypes.Mod | 70 -- src/lib/ulm/armv6j/ulmTypes.Mod | 133 ---- src/lib/ulm/{ => armv6j_hardfp}/ulmSYSTEM.Mod | 0 .../ulm/armv7a_hardfp/ulmSysConversions.Mod | 574 --------------- src/lib/ulm/armv7a_hardfp/ulmSysStat.Mod | 201 ------ src/lib/ulm/armv7a_hardfp/ulmSysTypes.Mod | 70 -- src/lib/ulm/armv7a_hardfp/ulmTypes.Mod | 133 ---- src/lib/ulm/powerpc/ulmSYSTEM.Mod | 137 ++++ src/lib/ulm/x86/ulmSYSTEM.Mod | 137 ++++ src/lib/ulm/x86_64/ulmSYSTEM.Mod | 137 ++++ src/test/files/testFiles.Mod | 3 +- voc | Bin 1277232 -> 1277272 bytes vocstatic.linux.clang.x86_64 | Bin 1277232 -> 1350895 bytes vocstatic.linux.gcc.x86 | Bin 889247 -> 889337 bytes vocstatic.linux.gcc.x86_64 | Bin 1277232 -> 1277272 bytes 42 files changed, 8324 insertions(+), 2078 deletions(-) rename src/lib/system/linux/clang/{ => armv6j_hardfp}/Files.Mod (100%) rename src/lib/system/linux/clang/{ => armv6j_hardfp}/Files0.Mod (100%) rename src/lib/system/linux/{gcc => clang/powerpc}/Files.Mod (100%) rename src/lib/system/linux/{gcc => clang/powerpc}/Files0.Mod (100%) create mode 100644 src/lib/system/linux/clang/x86/Files.Mod create mode 100644 src/lib/system/linux/clang/x86/Files0.Mod create mode 100644 src/lib/system/linux/clang/x86_64/Files.Mod create mode 100644 src/lib/system/linux/clang/x86_64/Files0.Mod create mode 100644 src/lib/system/linux/gcc/armv6j_hardfp/Files.Mod create mode 100644 src/lib/system/linux/gcc/armv6j_hardfp/Files0.Mod create mode 100644 src/lib/system/linux/gcc/powerpc/Files.Mod create mode 100644 src/lib/system/linux/gcc/powerpc/Files0.Mod create mode 100644 src/lib/system/linux/gcc/x86/Files.Mod create mode 100644 src/lib/system/linux/gcc/x86/Files0.Mod create mode 100644 src/lib/system/linux/gcc/x86_64/Files.Mod create mode 100644 src/lib/system/linux/gcc/x86_64/Files0.Mod delete mode 100644 src/lib/ulm/armv6j/ulmSysConversions.Mod delete mode 100644 src/lib/ulm/armv6j/ulmSysStat.Mod delete mode 100644 src/lib/ulm/armv6j/ulmSysTypes.Mod delete mode 100644 src/lib/ulm/armv6j/ulmTypes.Mod rename src/lib/ulm/{ => armv6j_hardfp}/ulmSYSTEM.Mod (100%) delete mode 100644 src/lib/ulm/armv7a_hardfp/ulmSysConversions.Mod delete mode 100644 src/lib/ulm/armv7a_hardfp/ulmSysStat.Mod delete mode 100644 src/lib/ulm/armv7a_hardfp/ulmSysTypes.Mod delete mode 100644 src/lib/ulm/armv7a_hardfp/ulmTypes.Mod create mode 100644 src/lib/ulm/powerpc/ulmSYSTEM.Mod create mode 100644 src/lib/ulm/x86/ulmSYSTEM.Mod create mode 100644 src/lib/ulm/x86_64/ulmSYSTEM.Mod diff --git a/src/lib/system/linux/clang/Console.Mod b/src/lib/system/linux/clang/Console.Mod index 9b6b6611..e523ef7b 100644 --- a/src/lib/system/linux/clang/Console.Mod +++ b/src/lib/system/linux/clang/Console.Mod @@ -7,10 +7,6 @@ MODULE Console; (* J. Templ, 29-June-96 *) VAR line: ARRAY 128 OF CHAR; pos: INTEGER; - (* for read(), write() *) - PROCEDURE -includeUnistd() - "#include "; - PROCEDURE -Write(adr, n: LONGINT) "write(1/*stdout*/, adr, n)"; diff --git a/src/lib/system/linux/clang/Files.Mod b/src/lib/system/linux/clang/armv6j_hardfp/Files.Mod similarity index 100% rename from src/lib/system/linux/clang/Files.Mod rename to src/lib/system/linux/clang/armv6j_hardfp/Files.Mod diff --git a/src/lib/system/linux/clang/Files0.Mod b/src/lib/system/linux/clang/armv6j_hardfp/Files0.Mod similarity index 100% rename from src/lib/system/linux/clang/Files0.Mod rename to src/lib/system/linux/clang/armv6j_hardfp/Files0.Mod diff --git a/src/lib/system/linux/clang/armv6j_hardfp/SYSTEM.c0 b/src/lib/system/linux/clang/armv6j_hardfp/SYSTEM.c0 index a5599acc..580449aa 100644 --- a/src/lib/system/linux/clang/armv6j_hardfp/SYSTEM.c0 +++ b/src/lib/system/linux/clang/armv6j_hardfp/SYSTEM.c0 @@ -20,7 +20,7 @@ #include "varargs.h" #endif -extern void *malloc(unsigned int size); +extern void *malloc(long size); extern void exit(int status); void (*SYSTEM_Halt)(); diff --git a/src/lib/system/linux/clang/armv6j_hardfp/SYSTEM.h b/src/lib/system/linux/clang/armv6j_hardfp/SYSTEM.h index 0c7b19af..719a6d18 100644 --- a/src/lib/system/linux/clang/armv6j_hardfp/SYSTEM.h +++ b/src/lib/system/linux/clang/armv6j_hardfp/SYSTEM.h @@ -15,7 +15,7 @@ uses double # as concatenation operator //extern void *memcpy(void *dest, const void *src, long n); extern void *memcpy(void *dest, const void *src, size_t n); -extern void *malloc(unsigned int size); +extern void *malloc(long size); extern void exit(int status); #define export diff --git a/src/lib/system/linux/gcc/Files.Mod b/src/lib/system/linux/clang/powerpc/Files.Mod similarity index 100% rename from src/lib/system/linux/gcc/Files.Mod rename to src/lib/system/linux/clang/powerpc/Files.Mod diff --git a/src/lib/system/linux/gcc/Files0.Mod b/src/lib/system/linux/clang/powerpc/Files0.Mod similarity index 100% rename from src/lib/system/linux/gcc/Files0.Mod rename to src/lib/system/linux/clang/powerpc/Files0.Mod diff --git a/src/lib/system/linux/clang/powerpc/SYSTEM.c0 b/src/lib/system/linux/clang/powerpc/SYSTEM.c0 index a5599acc..580449aa 100644 --- a/src/lib/system/linux/clang/powerpc/SYSTEM.c0 +++ b/src/lib/system/linux/clang/powerpc/SYSTEM.c0 @@ -20,7 +20,7 @@ #include "varargs.h" #endif -extern void *malloc(unsigned int size); +extern void *malloc(long size); extern void exit(int status); void (*SYSTEM_Halt)(); diff --git a/src/lib/system/linux/clang/powerpc/SYSTEM.h b/src/lib/system/linux/clang/powerpc/SYSTEM.h index 0c7b19af..719a6d18 100644 --- a/src/lib/system/linux/clang/powerpc/SYSTEM.h +++ b/src/lib/system/linux/clang/powerpc/SYSTEM.h @@ -15,7 +15,7 @@ uses double # as concatenation operator //extern void *memcpy(void *dest, const void *src, long n); extern void *memcpy(void *dest, const void *src, size_t n); -extern void *malloc(unsigned int size); +extern void *malloc(long size); extern void exit(int status); #define export diff --git a/src/lib/system/linux/clang/x86/Files.Mod b/src/lib/system/linux/clang/x86/Files.Mod new file mode 100644 index 00000000..6307407d --- /dev/null +++ b/src/lib/system/linux/clang/x86/Files.Mod @@ -0,0 +1,663 @@ +MODULE Files; (* J. Templ 1.12. 89/12.4.95 Oberon files mapped onto Unix files *) +(* modified version of Files, which opens only the file provided and does not scan any path in any environment variable, also ReadLine procedure added; -- noch *) + IMPORT SYSTEM, Unix, Kernel, Args, Console; + + (* 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 = 4096; + fileTabSize = 64; + noDesc = -1; + notDone = -1; + + (* file states *) + open = 0; create = 1; close = 2; + + + TYPE + FileName = ARRAY 101 OF CHAR; + File* = POINTER TO Handle; + Buffer = POINTER TO BufDesc; + + Handle = RECORD + workName, registerName: FileName; + tempFile: BOOLEAN; + dev, ino, 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 ; + + Time = POINTER TO TimeDesc; + TimeDesc = RECORD + sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: LONGINT; +(* sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: INTEGER;*) + END ; + + VAR + fileTab: ARRAY fileTabSize OF LONGINT (*=File*); + tempno: INTEGER; + +(* for localtime *) + PROCEDURE -includetime() + '#include "time.h"'; + + PROCEDURE -localtime(VAR clock: LONGINT): Time + "(Files_Time) localtime(clock)"; + + PROCEDURE -getcwd(VAR cwd: Unix.Name) + "getcwd(cwd, cwd__len)"; + + PROCEDURE -IdxTrap "__HALT(-1)"; + + PROCEDURE^ Finalize(o: SYSTEM.PTR); + + PROCEDURE Err(s: ARRAY OF CHAR; f: File; errno: LONGINT); + BEGIN + Console.Ln; Console.String("-- "); Console.String(s); Console.String(": "); + IF f # NIL THEN + IF f.registerName # "" THEN Console.String(f.registerName) ELSE Console.String(f.workName) END + END ; + IF errno # 0 THEN Console.String(" errno = "); Console.Int(errno, 1) END ; + Console.Ln; + HALT(99) + END Err; + + 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 Kernel.CWD[i] # 0X DO name[i] := Kernel.CWD[i]; INC(i) END; + IF Kernel.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 Create(f: File); + VAR stat: Unix.Status; done: BOOLEAN; + errno: LONGINT; err: ARRAY 32 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 ; + errno := Unix.Unlink(f.workName); (*unlink first to avoid stale NFS handles and to avoid reuse of inodes*) + f.fd := Unix.Open(f.workName, Unix.rdwr + Unix.creat + Unix.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(TRUE); + f.fd := Unix.Open(f.workName, Unix.rdwr + Unix.creat + Unix.trunc, {2, 4,5, 7,8}); + done := f.fd >= 0 + END ; + IF done THEN + IF f.fd >= fileTabSize THEN errno := Unix.Close(f.fd); Err("too many files open", f, 0) + ELSE fileTab[f.fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.state := open; f.pos := 0; errno := Unix.Fstat(f.fd, 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" + ELSIF (errno = Unix.ENFILE) OR (errno = Unix.EMFILE) THEN err := "too many files open" + ELSE err := "file not created" + END ; + Err(err, f, errno) + END + END + END Create; + + PROCEDURE Flush(buf: Buffer); + VAR res: LONGINT; f: File; 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 Err("error in writing file", f, Unix.errno()) END ; + f.pos := buf.org + buf.size; + buf.chg := FALSE; + res := Unix.Fstat(f.fd, stat); + f.mtime := stat.mtime + END + END Flush; + + PROCEDURE Close* (f: File); + VAR i, res: LONGINT; + 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 < 0 THEN Err("error in writing file", f, Unix.errno()) 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; + BEGIN + NEW(f); f.workName := ""; COPY(name, 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); (* supports ~, ~user and blanks inside path *) + VAR i: INTEGER; ch: CHAR; home: ARRAY 256 OF CHAR; + BEGIN + i := 0; ch := Kernel.OBERON[pos]; + WHILE (ch = " ") OR (ch = ":") DO INC(pos); ch := Kernel.OBERON[pos] END ; + IF ch = "~" THEN + INC(pos); ch := Kernel.OBERON[pos]; + home := ""; Args.GetEnv("HOME", home); + WHILE home[i] # 0X DO dir[i] := home[i]; INC(i) END ; + IF (ch # "/") & (ch # 0X) & (ch # ":") & (ch # " ") THEN + WHILE (i > 0) & (dir[i-1] # "/") DO DEC(i) END + END + END ; + WHILE (ch # 0X) & (ch # ":") DO dir[i] := ch; INC(i); INC(pos); ch := Kernel.OBERON[pos] END ; + WHILE (i > 0) & (dir[i-1] = " ") DO DEC(i) 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, 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: INTEGER; done: BOOLEAN; + dir, path: ARRAY 256 OF CHAR; + stat: Unix.Status; + BEGIN + IF name # "" THEN + IF HasDir(name) THEN dir := ""; COPY(name, path) + ELSE + pos := 0; + COPY(name, path); (* -- noch *) + (*ScanPath(pos, dir);*) (*MakeFileName(dir, name, path);*) (*ScanPath(pos, dir)*) + END ; + LOOP + fd := Unix.Open(path, Unix.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(TRUE); + fd := Unix.Open(path, Unix.rdwr, {}); + done := fd >= 0; errno := Unix.errno(); + IF ~done & ((errno = Unix.ENFILE) OR (errno = Unix.EMFILE)) THEN Err("too many files open", f, errno) END + END ; + IF ~done & ((errno = Unix.EACCES) OR (errno = Unix.EROFS) OR (errno = Unix.EAGAIN)) THEN + (* errno EAGAIN observed on Solaris 2.4 *) + fd := Unix.Open(path, Unix.rdonly, {}); done := fd >= 0; errno := Unix.errno() + END ; +IF (~done) & (errno # Unix.ENOENT) THEN + Console.String("warning Files.Old "); Console.String(name); + Console.String(" errno = "); Console.Int(errno, 0); Console.Ln; +END ; + IF done THEN + res := Unix.Fstat(fd, 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", f, 0) + ELSE NEW(f); fileTab[fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.fd := fd; f.state := open; f.len := stat.size; f.pos := 0; f.swapper := -1; (*all f.buf[i] = NIL*) + COPY(name, 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)*) + RETURN NIL + 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, 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, 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: LONGINT; buf: Buffer; + 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.ReadBlk(f.fd, buf.data); + IF n < 0 THEN Err("read from file not done", f, Unix.errno()) 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 ReadByte* (VAR r : Rider; VAR x : ARRAY OF SYSTEM.BYTE); + BEGIN + ReadBytes(r, x, 1); + END ReadByte; + + 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 WriteByte* (VAR r : Rider; x : SYSTEM.BYTE); (* added for compatibility with PO 2013, -- noch *) + BEGIN + Write(r, x); + END WriteByte; + + 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 Delete* (name: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Unlink(name)); + res := SHORT(Unix.errno()) + 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; + BEGIN + r := Unix.Stat(old, ostat); + IF r >= 0 THEN + r := Unix.Stat(new, 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(old, new); + IF r < 0 THEN res := SHORT(Unix.errno()); + IF res = Unix.EXDEV THEN (* cross device link, move the file *) + fdold := Unix.Open(old, Unix.rdonly, {}); + IF fdold < 0 THEN res := 2; RETURN END ; + fdnew := Unix.Open(new, Unix.rdwr + Unix.creat + Unix.trunc, {2, 4,5, 7,8}); + IF fdnew < 0 THEN r := Unix.Close(fdold); res := 3; RETURN 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); + Err("cannot move file", NIL, errno) + END ; + n := Unix.Read(fdold, SYSTEM.ADR(buf), bufsize) + END ; + errno := Unix.errno(); + r := Unix.Close(fdold); r := Unix.Close(fdnew); + IF n = 0 THEN r := Unix.Unlink(old); res := 0 + ELSE Err("cannot move file", NIL, errno) + END ; + ELSE RETURN (* res is Unix.Rename return code *) + END + END ; + res := 0 + ELSE res := 2 (* old file not found *) + END + END Rename; + + PROCEDURE Register* (f: File); + VAR idx, errno: INTEGER; f1: File; file: ARRAY 104 OF CHAR; + BEGIN + IF (f.state = create) & (f.registerName # "") THEN f.state := close (* shortcut renaming *) END ; + 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 ChangeDirectory*(path: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Chdir(path)); + getcwd(Kernel.CWD) + END ChangeDirectory; + + PROCEDURE FlipBytes(VAR src, dest: ARRAY OF SYSTEM.BYTE); + VAR i, j: LONGINT; + BEGIN + IF ~Kernel.littleEndian THEN i := LEN(src); j := 0; + WHILE i > 0 DO DEC(i); dest[j] := src[i]; INC(j) END + ELSE SYSTEM.MOVE(SYSTEM.ADR(src), SYSTEM.ADR(dest), LEN(src)) + 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, b, 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, b, 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, b, 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, b, 4); FlipBytes(b, x) + END ReadReal; + + PROCEDURE ReadLReal* (VAR R: Rider; VAR x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN ReadBytes(R, b, 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; + + (* need to read line; -- noch *) + PROCEDURE ReadLine* (VAR R: Rider; VAR x: ARRAY OF CHAR); + VAR i: INTEGER; ch: CHAR; b : BOOLEAN; + BEGIN i := 0; + b := FALSE; + REPEAT + Read(R, ch); + IF ((ch = 0X) OR (ch = 0AX) OR (ch = 0DX)) THEN + b := TRUE + ELSE + x[i] := ch; + INC(i); + END; + UNTIL b + END ReadLine; + + 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, b, 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, b, 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, b, 4); + END WriteSet; + + PROCEDURE WriteReal* (VAR R: Rider; x: REAL); + VAR b: ARRAY 4 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 4) + END WriteReal; + + PROCEDURE WriteLReal* (VAR R: Rider; x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 8) + END WriteLReal; + + PROCEDURE WriteString* (VAR R: Rider; x: ARRAY [1] 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 GetName*(f: File; VAR name: ARRAY OF CHAR); + BEGIN + COPY (f.workName, name); + END GetName; + + PROCEDURE Finalize(o: SYSTEM.PTR); + VAR f: File; res: LONGINT; + BEGIN + f := SYSTEM.VAL(File, o); + IF f.fd >= 0 THEN + fileTab[f.fd] := 0; res := Unix.Close(f.fd); f.fd := -1; DEC(Kernel.nofiles); + IF f.tempFile THEN res := Unix.Unlink(f.workName) END + END + END Finalize; + + PROCEDURE Init; + VAR i: LONGINT; + BEGIN + i := 0; WHILE i < fileTabSize DO fileTab[i] := 0; INC(i) END ; + tempno := -1; Kernel.nofiles := 0 + END Init; + +BEGIN Init +END Files. diff --git a/src/lib/system/linux/clang/x86/Files0.Mod b/src/lib/system/linux/clang/x86/Files0.Mod new file mode 100644 index 00000000..4f021ede --- /dev/null +++ b/src/lib/system/linux/clang/x86/Files0.Mod @@ -0,0 +1,635 @@ +MODULE Files0; (* J. Templ 1.12. 89/12.4.95 Oberon files mapped onto Unix files *) + +(* this module is not for use by developers and inteded to bootstrap voc *) +(* for general use import Files module *) + + IMPORT SYSTEM, Unix, Kernel := Kernel0, Args, Console; + + (* 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 = 4096; + fileTabSize = 64; + noDesc = -1; + notDone = -1; + + (* file states *) + open = 0; create = 1; close = 2; + + + TYPE + FileName = ARRAY 101 OF CHAR; + File* = POINTER TO Handle; + Buffer = POINTER TO BufDesc; + + Handle = RECORD + workName, registerName: FileName; + tempFile: BOOLEAN; + dev, ino, 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 ; + + Time = POINTER TO TimeDesc; + TimeDesc = RECORD + sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: LONGINT; +(* sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: INTEGER;*) + END ; + + VAR + fileTab: ARRAY fileTabSize OF LONGINT (*=File*); + tempno: INTEGER; + +(* for localtime *) + PROCEDURE -includetime() + '#include "time.h"'; + + PROCEDURE -localtime(VAR clock: LONGINT): Time + "(Files0_Time) localtime(clock)"; + + PROCEDURE -getcwd(VAR cwd: Unix.Name) + "getcwd(cwd, cwd__len)"; + + PROCEDURE -IdxTrap "__HALT(-1)"; + + PROCEDURE^ Finalize(o: SYSTEM.PTR); + + PROCEDURE Err(s: ARRAY OF CHAR; f: File; errno: LONGINT); + BEGIN + Console.Ln; Console.String("-- "); Console.String(s); Console.String(": "); + IF f # NIL THEN + IF f.registerName # "" THEN Console.String(f.registerName) ELSE Console.String(f.workName) END + END ; + IF errno # 0 THEN Console.String(" errno = "); Console.Int(errno, 1) END ; + Console.Ln; + HALT(99) + END Err; + + 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 Kernel.CWD[i] # 0X DO name[i] := Kernel.CWD[i]; INC(i) END; + IF Kernel.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 Create(f: File); + VAR stat: Unix.Status; done: BOOLEAN; + errno: LONGINT; err: ARRAY 32 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 ; + errno := Unix.Unlink(f.workName); (*unlink first to avoid stale NFS handles and to avoid reuse of inodes*) + f.fd := Unix.Open(f.workName, Unix.rdwr + Unix.creat + Unix.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(TRUE); + f.fd := Unix.Open(f.workName, Unix.rdwr + Unix.creat + Unix.trunc, {2, 4,5, 7,8}); + done := f.fd >= 0 + END ; + IF done THEN + IF f.fd >= fileTabSize THEN errno := Unix.Close(f.fd); Err("too many files open", f, 0) + ELSE fileTab[f.fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.state := open; f.pos := 0; errno := Unix.Fstat(f.fd, 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" + ELSIF (errno = Unix.ENFILE) OR (errno = Unix.EMFILE) THEN err := "too many files open" + ELSE err := "file not created" + END ; + Err(err, f, errno) + END + END + END Create; + + PROCEDURE Flush(buf: Buffer); + VAR res: LONGINT; f: File; 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 Err("error in writing file", f, Unix.errno()) END ; + f.pos := buf.org + buf.size; + buf.chg := FALSE; + res := Unix.Fstat(f.fd, stat); + f.mtime := stat.mtime + END + END Flush; + + PROCEDURE Close* (f: File); + VAR i, res: LONGINT; + 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 < 0 THEN Err("error in writing file", f, Unix.errno()) 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; + BEGIN + NEW(f); f.workName := ""; COPY(name, 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); (* supports ~, ~user and blanks inside path *) + VAR i: INTEGER; ch: CHAR; home: ARRAY 256 OF CHAR; + BEGIN + i := 0; ch := Kernel.OBERON[pos]; + WHILE (ch = " ") OR (ch = ":") DO INC(pos); ch := Kernel.OBERON[pos] END ; + IF ch = "~" THEN + INC(pos); ch := Kernel.OBERON[pos]; + home := ""; Args.GetEnv("HOME", home); + WHILE home[i] # 0X DO dir[i] := home[i]; INC(i) END ; + IF (ch # "/") & (ch # 0X) & (ch # ":") & (ch # " ") THEN + WHILE (i > 0) & (dir[i-1] # "/") DO DEC(i) END + END + END ; + WHILE (ch # 0X) & (ch # ":") DO dir[i] := ch; INC(i); INC(pos); ch := Kernel.OBERON[pos] END ; + WHILE (i > 0) & (dir[i-1] = " ") DO DEC(i) 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, 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: INTEGER; done: BOOLEAN; + dir, path: ARRAY 256 OF CHAR; + stat: Unix.Status; + BEGIN + IF name # "" THEN + IF 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(path, Unix.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(TRUE); + fd := Unix.Open(path, Unix.rdwr, {}); + done := fd >= 0; errno := Unix.errno(); + IF ~done & ((errno = Unix.ENFILE) OR (errno = Unix.EMFILE)) THEN Err("too many files open", f, errno) END + END ; + IF ~done & ((errno = Unix.EACCES) OR (errno = Unix.EROFS) OR (errno = Unix.EAGAIN)) THEN + (* errno EAGAIN observed on Solaris 2.4 *) + fd := Unix.Open(path, Unix.rdonly, {}); done := fd >= 0; errno := Unix.errno() + END ; +IF (~done) & (errno # Unix.ENOENT) THEN + Console.String("warning Files0.Old "); Console.String(name); + Console.String(" errno = "); Console.Int(errno, 0); Console.Ln; +END ; + IF done THEN + res := Unix.Fstat(fd, 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", f, 0) + ELSE NEW(f); fileTab[fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.fd := fd; f.state := open; f.len := stat.size; f.pos := 0; f.swapper := -1; (*all f.buf[i] = NIL*) + COPY(name, 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, 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, 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: LONGINT; buf: Buffer; + 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.ReadBlk(f.fd, buf.data); + IF n < 0 THEN Err("read from file not done", f, Unix.errno()) 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 ReadByte* (VAR r : Rider; VAR x : ARRAY OF SYSTEM.BYTE); + BEGIN + ReadBytes(r, x, 1); + END ReadByte; + + 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 Delete* (name: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Unlink(name)); + res := SHORT(Unix.errno()) + 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; + BEGIN + r := Unix.Stat(old, ostat); + IF r >= 0 THEN + r := Unix.Stat(new, 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(old, new); + IF r < 0 THEN res := SHORT(Unix.errno()); + IF res = Unix.EXDEV THEN (* cross device link, move the file *) + fdold := Unix.Open(old, Unix.rdonly, {}); + IF fdold < 0 THEN res := 2; RETURN END ; + fdnew := Unix.Open(new, Unix.rdwr + Unix.creat + Unix.trunc, {2, 4,5, 7,8}); + IF fdnew < 0 THEN r := Unix.Close(fdold); res := 3; RETURN 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); + Err("cannot move file", NIL, errno) + END ; + n := Unix.Read(fdold, SYSTEM.ADR(buf), bufsize) + END ; + errno := Unix.errno(); + r := Unix.Close(fdold); r := Unix.Close(fdnew); + IF n = 0 THEN r := Unix.Unlink(old); res := 0 + ELSE Err("cannot move file", NIL, errno) + END ; + ELSE RETURN (* res is Unix.Rename return code *) + END + END ; + res := 0 + ELSE res := 2 (* old file not found *) + END + END Rename; + + PROCEDURE Register* (f: File); + VAR idx, errno: INTEGER; f1: File; file: ARRAY 104 OF CHAR; + BEGIN + IF (f.state = create) & (f.registerName # "") THEN f.state := close (* shortcut renaming *) END ; + 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 ChangeDirectory*(path: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Chdir(path)); + getcwd(Kernel.CWD) + END ChangeDirectory; + + PROCEDURE FlipBytes(VAR src, dest: ARRAY OF SYSTEM.BYTE); + VAR i, j: LONGINT; + BEGIN + IF ~Kernel.littleEndian THEN i := LEN(src); j := 0; + WHILE i > 0 DO DEC(i); dest[j] := src[i]; INC(j) END + ELSE SYSTEM.MOVE(SYSTEM.ADR(src), SYSTEM.ADR(dest), LEN(src)) + 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, b, 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, b, 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, b, 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, b, 4); FlipBytes(b, x) + END ReadReal; + + PROCEDURE ReadLReal* (VAR R: Rider; VAR x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN ReadBytes(R, b, 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, b, 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, b, 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, b, 4); + END WriteSet; + + PROCEDURE WriteReal* (VAR R: Rider; x: REAL); + VAR b: ARRAY 4 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 4) + END WriteReal; + + PROCEDURE WriteLReal* (VAR R: Rider; x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 8) + END WriteLReal; + + PROCEDURE WriteString* (VAR R: Rider; x: ARRAY [1] 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 Finalize(o: SYSTEM.PTR); + VAR f: File; res: LONGINT; + BEGIN + f := SYSTEM.VAL(File, o); + IF f.fd >= 0 THEN + fileTab[f.fd] := 0; res := Unix.Close(f.fd); f.fd := -1; DEC(Kernel.nofiles); + IF f.tempFile THEN res := Unix.Unlink(f.workName) END + END + END Finalize; + + PROCEDURE Init; + VAR i: LONGINT; + BEGIN + i := 0; WHILE i < fileTabSize DO fileTab[i] := 0; INC(i) END ; + tempno := -1; Kernel.nofiles := 0 + END Init; + +BEGIN Init +END Files0. diff --git a/src/lib/system/linux/clang/x86/SYSTEM.c0 b/src/lib/system/linux/clang/x86/SYSTEM.c0 index a5599acc..580449aa 100644 --- a/src/lib/system/linux/clang/x86/SYSTEM.c0 +++ b/src/lib/system/linux/clang/x86/SYSTEM.c0 @@ -20,7 +20,7 @@ #include "varargs.h" #endif -extern void *malloc(unsigned int size); +extern void *malloc(long size); extern void exit(int status); void (*SYSTEM_Halt)(); diff --git a/src/lib/system/linux/clang/x86/SYSTEM.h b/src/lib/system/linux/clang/x86/SYSTEM.h index 0c7b19af..719a6d18 100644 --- a/src/lib/system/linux/clang/x86/SYSTEM.h +++ b/src/lib/system/linux/clang/x86/SYSTEM.h @@ -15,7 +15,7 @@ uses double # as concatenation operator //extern void *memcpy(void *dest, const void *src, long n); extern void *memcpy(void *dest, const void *src, size_t n); -extern void *malloc(unsigned int size); +extern void *malloc(long size); extern void exit(int status); #define export diff --git a/src/lib/system/linux/clang/x86_64/Files.Mod b/src/lib/system/linux/clang/x86_64/Files.Mod new file mode 100644 index 00000000..c8f42ca5 --- /dev/null +++ b/src/lib/system/linux/clang/x86_64/Files.Mod @@ -0,0 +1,664 @@ +MODULE Files; (* J. Templ 1.12. 89/12.4.95 Oberon files mapped onto Unix files *) +(* modified version of Files, which opens only the file provided and does not scan any path in any environment variable, also ReadLine procedure added; -- noch *) + IMPORT SYSTEM, Unix, Kernel, Args, Console; + + (* 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 = 4096; + fileTabSize = 64; + noDesc = -1; + notDone = -1; + + (* file states *) + open = 0; create = 1; close = 2; + + + TYPE + FileName = ARRAY 101 OF CHAR; + File* = POINTER TO Handle; + Buffer = POINTER TO BufDesc; + + Handle = RECORD + workName, registerName: FileName; + tempFile: BOOLEAN; + dev, ino, mtime: LONGINT; + fd-: INTEGER; + 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 ; + + Time = POINTER TO TimeDesc; + TimeDesc = RECORD + sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: LONGINT; +(* sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: INTEGER;*) + END ; + + VAR + fileTab: ARRAY fileTabSize OF LONGINT (*=File*); + tempno: INTEGER; + +(* for localtime *) + PROCEDURE -includetime() + '#include "time.h"'; + + PROCEDURE -localtime(VAR clock: LONGINT): Time + "(Files_Time) localtime(clock)"; + + PROCEDURE -getcwd(VAR cwd: Unix.Name) + "getcwd(cwd, cwd__len)"; + + PROCEDURE -IdxTrap "__HALT(-1)"; + + PROCEDURE^ Finalize(o: SYSTEM.PTR); + + PROCEDURE Err(s: ARRAY OF CHAR; f: File; errno: LONGINT); + BEGIN + Console.Ln; Console.String("-- "); Console.String(s); Console.String(": "); + IF f # NIL THEN + IF f.registerName # "" THEN Console.String(f.registerName) ELSE Console.String(f.workName) END + END ; + IF errno # 0 THEN Console.String(" errno = "); Console.Int(errno, 1) END ; + Console.Ln; + HALT(99) + END Err; + + 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 Kernel.CWD[i] # 0X DO name[i] := Kernel.CWD[i]; INC(i) END; + IF Kernel.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 Create(f: File); + VAR stat: Unix.Status; done: BOOLEAN; + errno: LONGINT; err: ARRAY 32 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 ; + errno := Unix.Unlink(f.workName); (*unlink first to avoid stale NFS handles and to avoid reuse of inodes*) + f.fd := Unix.Open(f.workName, SHORT(SYSTEM.VAL(LONGINT, Unix.rdwr + Unix.creat + Unix.trunc)), SHORT(SYSTEM.VAL(LONGINT, {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(TRUE); + f.fd := Unix.Open(f.workName, SHORT(SYSTEM.VAL(LONGINT, Unix.rdwr + Unix.creat + Unix.trunc)), SHORT(SYSTEM.VAL(LONGINT, {2, 4,5, 7,8}))); + done := f.fd >= 0 + END ; + IF done THEN + IF f.fd >= fileTabSize THEN errno := Unix.Close(f.fd); Err("too many files open", f, 0) + ELSE fileTab[f.fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.state := open; f.pos := 0; errno := Unix.Fstat(f.fd, 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" + ELSIF (errno = Unix.ENFILE) OR (errno = Unix.EMFILE) THEN err := "too many files open" + ELSE err := "file not created" + END ; + Err(err, f, errno) + END + END + END Create; + + PROCEDURE Flush(buf: Buffer); + VAR res: LONGINT; f: File; 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 Err("error in writing file", f, Unix.errno()) END ; + f.pos := buf.org + buf.size; + buf.chg := FALSE; + res := Unix.Fstat(f.fd, stat); + f.mtime := stat.mtime + END + END Flush; + + PROCEDURE Close* (f: File); + VAR i, res: LONGINT; + 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 < 0 THEN Err("error in writing file", f, Unix.errno()) 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; + BEGIN + NEW(f); f.workName := ""; COPY(name, 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); (* supports ~, ~user and blanks inside path *) + VAR i: INTEGER; ch: CHAR; home: ARRAY 256 OF CHAR; + BEGIN + i := 0; ch := Kernel.OBERON[pos]; + WHILE (ch = " ") OR (ch = ":") DO INC(pos); ch := Kernel.OBERON[pos] END ; + IF ch = "~" THEN + INC(pos); ch := Kernel.OBERON[pos]; + home := ""; Args.GetEnv("HOME", home); + WHILE home[i] # 0X DO dir[i] := home[i]; INC(i) END ; + IF (ch # "/") & (ch # 0X) & (ch # ":") & (ch # " ") THEN + WHILE (i > 0) & (dir[i-1] # "/") DO DEC(i) END + END + END ; + WHILE (ch # 0X) & (ch # ":") DO dir[i] := ch; INC(i); INC(pos); ch := Kernel.OBERON[pos] END ; + WHILE (i > 0) & (dir[i-1] = " ") DO DEC(i) 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, 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: INTEGER; res, errno: LONGINT; pos: INTEGER; done: BOOLEAN; + dir, path: ARRAY 256 OF CHAR; + stat: Unix.Status; + BEGIN + IF name # "" THEN + IF HasDir(name) THEN dir := ""; COPY(name, path) + ELSE + pos := 0; + COPY(name, path); (* -- noch *) + (*ScanPath(pos, dir);*) (*MakeFileName(dir, name, path);*) (*ScanPath(pos, dir)*) + END ; + LOOP + fd := Unix.Open(path, SHORT(SYSTEM.VAL(LONGINT, Unix.rdwr)), SHORT(SYSTEM.VAL(LONGINT, {}))); 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(TRUE); + fd := Unix.Open(path, SHORT(SYSTEM.VAL(LONGINT, Unix.rdwr)), SHORT(SYSTEM.VAL(LONGINT, {}))); + done := fd >= 0; errno := Unix.errno(); + IF ~done & ((errno = Unix.ENFILE) OR (errno = Unix.EMFILE)) THEN Err("too many files open", f, errno) END + END ; + IF ~done & ((errno = Unix.EACCES) OR (errno = Unix.EROFS) OR (errno = Unix.EAGAIN)) THEN + (* errno EAGAIN observed on Solaris 2.4 *) + fd := Unix.Open(path, SHORT(SYSTEM.VAL(LONGINT, Unix.rdonly)), SHORT(SYSTEM.VAL(LONGINT, {}))); done := fd >= 0; errno := Unix.errno() + END ; +IF (~done) & (errno # Unix.ENOENT) THEN + Console.String("warning Files.Old "); Console.String(name); + Console.String(" errno = "); Console.Int(errno, 0); Console.Ln; +END ; + IF done THEN + res := Unix.Fstat(fd, 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", f, 0) + ELSE NEW(f); fileTab[fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.fd := fd; f.state := open; f.len := stat.size; f.pos := 0; f.swapper := -1; (*all f.buf[i] = NIL*) + COPY(name, 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)*) + RETURN NIL + 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, 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, 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: LONGINT; buf: Buffer; + 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.ReadBlk(f.fd, buf.data); + IF n < 0 THEN Err("read from file not done", f, Unix.errno()) 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 ReadByte* (VAR r : Rider; VAR x : ARRAY OF SYSTEM.BYTE); + BEGIN + ReadBytes(r, x, 1); + END ReadByte; + + 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 WriteByte* (VAR r : Rider; x : SYSTEM.BYTE); (* added for compatibility with PO 2013, -- noch *) + BEGIN + Write(r, x); + END WriteByte; + + 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 Delete* (name: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Unlink(name)); + res := SHORT(Unix.errno()) + END Delete; + + PROCEDURE Rename* (old, new: ARRAY OF CHAR; VAR res: INTEGER); + VAR fdold, fdnew: INTEGER; n, errno, r: LONGINT; + ostat, nstat: Unix.Status; + buf: ARRAY 4096 OF CHAR; + BEGIN + r := Unix.Stat(old, ostat); + IF r >= 0 THEN + r := Unix.Stat(new, 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(old, new); + IF r < 0 THEN res := SHORT(Unix.errno()); + IF res = Unix.EXDEV THEN (* cross device link, move the file *) + fdold := Unix.Open(old, SHORT(SYSTEM.VAL(LONGINT, Unix.rdonly)), SHORT(SYSTEM.VAL(LONGINT, {}))); + IF fdold < 0 THEN res := 2; RETURN END ; + fdnew := Unix.Open(new, SHORT(SYSTEM.VAL(LONGINT, Unix.rdwr + Unix.creat + Unix.trunc)), SHORT(SYSTEM.VAL(LONGINT, {2, 4,5, 7,8}))); + IF fdnew < 0 THEN r := Unix.Close(fdold); res := 3; RETURN 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); + Err("cannot move file", NIL, errno) + END ; + n := Unix.Read(fdold, SYSTEM.ADR(buf), bufsize) + END ; + errno := Unix.errno(); + r := Unix.Close(fdold); r := Unix.Close(fdnew); + IF n = 0 THEN r := Unix.Unlink(old); res := 0 + ELSE Err("cannot move file", NIL, errno) + END ; + ELSE RETURN (* res is Unix.Rename return code *) + END + END ; + res := 0 + ELSE res := 2 (* old file not found *) + END + END Rename; + + PROCEDURE Register* (f: File); + VAR idx, errno: INTEGER; f1: File; file: ARRAY 104 OF CHAR; + BEGIN + IF (f.state = create) & (f.registerName # "") THEN f.state := close (* shortcut renaming *) END ; + 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 ChangeDirectory*(path: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Chdir(path)); + getcwd(Kernel.CWD) + END ChangeDirectory; + + PROCEDURE FlipBytes(VAR src, dest: ARRAY OF SYSTEM.BYTE); + VAR i, j: LONGINT; + BEGIN + IF ~Kernel.littleEndian THEN i := LEN(src); j := 0; + WHILE i > 0 DO DEC(i); dest[j] := src[i]; INC(j) END + ELSE SYSTEM.MOVE(SYSTEM.ADR(src), SYSTEM.ADR(dest), LEN(src)) + 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, b, 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, b, 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, b, 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, b, 4); FlipBytes(b, x) + END ReadReal; + + PROCEDURE ReadLReal* (VAR R: Rider; VAR x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN ReadBytes(R, b, 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; + + (* need to read line; -- noch *) + PROCEDURE ReadLine* (VAR R: Rider; VAR x: ARRAY OF CHAR); + VAR i: INTEGER; ch: CHAR; b : BOOLEAN; + BEGIN i := 0; + b := FALSE; + REPEAT + Read(R, ch); + IF ((ch = 0X) OR (ch = 0AX) OR (ch = 0DX)) THEN + b := TRUE + ELSE + x[i] := ch; + INC(i); + END; + UNTIL b + END ReadLine; + + 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, b, 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, b, 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, b, 4); + END WriteSet; + + PROCEDURE WriteReal* (VAR R: Rider; x: REAL); + VAR b: ARRAY 4 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 4) + END WriteReal; + + PROCEDURE WriteLReal* (VAR R: Rider; x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 8) + END WriteLReal; + + PROCEDURE WriteString* (VAR R: Rider; x: ARRAY [1] 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 GetName*(f: File; VAR name: ARRAY OF CHAR); + BEGIN + COPY (f.workName, name); + END GetName; + + PROCEDURE Finalize(o: SYSTEM.PTR); + VAR f: File; res: LONGINT; + BEGIN + f := SYSTEM.VAL(File, o); + IF f.fd >= 0 THEN + fileTab[f.fd] := 0; res := Unix.Close(f.fd); f.fd := -1; DEC(Kernel.nofiles); + IF f.tempFile THEN res := Unix.Unlink(f.workName) END + END + END Finalize; + + PROCEDURE Init; + VAR i: LONGINT; + BEGIN + i := 0; WHILE i < fileTabSize DO fileTab[i] := 0; INC(i) END ; + tempno := -1; Kernel.nofiles := 0 + END Init; + +BEGIN Init +END Files. diff --git a/src/lib/system/linux/clang/x86_64/Files0.Mod b/src/lib/system/linux/clang/x86_64/Files0.Mod new file mode 100644 index 00000000..1d9cd953 --- /dev/null +++ b/src/lib/system/linux/clang/x86_64/Files0.Mod @@ -0,0 +1,636 @@ +MODULE Files0; (* J. Templ 1.12. 89/12.4.95 Oberon files mapped onto Unix files *) + +(* this module is not for use by developers and inteded to bootstrap voc *) +(* for general use import Files module *) + + IMPORT SYSTEM, Unix, Kernel := Kernel0, Args, Console; + + (* 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 = 4096; + fileTabSize = 64; + noDesc = -1; + notDone = -1; + + (* file states *) + open = 0; create = 1; close = 2; + + + TYPE + FileName = ARRAY 101 OF CHAR; + File* = POINTER TO Handle; + Buffer = POINTER TO BufDesc; + + Handle = RECORD + workName, registerName: FileName; + tempFile: BOOLEAN; + dev, ino, mtime: LONGINT; + fd-: INTEGER; + 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 ; + + Time = POINTER TO TimeDesc; + TimeDesc = RECORD + sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: LONGINT; +(* sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: INTEGER;*) + END ; + + VAR + fileTab: ARRAY fileTabSize OF LONGINT (*=File*); + tempno: INTEGER; + +(* for localtime *) + PROCEDURE -includetime() + '#include "time.h"'; + + PROCEDURE -localtime(VAR clock: LONGINT): Time + "(Files0_Time) localtime(clock)"; + + PROCEDURE -getcwd(VAR cwd: Unix.Name) + "getcwd(cwd, cwd__len)"; + + PROCEDURE -IdxTrap "__HALT(-1)"; + + PROCEDURE^ Finalize(o: SYSTEM.PTR); + + PROCEDURE Err(s: ARRAY OF CHAR; f: File; errno: LONGINT); + BEGIN + Console.Ln; Console.String("-- "); Console.String(s); Console.String(": "); + IF f # NIL THEN + IF f.registerName # "" THEN Console.String(f.registerName) ELSE Console.String(f.workName) END + END ; + IF errno # 0 THEN Console.String(" errno = "); Console.Int(errno, 1) END ; + Console.Ln; + HALT(99) + END Err; + + 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 Kernel.CWD[i] # 0X DO name[i] := Kernel.CWD[i]; INC(i) END; + IF Kernel.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 Create(f: File); + VAR stat: Unix.Status; done: BOOLEAN; + errno: LONGINT; err: ARRAY 32 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 ; + errno := Unix.Unlink(f.workName); (*unlink first to avoid stale NFS handles and to avoid reuse of inodes*) +f.fd := Unix.Open(f.workName, SHORT(SYSTEM.VAL(LONGINT, (Unix.rdwr + Unix.creat + Unix.trunc))), SHORT(SYSTEM.VAL(LONGINT, ({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(TRUE); + f.fd := Unix.Open(f.workName, SHORT(SYSTEM.VAL(LONGINT, (Unix.rdwr + Unix.creat + Unix.trunc))), SHORT(SYSTEM.VAL(LONGINT, {2, 4,5, 7,8}))); + done := f.fd >= 0 + END ; + IF done THEN + IF f.fd >= fileTabSize THEN errno := Unix.Close(f.fd); Err("too many files open", f, 0) + ELSE fileTab[f.fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.state := open; f.pos := 0; errno := Unix.Fstat(f.fd, 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" + ELSIF (errno = Unix.ENFILE) OR (errno = Unix.EMFILE) THEN err := "too many files open" + ELSE err := "file not created" + END ; + Err(err, f, errno) + END + END + END Create; + + PROCEDURE Flush(buf: Buffer); + VAR res: LONGINT; f: File; 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 Err("error in writing file", f, Unix.errno()) END ; + f.pos := buf.org + buf.size; + buf.chg := FALSE; + res := Unix.Fstat(f.fd, stat); + f.mtime := stat.mtime + END + END Flush; + + PROCEDURE Close* (f: File); + VAR i, res: LONGINT; + 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 < 0 THEN Err("error in writing file", f, Unix.errno()) 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; + BEGIN + NEW(f); f.workName := ""; COPY(name, 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); (* supports ~, ~user and blanks inside path *) + VAR i: INTEGER; ch: CHAR; home: ARRAY 256 OF CHAR; + BEGIN + i := 0; ch := Kernel.OBERON[pos]; + WHILE (ch = " ") OR (ch = ":") DO INC(pos); ch := Kernel.OBERON[pos] END ; + IF ch = "~" THEN + INC(pos); ch := Kernel.OBERON[pos]; + home := ""; Args.GetEnv("HOME", home); + WHILE home[i] # 0X DO dir[i] := home[i]; INC(i) END ; + IF (ch # "/") & (ch # 0X) & (ch # ":") & (ch # " ") THEN + WHILE (i > 0) & (dir[i-1] # "/") DO DEC(i) END + END + END ; + WHILE (ch # 0X) & (ch # ":") DO dir[i] := ch; INC(i); INC(pos); ch := Kernel.OBERON[pos] END ; + WHILE (i > 0) & (dir[i-1] = " ") DO DEC(i) 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, 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: INTEGER; res, errno: LONGINT; pos: INTEGER; done: BOOLEAN; + dir, path: ARRAY 256 OF CHAR; + stat: Unix.Status; + BEGIN + IF name # "" THEN + IF 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(path, SHORT(SYSTEM.VAL(LONGINT, Unix.rdwr)), SHORT(SYSTEM.VAL(LONGINT, {}))); 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(TRUE); + fd := Unix.Open(path, SHORT(SYSTEM.VAL(LONGINT, Unix.rdwr)), SHORT(SYSTEM.VAL(LONGINT, {}))); + done := fd >= 0; errno := Unix.errno(); + IF ~done & ((errno = Unix.ENFILE) OR (errno = Unix.EMFILE)) THEN Err("too many files open", f, errno) END + END ; + IF ~done & ((errno = Unix.EACCES) OR (errno = Unix.EROFS) OR (errno = Unix.EAGAIN)) THEN + (* errno EAGAIN observed on Solaris 2.4 *) + fd := Unix.Open(path, SHORT(SYSTEM.VAL(LONGINT, Unix.rdonly)), SHORT(SYSTEM.VAL(LONGINT, {}))); done := fd >= 0; errno := Unix.errno() + END ; +IF (~done) & (errno # Unix.ENOENT) THEN + Console.String("warning Files0.Old "); Console.String(name); + Console.String(" errno = "); Console.Int(errno, 0); Console.Ln; +END ; + IF done THEN + res := Unix.Fstat(fd, 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", f, 0) + ELSE NEW(f); fileTab[fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.fd := fd; f.state := open; f.len := stat.size; f.pos := 0; f.swapper := -1; (*all f.buf[i] = NIL*) + COPY(name, 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, 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, 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: LONGINT; buf: Buffer; + 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.ReadBlk(f.fd, buf.data); + IF n < 0 THEN Err("read from file not done", f, Unix.errno()) 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 ReadByte* (VAR r : Rider; VAR x : ARRAY OF SYSTEM.BYTE); + BEGIN + ReadBytes(r, x, 1); + END ReadByte; + + 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 Delete* (name: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Unlink(name)); + res := SHORT(Unix.errno()) + END Delete; + + PROCEDURE Rename* (old, new: ARRAY OF CHAR; VAR res: INTEGER); + VAR fdold, fdnew: INTEGER; n, errno, r: LONGINT; + ostat, nstat: Unix.Status; + buf: ARRAY 4096 OF CHAR; + BEGIN + r := Unix.Stat(old, ostat); + IF r >= 0 THEN + r := Unix.Stat(new, 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(old, new); + IF r < 0 THEN res := SHORT(Unix.errno()); + IF res = Unix.EXDEV THEN (* cross device link, move the file *) + fdold := Unix.Open(old, SHORT(SYSTEM.VAL(LONGINT, Unix.rdonly)), SHORT(SYSTEM.VAL(LONGINT, {}))); + IF fdold < 0 THEN res := 2; RETURN END ; + fdnew := Unix.Open(new, SHORT(SYSTEM.VAL(LONGINT, Unix.rdwr + Unix.creat + Unix.trunc)), SHORT(SYSTEM.VAL(LONGINT, {2, 4,5, 7,8}))); + IF fdnew < 0 THEN r := Unix.Close(fdold); res := 3; RETURN 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); + Err("cannot move file", NIL, errno) + END ; + n := Unix.Read(fdold, SYSTEM.ADR(buf), bufsize) + END ; + errno := Unix.errno(); + r := Unix.Close(fdold); r := Unix.Close(fdnew); + IF n = 0 THEN r := Unix.Unlink(old); res := 0 + ELSE Err("cannot move file", NIL, errno) + END ; + ELSE RETURN (* res is Unix.Rename return code *) + END + END ; + res := 0 + ELSE res := 2 (* old file not found *) + END + END Rename; + + PROCEDURE Register* (f: File); + VAR idx, errno: INTEGER; f1: File; file: ARRAY 104 OF CHAR; + BEGIN + IF (f.state = create) & (f.registerName # "") THEN f.state := close (* shortcut renaming *) END ; + 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 ChangeDirectory*(path: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Chdir(path)); + getcwd(Kernel.CWD) + END ChangeDirectory; + + PROCEDURE FlipBytes(VAR src, dest: ARRAY OF SYSTEM.BYTE); + VAR i, j: LONGINT; + BEGIN + IF ~Kernel.littleEndian THEN i := LEN(src); j := 0; + WHILE i > 0 DO DEC(i); dest[j] := src[i]; INC(j) END + ELSE SYSTEM.MOVE(SYSTEM.ADR(src), SYSTEM.ADR(dest), LEN(src)) + 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, b, 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, b, 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, b, 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, b, 4); FlipBytes(b, x) + END ReadReal; + + PROCEDURE ReadLReal* (VAR R: Rider; VAR x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN ReadBytes(R, b, 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, b, 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, b, 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, b, 4); + END WriteSet; + + PROCEDURE WriteReal* (VAR R: Rider; x: REAL); + VAR b: ARRAY 4 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 4) + END WriteReal; + + PROCEDURE WriteLReal* (VAR R: Rider; x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 8) + END WriteLReal; + + PROCEDURE WriteString* (VAR R: Rider; x: ARRAY [1] 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 Finalize(o: SYSTEM.PTR); + VAR f: File; res: LONGINT; + BEGIN + f := SYSTEM.VAL(File, o); + IF f.fd >= 0 THEN + fileTab[f.fd] := 0; res := Unix.Close(f.fd); f.fd := -1; DEC(Kernel.nofiles); + IF f.tempFile THEN res := Unix.Unlink(f.workName) END + END + END Finalize; + + PROCEDURE Init; + VAR i: LONGINT; + BEGIN + i := 0; WHILE i < fileTabSize DO fileTab[i] := 0; INC(i) END ; + tempno := -1; Kernel.nofiles := 0 + END Init; + +BEGIN Init +END Files0. diff --git a/src/lib/system/linux/clang/x86_64/Unix.Mod b/src/lib/system/linux/clang/x86_64/Unix.Mod index 3e477d69..195bb41d 100644 --- a/src/lib/system/linux/clang/x86_64/Unix.Mod +++ b/src/lib/system/linux/clang/x86_64/Unix.Mod @@ -292,16 +292,18 @@ from man gettimeofday END ; Sockaddr* = RECORD - family*: INTEGER; - port*: INTEGER; - internetAddr*: LONGINT; - pad*: ARRAY 8 OF CHAR; + family0*, family1*: SHORTINT; + pad0, pad1: SHORTINT; + pad2 : INTEGER; + (*port*: INTEGER; + internetAddr*: LONGINT;*) + pad*: ARRAY 14 OF CHAR; END ; HostEntry* = POINTER [1] TO Hostent; Hostent* = RECORD name*, aliases*: LONGINT; - addrtype*, length*: LONGINT; + addrtype*, length*: INTEGER; addrlist*: LONGINT; (*POINTER TO POINTER TO LONGINT, network byte order*) END; @@ -321,7 +323,7 @@ from man gettimeofday PROCEDURE -includeStdlib() "#include "; - (* for nanosleep() *) + (* for nanosleep() *) PROCEDURE -includeTime() "#include "; @@ -337,71 +339,71 @@ from man gettimeofday RETURN err() END errno; - PROCEDURE -Exit*(n: LONGINT) + PROCEDURE -Exit*(n: INTEGER) "exit(n)"; - PROCEDURE -Fork*(): LONGINT + PROCEDURE -Fork*(): INTEGER "fork()"; - PROCEDURE -Wait*(VAR status: LONGINT): LONGINT + PROCEDURE -Wait*(VAR status: INTEGER): INTEGER "wait(status)"; - PROCEDURE -Select*(width: LONGINT; VAR readfds, writefds, exceptfds: FdSet; VAR timeout: Timeval): LONGINT + PROCEDURE -Select*(width: INTEGER; VAR readfds, writefds, exceptfds: FdSet; VAR timeout: Timeval): INTEGER "select(width, readfds, writefds, exceptfds, timeout)"; - PROCEDURE -Gettimeofday* (VAR tv: Timeval; VAR tz: Timezone) : LONGINT + PROCEDURE -Gettimeofday* (VAR tv: Timeval; VAR tz: Timezone) : INTEGER "gettimeofday(tv, tz)"; - PROCEDURE -Read* (fd, buf, nbyte: LONGINT): LONGINT + PROCEDURE -Read* (fd: INTEGER; buf, nbyte: LONGINT): LONGINT "read(fd, buf, nbyte)"; - PROCEDURE -ReadBlk* (fd: LONGINT; VAR buf: ARRAY OF SYSTEM.BYTE): LONGINT + PROCEDURE -ReadBlk* (fd: INTEGER; VAR buf: ARRAY OF SYSTEM.BYTE): LONGINT "read(fd, buf, buf__len)"; - PROCEDURE -Write* (fd, buf, nbyte: LONGINT): LONGINT + PROCEDURE -Write* (fd: INTEGER; buf, nbyte: LONGINT): LONGINT "write(fd, buf, nbyte)"; - PROCEDURE -WriteBlk* (fd: LONGINT; VAR buf: ARRAY OF SYSTEM.BYTE): LONGINT + PROCEDURE -WriteBlk* (fd: INTEGER; VAR buf: ARRAY OF SYSTEM.BYTE): LONGINT "write(fd, buf, buf__len)"; - PROCEDURE -Dup*(fd: LONGINT): LONGINT + PROCEDURE -Dup*(fd: INTEGER): INTEGER "dup(fd)"; - PROCEDURE -Dup2*(fd1, fd2: LONGINT): LONGINT + PROCEDURE -Dup2*(fd1, fd2: INTEGER): INTEGER "dup(fd1, fd2)"; - PROCEDURE -Pipe*(fds : LONGINT): LONGINT + PROCEDURE -Pipe*(fds : LONGINT): INTEGER "pipe(fds)"; - PROCEDURE -Getpid*(): LONGINT + PROCEDURE -Getpid*(): INTEGER "getpid()"; - PROCEDURE -Getuid*(): LONGINT + PROCEDURE -Getuid*(): INTEGER "getuid()"; - PROCEDURE -Geteuid*(): LONGINT + PROCEDURE -Geteuid*(): INTEGER "geteuid()"; - PROCEDURE -Getgid*(): LONGINT + PROCEDURE -Getgid*(): INTEGER "getgid()"; - PROCEDURE -Getegid*(): LONGINT + PROCEDURE -Getegid*(): INTEGER "getegid()"; - PROCEDURE -Unlink*(name: Name): LONGINT + PROCEDURE -Unlink*(name: Name): INTEGER "unlink(name)"; - PROCEDURE -Open*(name: Name; flag, mode: SET): LONGINT + PROCEDURE -Open*(name: Name; flag: INTEGER; mode: LONGINT): INTEGER "open(name, flag, mode)"; - PROCEDURE -Close*(fd: LONGINT): LONGINT + PROCEDURE -Close*(fd: INTEGER): INTEGER "close(fd)"; - PROCEDURE -stat(name: Name; VAR statbuf: Status): LONGINT + PROCEDURE -stat(name: Name; VAR statbuf: Status): INTEGER "stat((const char*)name, (struct stat*)statbuf)"; - PROCEDURE Stat*(name: Name; VAR statbuf: Status): LONGINT; - VAR res: LONGINT; + PROCEDURE Stat*(name: Name; VAR statbuf: Status): INTEGER; + VAR res: INTEGER; BEGIN res := stat(name, statbuf); (* make the first 4 bytes as unique as possible (used in module Files for caching!) *) @@ -411,11 +413,11 @@ from man gettimeofday RETURN res; END Stat; - PROCEDURE -fstat(fd: LONGINT; VAR statbuf: Status): LONGINT + PROCEDURE -fstat(fd: INTEGER; VAR statbuf: Status): INTEGER "fstat(fd, (struct stat*)statbuf)"; - PROCEDURE Fstat*(fd: LONGINT; VAR statbuf: Status): LONGINT; - VAR res: LONGINT; + PROCEDURE Fstat*(fd: INTEGER; VAR statbuf: Status): INTEGER; + VAR res: INTEGER; BEGIN res := fstat(fd, statbuf); (* make the first 4 bytes as unique as possible (used in module Files for caching!) *) @@ -424,47 +426,47 @@ from man gettimeofday RETURN res; END Fstat; - PROCEDURE -Fchmod*(fd, mode: LONGINT): LONGINT + PROCEDURE -Fchmod*(fd, mode: INTEGER): INTEGER "fchmod(fd, mode)"; - PROCEDURE -Chmod*(path: Name; mode: LONGINT): LONGINT + PROCEDURE -Chmod*(path: Name; mode: INTEGER): INTEGER "chmod(path, mode)"; - PROCEDURE -Lseek*(fd, offset, origin: LONGINT): LONGINT + PROCEDURE -Lseek*(fd: INTEGER; offset: LONGINT; origin: INTEGER): LONGINT "lseek(fd, offset, origin)"; - PROCEDURE -Fsync*(fd: LONGINT): LONGINT + PROCEDURE -Fsync*(fd: INTEGER): INTEGER "fsync(fd)"; - PROCEDURE -Fcntl*(fd, cmd, arg: LONGINT ): LONGINT + PROCEDURE -Fcntl*(fd: INTEGER; cmd: INTEGER; arg: LONGINT ): INTEGER "fcntl(fd, cmd, arg)"; - PROCEDURE -Flock*(fd, operation: LONGINT): LONGINT + PROCEDURE -Flock*(fd, operation: INTEGER): INTEGER "flock(fd, operation)"; - PROCEDURE -Ftruncate*(fd, length: LONGINT): LONGINT + PROCEDURE -Ftruncate*(fd: INTEGER; length: LONGINT): INTEGER "ftruncate(fd, length)"; - PROCEDURE -Readblk*(fd: LONGINT; VAR buf: ARRAY OF SYSTEM.BYTE; len: LONGINT): LONGINT + PROCEDURE -Readblk*(fd: INTEGER; VAR buf: ARRAY OF SYSTEM.BYTE; len: LONGINT): LONGINT "read(fd, buf, len)"; - PROCEDURE -Rename*(old, new: Name): LONGINT + PROCEDURE -Rename*(old, new: Name): INTEGER "rename(old, new)"; - PROCEDURE -Chdir*(path: Name): LONGINT + PROCEDURE -Chdir*(path: Name): INTEGER "chdir(path)"; - PROCEDURE -Ioctl*(fd, request, arg: LONGINT): LONGINT + PROCEDURE -Ioctl*(fd: INTEGER; request, arg: LONGINT): INTEGER "ioctl(fd, request, arg)"; - PROCEDURE -Kill*(pid, sig: LONGINT): LONGINT + PROCEDURE -Kill*(pid, sig: INTEGER): INTEGER "kill(pid, sig)"; - PROCEDURE -Sigsetmask*(mask: LONGINT): LONGINT + PROCEDURE -Sigsetmask*(mask: INTEGER): INTEGER "sigsetmask(mask)"; PROCEDURE -Sleep*(ms : INTEGER): INTEGER - "(INTEGER)sleep(ms)"; + "(INTEGER)sleep(ms)"; PROCEDURE -Nanosleep*(VAR req : Timeval; VAR rem : Timeval): INTEGER "(INTEGER)nanosleep(req, rem)"; @@ -474,31 +476,31 @@ from man gettimeofday PROCEDURE -Gethostbyname*(name: Name): HostEntry "(Unix_HostEntry)gethostbyname(name)"; - PROCEDURE -Gethostname*(VAR name: Name): LONGINT + PROCEDURE -Gethostname*(VAR name: Name): INTEGER "gethostname(name, name__len)"; - PROCEDURE -Socket*(af, type, protocol: LONGINT): LONGINT + PROCEDURE -Socket*(af, type, protocol: INTEGER): INTEGER "socket(af, type, protocol)"; - PROCEDURE -Connect*(socket: LONGINT; name: Sockaddr; namelen: LONGINT): LONGINT + PROCEDURE -Connect*(socket: INTEGER; name: Sockaddr; namelen: INTEGER): INTEGER "connect(socket, &(name), namelen)"; - PROCEDURE -Getsockname*(socket: LONGINT; VAR name: Sockaddr; VAR namelen: LONGINT): LONGINT + PROCEDURE -Getsockname*(socket: INTEGER; VAR name: Sockaddr; VAR namelen: INTEGER): INTEGER "getsockname(socket, name, namelen)"; - PROCEDURE -Bind*(socket: LONGINT; name: Sockaddr; namelen: LONGINT): LONGINT + PROCEDURE -Bind*(socket: INTEGER; name: Sockaddr; namelen: INTEGER): INTEGER "bind(socket, &(name), namelen)"; - PROCEDURE -Listen*(socket, backlog: LONGINT): LONGINT + PROCEDURE -Listen*(socket, backlog: INTEGER): INTEGER "listen(socket, backlog)"; - PROCEDURE -Accept*(socket: LONGINT; VAR addr: Sockaddr; VAR addrlen: LONGINT): LONGINT + PROCEDURE -Accept*(socket: INTEGER; VAR addr: Sockaddr; VAR addrlen: INTEGER): LONGINT "accept(socket, addr, addrlen)"; - PROCEDURE -Recv*(socket, bufadr, buflen, flags: LONGINT): LONGINT + PROCEDURE -Recv*(socket: INTEGER; bufadr, buflen: LONGINT; flags: INTEGER): LONGINT "recv(socket, bufadr, buflen, flags)"; - PROCEDURE -Send*(socket, bufadr, buflen, flags: LONGINT): LONGINT + PROCEDURE -Send*(socket: INTEGER; bufadr, buflen: LONGINT; flags: INTEGER): LONGINT "send(socket, bufadr, buflen, flags)"; PROCEDURE -sys(str: ARRAY OF CHAR): INTEGER (* need this to call external tools like gcc or gas; noch *) diff --git a/src/lib/system/linux/gcc/armv6j_hardfp/Files.Mod b/src/lib/system/linux/gcc/armv6j_hardfp/Files.Mod new file mode 100644 index 00000000..6307407d --- /dev/null +++ b/src/lib/system/linux/gcc/armv6j_hardfp/Files.Mod @@ -0,0 +1,663 @@ +MODULE Files; (* J. Templ 1.12. 89/12.4.95 Oberon files mapped onto Unix files *) +(* modified version of Files, which opens only the file provided and does not scan any path in any environment variable, also ReadLine procedure added; -- noch *) + IMPORT SYSTEM, Unix, Kernel, Args, Console; + + (* 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 = 4096; + fileTabSize = 64; + noDesc = -1; + notDone = -1; + + (* file states *) + open = 0; create = 1; close = 2; + + + TYPE + FileName = ARRAY 101 OF CHAR; + File* = POINTER TO Handle; + Buffer = POINTER TO BufDesc; + + Handle = RECORD + workName, registerName: FileName; + tempFile: BOOLEAN; + dev, ino, 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 ; + + Time = POINTER TO TimeDesc; + TimeDesc = RECORD + sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: LONGINT; +(* sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: INTEGER;*) + END ; + + VAR + fileTab: ARRAY fileTabSize OF LONGINT (*=File*); + tempno: INTEGER; + +(* for localtime *) + PROCEDURE -includetime() + '#include "time.h"'; + + PROCEDURE -localtime(VAR clock: LONGINT): Time + "(Files_Time) localtime(clock)"; + + PROCEDURE -getcwd(VAR cwd: Unix.Name) + "getcwd(cwd, cwd__len)"; + + PROCEDURE -IdxTrap "__HALT(-1)"; + + PROCEDURE^ Finalize(o: SYSTEM.PTR); + + PROCEDURE Err(s: ARRAY OF CHAR; f: File; errno: LONGINT); + BEGIN + Console.Ln; Console.String("-- "); Console.String(s); Console.String(": "); + IF f # NIL THEN + IF f.registerName # "" THEN Console.String(f.registerName) ELSE Console.String(f.workName) END + END ; + IF errno # 0 THEN Console.String(" errno = "); Console.Int(errno, 1) END ; + Console.Ln; + HALT(99) + END Err; + + 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 Kernel.CWD[i] # 0X DO name[i] := Kernel.CWD[i]; INC(i) END; + IF Kernel.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 Create(f: File); + VAR stat: Unix.Status; done: BOOLEAN; + errno: LONGINT; err: ARRAY 32 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 ; + errno := Unix.Unlink(f.workName); (*unlink first to avoid stale NFS handles and to avoid reuse of inodes*) + f.fd := Unix.Open(f.workName, Unix.rdwr + Unix.creat + Unix.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(TRUE); + f.fd := Unix.Open(f.workName, Unix.rdwr + Unix.creat + Unix.trunc, {2, 4,5, 7,8}); + done := f.fd >= 0 + END ; + IF done THEN + IF f.fd >= fileTabSize THEN errno := Unix.Close(f.fd); Err("too many files open", f, 0) + ELSE fileTab[f.fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.state := open; f.pos := 0; errno := Unix.Fstat(f.fd, 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" + ELSIF (errno = Unix.ENFILE) OR (errno = Unix.EMFILE) THEN err := "too many files open" + ELSE err := "file not created" + END ; + Err(err, f, errno) + END + END + END Create; + + PROCEDURE Flush(buf: Buffer); + VAR res: LONGINT; f: File; 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 Err("error in writing file", f, Unix.errno()) END ; + f.pos := buf.org + buf.size; + buf.chg := FALSE; + res := Unix.Fstat(f.fd, stat); + f.mtime := stat.mtime + END + END Flush; + + PROCEDURE Close* (f: File); + VAR i, res: LONGINT; + 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 < 0 THEN Err("error in writing file", f, Unix.errno()) 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; + BEGIN + NEW(f); f.workName := ""; COPY(name, 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); (* supports ~, ~user and blanks inside path *) + VAR i: INTEGER; ch: CHAR; home: ARRAY 256 OF CHAR; + BEGIN + i := 0; ch := Kernel.OBERON[pos]; + WHILE (ch = " ") OR (ch = ":") DO INC(pos); ch := Kernel.OBERON[pos] END ; + IF ch = "~" THEN + INC(pos); ch := Kernel.OBERON[pos]; + home := ""; Args.GetEnv("HOME", home); + WHILE home[i] # 0X DO dir[i] := home[i]; INC(i) END ; + IF (ch # "/") & (ch # 0X) & (ch # ":") & (ch # " ") THEN + WHILE (i > 0) & (dir[i-1] # "/") DO DEC(i) END + END + END ; + WHILE (ch # 0X) & (ch # ":") DO dir[i] := ch; INC(i); INC(pos); ch := Kernel.OBERON[pos] END ; + WHILE (i > 0) & (dir[i-1] = " ") DO DEC(i) 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, 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: INTEGER; done: BOOLEAN; + dir, path: ARRAY 256 OF CHAR; + stat: Unix.Status; + BEGIN + IF name # "" THEN + IF HasDir(name) THEN dir := ""; COPY(name, path) + ELSE + pos := 0; + COPY(name, path); (* -- noch *) + (*ScanPath(pos, dir);*) (*MakeFileName(dir, name, path);*) (*ScanPath(pos, dir)*) + END ; + LOOP + fd := Unix.Open(path, Unix.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(TRUE); + fd := Unix.Open(path, Unix.rdwr, {}); + done := fd >= 0; errno := Unix.errno(); + IF ~done & ((errno = Unix.ENFILE) OR (errno = Unix.EMFILE)) THEN Err("too many files open", f, errno) END + END ; + IF ~done & ((errno = Unix.EACCES) OR (errno = Unix.EROFS) OR (errno = Unix.EAGAIN)) THEN + (* errno EAGAIN observed on Solaris 2.4 *) + fd := Unix.Open(path, Unix.rdonly, {}); done := fd >= 0; errno := Unix.errno() + END ; +IF (~done) & (errno # Unix.ENOENT) THEN + Console.String("warning Files.Old "); Console.String(name); + Console.String(" errno = "); Console.Int(errno, 0); Console.Ln; +END ; + IF done THEN + res := Unix.Fstat(fd, 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", f, 0) + ELSE NEW(f); fileTab[fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.fd := fd; f.state := open; f.len := stat.size; f.pos := 0; f.swapper := -1; (*all f.buf[i] = NIL*) + COPY(name, 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)*) + RETURN NIL + 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, 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, 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: LONGINT; buf: Buffer; + 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.ReadBlk(f.fd, buf.data); + IF n < 0 THEN Err("read from file not done", f, Unix.errno()) 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 ReadByte* (VAR r : Rider; VAR x : ARRAY OF SYSTEM.BYTE); + BEGIN + ReadBytes(r, x, 1); + END ReadByte; + + 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 WriteByte* (VAR r : Rider; x : SYSTEM.BYTE); (* added for compatibility with PO 2013, -- noch *) + BEGIN + Write(r, x); + END WriteByte; + + 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 Delete* (name: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Unlink(name)); + res := SHORT(Unix.errno()) + 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; + BEGIN + r := Unix.Stat(old, ostat); + IF r >= 0 THEN + r := Unix.Stat(new, 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(old, new); + IF r < 0 THEN res := SHORT(Unix.errno()); + IF res = Unix.EXDEV THEN (* cross device link, move the file *) + fdold := Unix.Open(old, Unix.rdonly, {}); + IF fdold < 0 THEN res := 2; RETURN END ; + fdnew := Unix.Open(new, Unix.rdwr + Unix.creat + Unix.trunc, {2, 4,5, 7,8}); + IF fdnew < 0 THEN r := Unix.Close(fdold); res := 3; RETURN 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); + Err("cannot move file", NIL, errno) + END ; + n := Unix.Read(fdold, SYSTEM.ADR(buf), bufsize) + END ; + errno := Unix.errno(); + r := Unix.Close(fdold); r := Unix.Close(fdnew); + IF n = 0 THEN r := Unix.Unlink(old); res := 0 + ELSE Err("cannot move file", NIL, errno) + END ; + ELSE RETURN (* res is Unix.Rename return code *) + END + END ; + res := 0 + ELSE res := 2 (* old file not found *) + END + END Rename; + + PROCEDURE Register* (f: File); + VAR idx, errno: INTEGER; f1: File; file: ARRAY 104 OF CHAR; + BEGIN + IF (f.state = create) & (f.registerName # "") THEN f.state := close (* shortcut renaming *) END ; + 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 ChangeDirectory*(path: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Chdir(path)); + getcwd(Kernel.CWD) + END ChangeDirectory; + + PROCEDURE FlipBytes(VAR src, dest: ARRAY OF SYSTEM.BYTE); + VAR i, j: LONGINT; + BEGIN + IF ~Kernel.littleEndian THEN i := LEN(src); j := 0; + WHILE i > 0 DO DEC(i); dest[j] := src[i]; INC(j) END + ELSE SYSTEM.MOVE(SYSTEM.ADR(src), SYSTEM.ADR(dest), LEN(src)) + 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, b, 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, b, 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, b, 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, b, 4); FlipBytes(b, x) + END ReadReal; + + PROCEDURE ReadLReal* (VAR R: Rider; VAR x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN ReadBytes(R, b, 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; + + (* need to read line; -- noch *) + PROCEDURE ReadLine* (VAR R: Rider; VAR x: ARRAY OF CHAR); + VAR i: INTEGER; ch: CHAR; b : BOOLEAN; + BEGIN i := 0; + b := FALSE; + REPEAT + Read(R, ch); + IF ((ch = 0X) OR (ch = 0AX) OR (ch = 0DX)) THEN + b := TRUE + ELSE + x[i] := ch; + INC(i); + END; + UNTIL b + END ReadLine; + + 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, b, 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, b, 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, b, 4); + END WriteSet; + + PROCEDURE WriteReal* (VAR R: Rider; x: REAL); + VAR b: ARRAY 4 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 4) + END WriteReal; + + PROCEDURE WriteLReal* (VAR R: Rider; x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 8) + END WriteLReal; + + PROCEDURE WriteString* (VAR R: Rider; x: ARRAY [1] 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 GetName*(f: File; VAR name: ARRAY OF CHAR); + BEGIN + COPY (f.workName, name); + END GetName; + + PROCEDURE Finalize(o: SYSTEM.PTR); + VAR f: File; res: LONGINT; + BEGIN + f := SYSTEM.VAL(File, o); + IF f.fd >= 0 THEN + fileTab[f.fd] := 0; res := Unix.Close(f.fd); f.fd := -1; DEC(Kernel.nofiles); + IF f.tempFile THEN res := Unix.Unlink(f.workName) END + END + END Finalize; + + PROCEDURE Init; + VAR i: LONGINT; + BEGIN + i := 0; WHILE i < fileTabSize DO fileTab[i] := 0; INC(i) END ; + tempno := -1; Kernel.nofiles := 0 + END Init; + +BEGIN Init +END Files. diff --git a/src/lib/system/linux/gcc/armv6j_hardfp/Files0.Mod b/src/lib/system/linux/gcc/armv6j_hardfp/Files0.Mod new file mode 100644 index 00000000..4f021ede --- /dev/null +++ b/src/lib/system/linux/gcc/armv6j_hardfp/Files0.Mod @@ -0,0 +1,635 @@ +MODULE Files0; (* J. Templ 1.12. 89/12.4.95 Oberon files mapped onto Unix files *) + +(* this module is not for use by developers and inteded to bootstrap voc *) +(* for general use import Files module *) + + IMPORT SYSTEM, Unix, Kernel := Kernel0, Args, Console; + + (* 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 = 4096; + fileTabSize = 64; + noDesc = -1; + notDone = -1; + + (* file states *) + open = 0; create = 1; close = 2; + + + TYPE + FileName = ARRAY 101 OF CHAR; + File* = POINTER TO Handle; + Buffer = POINTER TO BufDesc; + + Handle = RECORD + workName, registerName: FileName; + tempFile: BOOLEAN; + dev, ino, 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 ; + + Time = POINTER TO TimeDesc; + TimeDesc = RECORD + sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: LONGINT; +(* sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: INTEGER;*) + END ; + + VAR + fileTab: ARRAY fileTabSize OF LONGINT (*=File*); + tempno: INTEGER; + +(* for localtime *) + PROCEDURE -includetime() + '#include "time.h"'; + + PROCEDURE -localtime(VAR clock: LONGINT): Time + "(Files0_Time) localtime(clock)"; + + PROCEDURE -getcwd(VAR cwd: Unix.Name) + "getcwd(cwd, cwd__len)"; + + PROCEDURE -IdxTrap "__HALT(-1)"; + + PROCEDURE^ Finalize(o: SYSTEM.PTR); + + PROCEDURE Err(s: ARRAY OF CHAR; f: File; errno: LONGINT); + BEGIN + Console.Ln; Console.String("-- "); Console.String(s); Console.String(": "); + IF f # NIL THEN + IF f.registerName # "" THEN Console.String(f.registerName) ELSE Console.String(f.workName) END + END ; + IF errno # 0 THEN Console.String(" errno = "); Console.Int(errno, 1) END ; + Console.Ln; + HALT(99) + END Err; + + 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 Kernel.CWD[i] # 0X DO name[i] := Kernel.CWD[i]; INC(i) END; + IF Kernel.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 Create(f: File); + VAR stat: Unix.Status; done: BOOLEAN; + errno: LONGINT; err: ARRAY 32 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 ; + errno := Unix.Unlink(f.workName); (*unlink first to avoid stale NFS handles and to avoid reuse of inodes*) + f.fd := Unix.Open(f.workName, Unix.rdwr + Unix.creat + Unix.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(TRUE); + f.fd := Unix.Open(f.workName, Unix.rdwr + Unix.creat + Unix.trunc, {2, 4,5, 7,8}); + done := f.fd >= 0 + END ; + IF done THEN + IF f.fd >= fileTabSize THEN errno := Unix.Close(f.fd); Err("too many files open", f, 0) + ELSE fileTab[f.fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.state := open; f.pos := 0; errno := Unix.Fstat(f.fd, 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" + ELSIF (errno = Unix.ENFILE) OR (errno = Unix.EMFILE) THEN err := "too many files open" + ELSE err := "file not created" + END ; + Err(err, f, errno) + END + END + END Create; + + PROCEDURE Flush(buf: Buffer); + VAR res: LONGINT; f: File; 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 Err("error in writing file", f, Unix.errno()) END ; + f.pos := buf.org + buf.size; + buf.chg := FALSE; + res := Unix.Fstat(f.fd, stat); + f.mtime := stat.mtime + END + END Flush; + + PROCEDURE Close* (f: File); + VAR i, res: LONGINT; + 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 < 0 THEN Err("error in writing file", f, Unix.errno()) 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; + BEGIN + NEW(f); f.workName := ""; COPY(name, 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); (* supports ~, ~user and blanks inside path *) + VAR i: INTEGER; ch: CHAR; home: ARRAY 256 OF CHAR; + BEGIN + i := 0; ch := Kernel.OBERON[pos]; + WHILE (ch = " ") OR (ch = ":") DO INC(pos); ch := Kernel.OBERON[pos] END ; + IF ch = "~" THEN + INC(pos); ch := Kernel.OBERON[pos]; + home := ""; Args.GetEnv("HOME", home); + WHILE home[i] # 0X DO dir[i] := home[i]; INC(i) END ; + IF (ch # "/") & (ch # 0X) & (ch # ":") & (ch # " ") THEN + WHILE (i > 0) & (dir[i-1] # "/") DO DEC(i) END + END + END ; + WHILE (ch # 0X) & (ch # ":") DO dir[i] := ch; INC(i); INC(pos); ch := Kernel.OBERON[pos] END ; + WHILE (i > 0) & (dir[i-1] = " ") DO DEC(i) 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, 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: INTEGER; done: BOOLEAN; + dir, path: ARRAY 256 OF CHAR; + stat: Unix.Status; + BEGIN + IF name # "" THEN + IF 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(path, Unix.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(TRUE); + fd := Unix.Open(path, Unix.rdwr, {}); + done := fd >= 0; errno := Unix.errno(); + IF ~done & ((errno = Unix.ENFILE) OR (errno = Unix.EMFILE)) THEN Err("too many files open", f, errno) END + END ; + IF ~done & ((errno = Unix.EACCES) OR (errno = Unix.EROFS) OR (errno = Unix.EAGAIN)) THEN + (* errno EAGAIN observed on Solaris 2.4 *) + fd := Unix.Open(path, Unix.rdonly, {}); done := fd >= 0; errno := Unix.errno() + END ; +IF (~done) & (errno # Unix.ENOENT) THEN + Console.String("warning Files0.Old "); Console.String(name); + Console.String(" errno = "); Console.Int(errno, 0); Console.Ln; +END ; + IF done THEN + res := Unix.Fstat(fd, 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", f, 0) + ELSE NEW(f); fileTab[fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.fd := fd; f.state := open; f.len := stat.size; f.pos := 0; f.swapper := -1; (*all f.buf[i] = NIL*) + COPY(name, 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, 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, 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: LONGINT; buf: Buffer; + 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.ReadBlk(f.fd, buf.data); + IF n < 0 THEN Err("read from file not done", f, Unix.errno()) 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 ReadByte* (VAR r : Rider; VAR x : ARRAY OF SYSTEM.BYTE); + BEGIN + ReadBytes(r, x, 1); + END ReadByte; + + 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 Delete* (name: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Unlink(name)); + res := SHORT(Unix.errno()) + 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; + BEGIN + r := Unix.Stat(old, ostat); + IF r >= 0 THEN + r := Unix.Stat(new, 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(old, new); + IF r < 0 THEN res := SHORT(Unix.errno()); + IF res = Unix.EXDEV THEN (* cross device link, move the file *) + fdold := Unix.Open(old, Unix.rdonly, {}); + IF fdold < 0 THEN res := 2; RETURN END ; + fdnew := Unix.Open(new, Unix.rdwr + Unix.creat + Unix.trunc, {2, 4,5, 7,8}); + IF fdnew < 0 THEN r := Unix.Close(fdold); res := 3; RETURN 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); + Err("cannot move file", NIL, errno) + END ; + n := Unix.Read(fdold, SYSTEM.ADR(buf), bufsize) + END ; + errno := Unix.errno(); + r := Unix.Close(fdold); r := Unix.Close(fdnew); + IF n = 0 THEN r := Unix.Unlink(old); res := 0 + ELSE Err("cannot move file", NIL, errno) + END ; + ELSE RETURN (* res is Unix.Rename return code *) + END + END ; + res := 0 + ELSE res := 2 (* old file not found *) + END + END Rename; + + PROCEDURE Register* (f: File); + VAR idx, errno: INTEGER; f1: File; file: ARRAY 104 OF CHAR; + BEGIN + IF (f.state = create) & (f.registerName # "") THEN f.state := close (* shortcut renaming *) END ; + 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 ChangeDirectory*(path: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Chdir(path)); + getcwd(Kernel.CWD) + END ChangeDirectory; + + PROCEDURE FlipBytes(VAR src, dest: ARRAY OF SYSTEM.BYTE); + VAR i, j: LONGINT; + BEGIN + IF ~Kernel.littleEndian THEN i := LEN(src); j := 0; + WHILE i > 0 DO DEC(i); dest[j] := src[i]; INC(j) END + ELSE SYSTEM.MOVE(SYSTEM.ADR(src), SYSTEM.ADR(dest), LEN(src)) + 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, b, 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, b, 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, b, 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, b, 4); FlipBytes(b, x) + END ReadReal; + + PROCEDURE ReadLReal* (VAR R: Rider; VAR x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN ReadBytes(R, b, 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, b, 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, b, 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, b, 4); + END WriteSet; + + PROCEDURE WriteReal* (VAR R: Rider; x: REAL); + VAR b: ARRAY 4 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 4) + END WriteReal; + + PROCEDURE WriteLReal* (VAR R: Rider; x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 8) + END WriteLReal; + + PROCEDURE WriteString* (VAR R: Rider; x: ARRAY [1] 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 Finalize(o: SYSTEM.PTR); + VAR f: File; res: LONGINT; + BEGIN + f := SYSTEM.VAL(File, o); + IF f.fd >= 0 THEN + fileTab[f.fd] := 0; res := Unix.Close(f.fd); f.fd := -1; DEC(Kernel.nofiles); + IF f.tempFile THEN res := Unix.Unlink(f.workName) END + END + END Finalize; + + PROCEDURE Init; + VAR i: LONGINT; + BEGIN + i := 0; WHILE i < fileTabSize DO fileTab[i] := 0; INC(i) END ; + tempno := -1; Kernel.nofiles := 0 + END Init; + +BEGIN Init +END Files0. diff --git a/src/lib/system/linux/gcc/powerpc/Files.Mod b/src/lib/system/linux/gcc/powerpc/Files.Mod new file mode 100644 index 00000000..6307407d --- /dev/null +++ b/src/lib/system/linux/gcc/powerpc/Files.Mod @@ -0,0 +1,663 @@ +MODULE Files; (* J. Templ 1.12. 89/12.4.95 Oberon files mapped onto Unix files *) +(* modified version of Files, which opens only the file provided and does not scan any path in any environment variable, also ReadLine procedure added; -- noch *) + IMPORT SYSTEM, Unix, Kernel, Args, Console; + + (* 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 = 4096; + fileTabSize = 64; + noDesc = -1; + notDone = -1; + + (* file states *) + open = 0; create = 1; close = 2; + + + TYPE + FileName = ARRAY 101 OF CHAR; + File* = POINTER TO Handle; + Buffer = POINTER TO BufDesc; + + Handle = RECORD + workName, registerName: FileName; + tempFile: BOOLEAN; + dev, ino, 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 ; + + Time = POINTER TO TimeDesc; + TimeDesc = RECORD + sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: LONGINT; +(* sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: INTEGER;*) + END ; + + VAR + fileTab: ARRAY fileTabSize OF LONGINT (*=File*); + tempno: INTEGER; + +(* for localtime *) + PROCEDURE -includetime() + '#include "time.h"'; + + PROCEDURE -localtime(VAR clock: LONGINT): Time + "(Files_Time) localtime(clock)"; + + PROCEDURE -getcwd(VAR cwd: Unix.Name) + "getcwd(cwd, cwd__len)"; + + PROCEDURE -IdxTrap "__HALT(-1)"; + + PROCEDURE^ Finalize(o: SYSTEM.PTR); + + PROCEDURE Err(s: ARRAY OF CHAR; f: File; errno: LONGINT); + BEGIN + Console.Ln; Console.String("-- "); Console.String(s); Console.String(": "); + IF f # NIL THEN + IF f.registerName # "" THEN Console.String(f.registerName) ELSE Console.String(f.workName) END + END ; + IF errno # 0 THEN Console.String(" errno = "); Console.Int(errno, 1) END ; + Console.Ln; + HALT(99) + END Err; + + 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 Kernel.CWD[i] # 0X DO name[i] := Kernel.CWD[i]; INC(i) END; + IF Kernel.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 Create(f: File); + VAR stat: Unix.Status; done: BOOLEAN; + errno: LONGINT; err: ARRAY 32 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 ; + errno := Unix.Unlink(f.workName); (*unlink first to avoid stale NFS handles and to avoid reuse of inodes*) + f.fd := Unix.Open(f.workName, Unix.rdwr + Unix.creat + Unix.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(TRUE); + f.fd := Unix.Open(f.workName, Unix.rdwr + Unix.creat + Unix.trunc, {2, 4,5, 7,8}); + done := f.fd >= 0 + END ; + IF done THEN + IF f.fd >= fileTabSize THEN errno := Unix.Close(f.fd); Err("too many files open", f, 0) + ELSE fileTab[f.fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.state := open; f.pos := 0; errno := Unix.Fstat(f.fd, 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" + ELSIF (errno = Unix.ENFILE) OR (errno = Unix.EMFILE) THEN err := "too many files open" + ELSE err := "file not created" + END ; + Err(err, f, errno) + END + END + END Create; + + PROCEDURE Flush(buf: Buffer); + VAR res: LONGINT; f: File; 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 Err("error in writing file", f, Unix.errno()) END ; + f.pos := buf.org + buf.size; + buf.chg := FALSE; + res := Unix.Fstat(f.fd, stat); + f.mtime := stat.mtime + END + END Flush; + + PROCEDURE Close* (f: File); + VAR i, res: LONGINT; + 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 < 0 THEN Err("error in writing file", f, Unix.errno()) 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; + BEGIN + NEW(f); f.workName := ""; COPY(name, 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); (* supports ~, ~user and blanks inside path *) + VAR i: INTEGER; ch: CHAR; home: ARRAY 256 OF CHAR; + BEGIN + i := 0; ch := Kernel.OBERON[pos]; + WHILE (ch = " ") OR (ch = ":") DO INC(pos); ch := Kernel.OBERON[pos] END ; + IF ch = "~" THEN + INC(pos); ch := Kernel.OBERON[pos]; + home := ""; Args.GetEnv("HOME", home); + WHILE home[i] # 0X DO dir[i] := home[i]; INC(i) END ; + IF (ch # "/") & (ch # 0X) & (ch # ":") & (ch # " ") THEN + WHILE (i > 0) & (dir[i-1] # "/") DO DEC(i) END + END + END ; + WHILE (ch # 0X) & (ch # ":") DO dir[i] := ch; INC(i); INC(pos); ch := Kernel.OBERON[pos] END ; + WHILE (i > 0) & (dir[i-1] = " ") DO DEC(i) 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, 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: INTEGER; done: BOOLEAN; + dir, path: ARRAY 256 OF CHAR; + stat: Unix.Status; + BEGIN + IF name # "" THEN + IF HasDir(name) THEN dir := ""; COPY(name, path) + ELSE + pos := 0; + COPY(name, path); (* -- noch *) + (*ScanPath(pos, dir);*) (*MakeFileName(dir, name, path);*) (*ScanPath(pos, dir)*) + END ; + LOOP + fd := Unix.Open(path, Unix.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(TRUE); + fd := Unix.Open(path, Unix.rdwr, {}); + done := fd >= 0; errno := Unix.errno(); + IF ~done & ((errno = Unix.ENFILE) OR (errno = Unix.EMFILE)) THEN Err("too many files open", f, errno) END + END ; + IF ~done & ((errno = Unix.EACCES) OR (errno = Unix.EROFS) OR (errno = Unix.EAGAIN)) THEN + (* errno EAGAIN observed on Solaris 2.4 *) + fd := Unix.Open(path, Unix.rdonly, {}); done := fd >= 0; errno := Unix.errno() + END ; +IF (~done) & (errno # Unix.ENOENT) THEN + Console.String("warning Files.Old "); Console.String(name); + Console.String(" errno = "); Console.Int(errno, 0); Console.Ln; +END ; + IF done THEN + res := Unix.Fstat(fd, 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", f, 0) + ELSE NEW(f); fileTab[fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.fd := fd; f.state := open; f.len := stat.size; f.pos := 0; f.swapper := -1; (*all f.buf[i] = NIL*) + COPY(name, 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)*) + RETURN NIL + 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, 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, 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: LONGINT; buf: Buffer; + 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.ReadBlk(f.fd, buf.data); + IF n < 0 THEN Err("read from file not done", f, Unix.errno()) 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 ReadByte* (VAR r : Rider; VAR x : ARRAY OF SYSTEM.BYTE); + BEGIN + ReadBytes(r, x, 1); + END ReadByte; + + 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 WriteByte* (VAR r : Rider; x : SYSTEM.BYTE); (* added for compatibility with PO 2013, -- noch *) + BEGIN + Write(r, x); + END WriteByte; + + 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 Delete* (name: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Unlink(name)); + res := SHORT(Unix.errno()) + 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; + BEGIN + r := Unix.Stat(old, ostat); + IF r >= 0 THEN + r := Unix.Stat(new, 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(old, new); + IF r < 0 THEN res := SHORT(Unix.errno()); + IF res = Unix.EXDEV THEN (* cross device link, move the file *) + fdold := Unix.Open(old, Unix.rdonly, {}); + IF fdold < 0 THEN res := 2; RETURN END ; + fdnew := Unix.Open(new, Unix.rdwr + Unix.creat + Unix.trunc, {2, 4,5, 7,8}); + IF fdnew < 0 THEN r := Unix.Close(fdold); res := 3; RETURN 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); + Err("cannot move file", NIL, errno) + END ; + n := Unix.Read(fdold, SYSTEM.ADR(buf), bufsize) + END ; + errno := Unix.errno(); + r := Unix.Close(fdold); r := Unix.Close(fdnew); + IF n = 0 THEN r := Unix.Unlink(old); res := 0 + ELSE Err("cannot move file", NIL, errno) + END ; + ELSE RETURN (* res is Unix.Rename return code *) + END + END ; + res := 0 + ELSE res := 2 (* old file not found *) + END + END Rename; + + PROCEDURE Register* (f: File); + VAR idx, errno: INTEGER; f1: File; file: ARRAY 104 OF CHAR; + BEGIN + IF (f.state = create) & (f.registerName # "") THEN f.state := close (* shortcut renaming *) END ; + 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 ChangeDirectory*(path: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Chdir(path)); + getcwd(Kernel.CWD) + END ChangeDirectory; + + PROCEDURE FlipBytes(VAR src, dest: ARRAY OF SYSTEM.BYTE); + VAR i, j: LONGINT; + BEGIN + IF ~Kernel.littleEndian THEN i := LEN(src); j := 0; + WHILE i > 0 DO DEC(i); dest[j] := src[i]; INC(j) END + ELSE SYSTEM.MOVE(SYSTEM.ADR(src), SYSTEM.ADR(dest), LEN(src)) + 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, b, 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, b, 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, b, 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, b, 4); FlipBytes(b, x) + END ReadReal; + + PROCEDURE ReadLReal* (VAR R: Rider; VAR x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN ReadBytes(R, b, 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; + + (* need to read line; -- noch *) + PROCEDURE ReadLine* (VAR R: Rider; VAR x: ARRAY OF CHAR); + VAR i: INTEGER; ch: CHAR; b : BOOLEAN; + BEGIN i := 0; + b := FALSE; + REPEAT + Read(R, ch); + IF ((ch = 0X) OR (ch = 0AX) OR (ch = 0DX)) THEN + b := TRUE + ELSE + x[i] := ch; + INC(i); + END; + UNTIL b + END ReadLine; + + 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, b, 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, b, 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, b, 4); + END WriteSet; + + PROCEDURE WriteReal* (VAR R: Rider; x: REAL); + VAR b: ARRAY 4 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 4) + END WriteReal; + + PROCEDURE WriteLReal* (VAR R: Rider; x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 8) + END WriteLReal; + + PROCEDURE WriteString* (VAR R: Rider; x: ARRAY [1] 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 GetName*(f: File; VAR name: ARRAY OF CHAR); + BEGIN + COPY (f.workName, name); + END GetName; + + PROCEDURE Finalize(o: SYSTEM.PTR); + VAR f: File; res: LONGINT; + BEGIN + f := SYSTEM.VAL(File, o); + IF f.fd >= 0 THEN + fileTab[f.fd] := 0; res := Unix.Close(f.fd); f.fd := -1; DEC(Kernel.nofiles); + IF f.tempFile THEN res := Unix.Unlink(f.workName) END + END + END Finalize; + + PROCEDURE Init; + VAR i: LONGINT; + BEGIN + i := 0; WHILE i < fileTabSize DO fileTab[i] := 0; INC(i) END ; + tempno := -1; Kernel.nofiles := 0 + END Init; + +BEGIN Init +END Files. diff --git a/src/lib/system/linux/gcc/powerpc/Files0.Mod b/src/lib/system/linux/gcc/powerpc/Files0.Mod new file mode 100644 index 00000000..4f021ede --- /dev/null +++ b/src/lib/system/linux/gcc/powerpc/Files0.Mod @@ -0,0 +1,635 @@ +MODULE Files0; (* J. Templ 1.12. 89/12.4.95 Oberon files mapped onto Unix files *) + +(* this module is not for use by developers and inteded to bootstrap voc *) +(* for general use import Files module *) + + IMPORT SYSTEM, Unix, Kernel := Kernel0, Args, Console; + + (* 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 = 4096; + fileTabSize = 64; + noDesc = -1; + notDone = -1; + + (* file states *) + open = 0; create = 1; close = 2; + + + TYPE + FileName = ARRAY 101 OF CHAR; + File* = POINTER TO Handle; + Buffer = POINTER TO BufDesc; + + Handle = RECORD + workName, registerName: FileName; + tempFile: BOOLEAN; + dev, ino, 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 ; + + Time = POINTER TO TimeDesc; + TimeDesc = RECORD + sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: LONGINT; +(* sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: INTEGER;*) + END ; + + VAR + fileTab: ARRAY fileTabSize OF LONGINT (*=File*); + tempno: INTEGER; + +(* for localtime *) + PROCEDURE -includetime() + '#include "time.h"'; + + PROCEDURE -localtime(VAR clock: LONGINT): Time + "(Files0_Time) localtime(clock)"; + + PROCEDURE -getcwd(VAR cwd: Unix.Name) + "getcwd(cwd, cwd__len)"; + + PROCEDURE -IdxTrap "__HALT(-1)"; + + PROCEDURE^ Finalize(o: SYSTEM.PTR); + + PROCEDURE Err(s: ARRAY OF CHAR; f: File; errno: LONGINT); + BEGIN + Console.Ln; Console.String("-- "); Console.String(s); Console.String(": "); + IF f # NIL THEN + IF f.registerName # "" THEN Console.String(f.registerName) ELSE Console.String(f.workName) END + END ; + IF errno # 0 THEN Console.String(" errno = "); Console.Int(errno, 1) END ; + Console.Ln; + HALT(99) + END Err; + + 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 Kernel.CWD[i] # 0X DO name[i] := Kernel.CWD[i]; INC(i) END; + IF Kernel.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 Create(f: File); + VAR stat: Unix.Status; done: BOOLEAN; + errno: LONGINT; err: ARRAY 32 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 ; + errno := Unix.Unlink(f.workName); (*unlink first to avoid stale NFS handles and to avoid reuse of inodes*) + f.fd := Unix.Open(f.workName, Unix.rdwr + Unix.creat + Unix.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(TRUE); + f.fd := Unix.Open(f.workName, Unix.rdwr + Unix.creat + Unix.trunc, {2, 4,5, 7,8}); + done := f.fd >= 0 + END ; + IF done THEN + IF f.fd >= fileTabSize THEN errno := Unix.Close(f.fd); Err("too many files open", f, 0) + ELSE fileTab[f.fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.state := open; f.pos := 0; errno := Unix.Fstat(f.fd, 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" + ELSIF (errno = Unix.ENFILE) OR (errno = Unix.EMFILE) THEN err := "too many files open" + ELSE err := "file not created" + END ; + Err(err, f, errno) + END + END + END Create; + + PROCEDURE Flush(buf: Buffer); + VAR res: LONGINT; f: File; 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 Err("error in writing file", f, Unix.errno()) END ; + f.pos := buf.org + buf.size; + buf.chg := FALSE; + res := Unix.Fstat(f.fd, stat); + f.mtime := stat.mtime + END + END Flush; + + PROCEDURE Close* (f: File); + VAR i, res: LONGINT; + 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 < 0 THEN Err("error in writing file", f, Unix.errno()) 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; + BEGIN + NEW(f); f.workName := ""; COPY(name, 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); (* supports ~, ~user and blanks inside path *) + VAR i: INTEGER; ch: CHAR; home: ARRAY 256 OF CHAR; + BEGIN + i := 0; ch := Kernel.OBERON[pos]; + WHILE (ch = " ") OR (ch = ":") DO INC(pos); ch := Kernel.OBERON[pos] END ; + IF ch = "~" THEN + INC(pos); ch := Kernel.OBERON[pos]; + home := ""; Args.GetEnv("HOME", home); + WHILE home[i] # 0X DO dir[i] := home[i]; INC(i) END ; + IF (ch # "/") & (ch # 0X) & (ch # ":") & (ch # " ") THEN + WHILE (i > 0) & (dir[i-1] # "/") DO DEC(i) END + END + END ; + WHILE (ch # 0X) & (ch # ":") DO dir[i] := ch; INC(i); INC(pos); ch := Kernel.OBERON[pos] END ; + WHILE (i > 0) & (dir[i-1] = " ") DO DEC(i) 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, 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: INTEGER; done: BOOLEAN; + dir, path: ARRAY 256 OF CHAR; + stat: Unix.Status; + BEGIN + IF name # "" THEN + IF 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(path, Unix.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(TRUE); + fd := Unix.Open(path, Unix.rdwr, {}); + done := fd >= 0; errno := Unix.errno(); + IF ~done & ((errno = Unix.ENFILE) OR (errno = Unix.EMFILE)) THEN Err("too many files open", f, errno) END + END ; + IF ~done & ((errno = Unix.EACCES) OR (errno = Unix.EROFS) OR (errno = Unix.EAGAIN)) THEN + (* errno EAGAIN observed on Solaris 2.4 *) + fd := Unix.Open(path, Unix.rdonly, {}); done := fd >= 0; errno := Unix.errno() + END ; +IF (~done) & (errno # Unix.ENOENT) THEN + Console.String("warning Files0.Old "); Console.String(name); + Console.String(" errno = "); Console.Int(errno, 0); Console.Ln; +END ; + IF done THEN + res := Unix.Fstat(fd, 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", f, 0) + ELSE NEW(f); fileTab[fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.fd := fd; f.state := open; f.len := stat.size; f.pos := 0; f.swapper := -1; (*all f.buf[i] = NIL*) + COPY(name, 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, 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, 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: LONGINT; buf: Buffer; + 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.ReadBlk(f.fd, buf.data); + IF n < 0 THEN Err("read from file not done", f, Unix.errno()) 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 ReadByte* (VAR r : Rider; VAR x : ARRAY OF SYSTEM.BYTE); + BEGIN + ReadBytes(r, x, 1); + END ReadByte; + + 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 Delete* (name: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Unlink(name)); + res := SHORT(Unix.errno()) + 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; + BEGIN + r := Unix.Stat(old, ostat); + IF r >= 0 THEN + r := Unix.Stat(new, 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(old, new); + IF r < 0 THEN res := SHORT(Unix.errno()); + IF res = Unix.EXDEV THEN (* cross device link, move the file *) + fdold := Unix.Open(old, Unix.rdonly, {}); + IF fdold < 0 THEN res := 2; RETURN END ; + fdnew := Unix.Open(new, Unix.rdwr + Unix.creat + Unix.trunc, {2, 4,5, 7,8}); + IF fdnew < 0 THEN r := Unix.Close(fdold); res := 3; RETURN 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); + Err("cannot move file", NIL, errno) + END ; + n := Unix.Read(fdold, SYSTEM.ADR(buf), bufsize) + END ; + errno := Unix.errno(); + r := Unix.Close(fdold); r := Unix.Close(fdnew); + IF n = 0 THEN r := Unix.Unlink(old); res := 0 + ELSE Err("cannot move file", NIL, errno) + END ; + ELSE RETURN (* res is Unix.Rename return code *) + END + END ; + res := 0 + ELSE res := 2 (* old file not found *) + END + END Rename; + + PROCEDURE Register* (f: File); + VAR idx, errno: INTEGER; f1: File; file: ARRAY 104 OF CHAR; + BEGIN + IF (f.state = create) & (f.registerName # "") THEN f.state := close (* shortcut renaming *) END ; + 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 ChangeDirectory*(path: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Chdir(path)); + getcwd(Kernel.CWD) + END ChangeDirectory; + + PROCEDURE FlipBytes(VAR src, dest: ARRAY OF SYSTEM.BYTE); + VAR i, j: LONGINT; + BEGIN + IF ~Kernel.littleEndian THEN i := LEN(src); j := 0; + WHILE i > 0 DO DEC(i); dest[j] := src[i]; INC(j) END + ELSE SYSTEM.MOVE(SYSTEM.ADR(src), SYSTEM.ADR(dest), LEN(src)) + 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, b, 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, b, 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, b, 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, b, 4); FlipBytes(b, x) + END ReadReal; + + PROCEDURE ReadLReal* (VAR R: Rider; VAR x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN ReadBytes(R, b, 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, b, 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, b, 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, b, 4); + END WriteSet; + + PROCEDURE WriteReal* (VAR R: Rider; x: REAL); + VAR b: ARRAY 4 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 4) + END WriteReal; + + PROCEDURE WriteLReal* (VAR R: Rider; x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 8) + END WriteLReal; + + PROCEDURE WriteString* (VAR R: Rider; x: ARRAY [1] 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 Finalize(o: SYSTEM.PTR); + VAR f: File; res: LONGINT; + BEGIN + f := SYSTEM.VAL(File, o); + IF f.fd >= 0 THEN + fileTab[f.fd] := 0; res := Unix.Close(f.fd); f.fd := -1; DEC(Kernel.nofiles); + IF f.tempFile THEN res := Unix.Unlink(f.workName) END + END + END Finalize; + + PROCEDURE Init; + VAR i: LONGINT; + BEGIN + i := 0; WHILE i < fileTabSize DO fileTab[i] := 0; INC(i) END ; + tempno := -1; Kernel.nofiles := 0 + END Init; + +BEGIN Init +END Files0. diff --git a/src/lib/system/linux/gcc/x86/Files.Mod b/src/lib/system/linux/gcc/x86/Files.Mod new file mode 100644 index 00000000..6307407d --- /dev/null +++ b/src/lib/system/linux/gcc/x86/Files.Mod @@ -0,0 +1,663 @@ +MODULE Files; (* J. Templ 1.12. 89/12.4.95 Oberon files mapped onto Unix files *) +(* modified version of Files, which opens only the file provided and does not scan any path in any environment variable, also ReadLine procedure added; -- noch *) + IMPORT SYSTEM, Unix, Kernel, Args, Console; + + (* 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 = 4096; + fileTabSize = 64; + noDesc = -1; + notDone = -1; + + (* file states *) + open = 0; create = 1; close = 2; + + + TYPE + FileName = ARRAY 101 OF CHAR; + File* = POINTER TO Handle; + Buffer = POINTER TO BufDesc; + + Handle = RECORD + workName, registerName: FileName; + tempFile: BOOLEAN; + dev, ino, 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 ; + + Time = POINTER TO TimeDesc; + TimeDesc = RECORD + sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: LONGINT; +(* sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: INTEGER;*) + END ; + + VAR + fileTab: ARRAY fileTabSize OF LONGINT (*=File*); + tempno: INTEGER; + +(* for localtime *) + PROCEDURE -includetime() + '#include "time.h"'; + + PROCEDURE -localtime(VAR clock: LONGINT): Time + "(Files_Time) localtime(clock)"; + + PROCEDURE -getcwd(VAR cwd: Unix.Name) + "getcwd(cwd, cwd__len)"; + + PROCEDURE -IdxTrap "__HALT(-1)"; + + PROCEDURE^ Finalize(o: SYSTEM.PTR); + + PROCEDURE Err(s: ARRAY OF CHAR; f: File; errno: LONGINT); + BEGIN + Console.Ln; Console.String("-- "); Console.String(s); Console.String(": "); + IF f # NIL THEN + IF f.registerName # "" THEN Console.String(f.registerName) ELSE Console.String(f.workName) END + END ; + IF errno # 0 THEN Console.String(" errno = "); Console.Int(errno, 1) END ; + Console.Ln; + HALT(99) + END Err; + + 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 Kernel.CWD[i] # 0X DO name[i] := Kernel.CWD[i]; INC(i) END; + IF Kernel.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 Create(f: File); + VAR stat: Unix.Status; done: BOOLEAN; + errno: LONGINT; err: ARRAY 32 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 ; + errno := Unix.Unlink(f.workName); (*unlink first to avoid stale NFS handles and to avoid reuse of inodes*) + f.fd := Unix.Open(f.workName, Unix.rdwr + Unix.creat + Unix.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(TRUE); + f.fd := Unix.Open(f.workName, Unix.rdwr + Unix.creat + Unix.trunc, {2, 4,5, 7,8}); + done := f.fd >= 0 + END ; + IF done THEN + IF f.fd >= fileTabSize THEN errno := Unix.Close(f.fd); Err("too many files open", f, 0) + ELSE fileTab[f.fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.state := open; f.pos := 0; errno := Unix.Fstat(f.fd, 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" + ELSIF (errno = Unix.ENFILE) OR (errno = Unix.EMFILE) THEN err := "too many files open" + ELSE err := "file not created" + END ; + Err(err, f, errno) + END + END + END Create; + + PROCEDURE Flush(buf: Buffer); + VAR res: LONGINT; f: File; 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 Err("error in writing file", f, Unix.errno()) END ; + f.pos := buf.org + buf.size; + buf.chg := FALSE; + res := Unix.Fstat(f.fd, stat); + f.mtime := stat.mtime + END + END Flush; + + PROCEDURE Close* (f: File); + VAR i, res: LONGINT; + 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 < 0 THEN Err("error in writing file", f, Unix.errno()) 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; + BEGIN + NEW(f); f.workName := ""; COPY(name, 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); (* supports ~, ~user and blanks inside path *) + VAR i: INTEGER; ch: CHAR; home: ARRAY 256 OF CHAR; + BEGIN + i := 0; ch := Kernel.OBERON[pos]; + WHILE (ch = " ") OR (ch = ":") DO INC(pos); ch := Kernel.OBERON[pos] END ; + IF ch = "~" THEN + INC(pos); ch := Kernel.OBERON[pos]; + home := ""; Args.GetEnv("HOME", home); + WHILE home[i] # 0X DO dir[i] := home[i]; INC(i) END ; + IF (ch # "/") & (ch # 0X) & (ch # ":") & (ch # " ") THEN + WHILE (i > 0) & (dir[i-1] # "/") DO DEC(i) END + END + END ; + WHILE (ch # 0X) & (ch # ":") DO dir[i] := ch; INC(i); INC(pos); ch := Kernel.OBERON[pos] END ; + WHILE (i > 0) & (dir[i-1] = " ") DO DEC(i) 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, 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: INTEGER; done: BOOLEAN; + dir, path: ARRAY 256 OF CHAR; + stat: Unix.Status; + BEGIN + IF name # "" THEN + IF HasDir(name) THEN dir := ""; COPY(name, path) + ELSE + pos := 0; + COPY(name, path); (* -- noch *) + (*ScanPath(pos, dir);*) (*MakeFileName(dir, name, path);*) (*ScanPath(pos, dir)*) + END ; + LOOP + fd := Unix.Open(path, Unix.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(TRUE); + fd := Unix.Open(path, Unix.rdwr, {}); + done := fd >= 0; errno := Unix.errno(); + IF ~done & ((errno = Unix.ENFILE) OR (errno = Unix.EMFILE)) THEN Err("too many files open", f, errno) END + END ; + IF ~done & ((errno = Unix.EACCES) OR (errno = Unix.EROFS) OR (errno = Unix.EAGAIN)) THEN + (* errno EAGAIN observed on Solaris 2.4 *) + fd := Unix.Open(path, Unix.rdonly, {}); done := fd >= 0; errno := Unix.errno() + END ; +IF (~done) & (errno # Unix.ENOENT) THEN + Console.String("warning Files.Old "); Console.String(name); + Console.String(" errno = "); Console.Int(errno, 0); Console.Ln; +END ; + IF done THEN + res := Unix.Fstat(fd, 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", f, 0) + ELSE NEW(f); fileTab[fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.fd := fd; f.state := open; f.len := stat.size; f.pos := 0; f.swapper := -1; (*all f.buf[i] = NIL*) + COPY(name, 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)*) + RETURN NIL + 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, 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, 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: LONGINT; buf: Buffer; + 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.ReadBlk(f.fd, buf.data); + IF n < 0 THEN Err("read from file not done", f, Unix.errno()) 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 ReadByte* (VAR r : Rider; VAR x : ARRAY OF SYSTEM.BYTE); + BEGIN + ReadBytes(r, x, 1); + END ReadByte; + + 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 WriteByte* (VAR r : Rider; x : SYSTEM.BYTE); (* added for compatibility with PO 2013, -- noch *) + BEGIN + Write(r, x); + END WriteByte; + + 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 Delete* (name: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Unlink(name)); + res := SHORT(Unix.errno()) + 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; + BEGIN + r := Unix.Stat(old, ostat); + IF r >= 0 THEN + r := Unix.Stat(new, 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(old, new); + IF r < 0 THEN res := SHORT(Unix.errno()); + IF res = Unix.EXDEV THEN (* cross device link, move the file *) + fdold := Unix.Open(old, Unix.rdonly, {}); + IF fdold < 0 THEN res := 2; RETURN END ; + fdnew := Unix.Open(new, Unix.rdwr + Unix.creat + Unix.trunc, {2, 4,5, 7,8}); + IF fdnew < 0 THEN r := Unix.Close(fdold); res := 3; RETURN 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); + Err("cannot move file", NIL, errno) + END ; + n := Unix.Read(fdold, SYSTEM.ADR(buf), bufsize) + END ; + errno := Unix.errno(); + r := Unix.Close(fdold); r := Unix.Close(fdnew); + IF n = 0 THEN r := Unix.Unlink(old); res := 0 + ELSE Err("cannot move file", NIL, errno) + END ; + ELSE RETURN (* res is Unix.Rename return code *) + END + END ; + res := 0 + ELSE res := 2 (* old file not found *) + END + END Rename; + + PROCEDURE Register* (f: File); + VAR idx, errno: INTEGER; f1: File; file: ARRAY 104 OF CHAR; + BEGIN + IF (f.state = create) & (f.registerName # "") THEN f.state := close (* shortcut renaming *) END ; + 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 ChangeDirectory*(path: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Chdir(path)); + getcwd(Kernel.CWD) + END ChangeDirectory; + + PROCEDURE FlipBytes(VAR src, dest: ARRAY OF SYSTEM.BYTE); + VAR i, j: LONGINT; + BEGIN + IF ~Kernel.littleEndian THEN i := LEN(src); j := 0; + WHILE i > 0 DO DEC(i); dest[j] := src[i]; INC(j) END + ELSE SYSTEM.MOVE(SYSTEM.ADR(src), SYSTEM.ADR(dest), LEN(src)) + 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, b, 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, b, 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, b, 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, b, 4); FlipBytes(b, x) + END ReadReal; + + PROCEDURE ReadLReal* (VAR R: Rider; VAR x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN ReadBytes(R, b, 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; + + (* need to read line; -- noch *) + PROCEDURE ReadLine* (VAR R: Rider; VAR x: ARRAY OF CHAR); + VAR i: INTEGER; ch: CHAR; b : BOOLEAN; + BEGIN i := 0; + b := FALSE; + REPEAT + Read(R, ch); + IF ((ch = 0X) OR (ch = 0AX) OR (ch = 0DX)) THEN + b := TRUE + ELSE + x[i] := ch; + INC(i); + END; + UNTIL b + END ReadLine; + + 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, b, 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, b, 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, b, 4); + END WriteSet; + + PROCEDURE WriteReal* (VAR R: Rider; x: REAL); + VAR b: ARRAY 4 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 4) + END WriteReal; + + PROCEDURE WriteLReal* (VAR R: Rider; x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 8) + END WriteLReal; + + PROCEDURE WriteString* (VAR R: Rider; x: ARRAY [1] 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 GetName*(f: File; VAR name: ARRAY OF CHAR); + BEGIN + COPY (f.workName, name); + END GetName; + + PROCEDURE Finalize(o: SYSTEM.PTR); + VAR f: File; res: LONGINT; + BEGIN + f := SYSTEM.VAL(File, o); + IF f.fd >= 0 THEN + fileTab[f.fd] := 0; res := Unix.Close(f.fd); f.fd := -1; DEC(Kernel.nofiles); + IF f.tempFile THEN res := Unix.Unlink(f.workName) END + END + END Finalize; + + PROCEDURE Init; + VAR i: LONGINT; + BEGIN + i := 0; WHILE i < fileTabSize DO fileTab[i] := 0; INC(i) END ; + tempno := -1; Kernel.nofiles := 0 + END Init; + +BEGIN Init +END Files. diff --git a/src/lib/system/linux/gcc/x86/Files0.Mod b/src/lib/system/linux/gcc/x86/Files0.Mod new file mode 100644 index 00000000..4f021ede --- /dev/null +++ b/src/lib/system/linux/gcc/x86/Files0.Mod @@ -0,0 +1,635 @@ +MODULE Files0; (* J. Templ 1.12. 89/12.4.95 Oberon files mapped onto Unix files *) + +(* this module is not for use by developers and inteded to bootstrap voc *) +(* for general use import Files module *) + + IMPORT SYSTEM, Unix, Kernel := Kernel0, Args, Console; + + (* 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 = 4096; + fileTabSize = 64; + noDesc = -1; + notDone = -1; + + (* file states *) + open = 0; create = 1; close = 2; + + + TYPE + FileName = ARRAY 101 OF CHAR; + File* = POINTER TO Handle; + Buffer = POINTER TO BufDesc; + + Handle = RECORD + workName, registerName: FileName; + tempFile: BOOLEAN; + dev, ino, 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 ; + + Time = POINTER TO TimeDesc; + TimeDesc = RECORD + sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: LONGINT; +(* sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: INTEGER;*) + END ; + + VAR + fileTab: ARRAY fileTabSize OF LONGINT (*=File*); + tempno: INTEGER; + +(* for localtime *) + PROCEDURE -includetime() + '#include "time.h"'; + + PROCEDURE -localtime(VAR clock: LONGINT): Time + "(Files0_Time) localtime(clock)"; + + PROCEDURE -getcwd(VAR cwd: Unix.Name) + "getcwd(cwd, cwd__len)"; + + PROCEDURE -IdxTrap "__HALT(-1)"; + + PROCEDURE^ Finalize(o: SYSTEM.PTR); + + PROCEDURE Err(s: ARRAY OF CHAR; f: File; errno: LONGINT); + BEGIN + Console.Ln; Console.String("-- "); Console.String(s); Console.String(": "); + IF f # NIL THEN + IF f.registerName # "" THEN Console.String(f.registerName) ELSE Console.String(f.workName) END + END ; + IF errno # 0 THEN Console.String(" errno = "); Console.Int(errno, 1) END ; + Console.Ln; + HALT(99) + END Err; + + 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 Kernel.CWD[i] # 0X DO name[i] := Kernel.CWD[i]; INC(i) END; + IF Kernel.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 Create(f: File); + VAR stat: Unix.Status; done: BOOLEAN; + errno: LONGINT; err: ARRAY 32 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 ; + errno := Unix.Unlink(f.workName); (*unlink first to avoid stale NFS handles and to avoid reuse of inodes*) + f.fd := Unix.Open(f.workName, Unix.rdwr + Unix.creat + Unix.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(TRUE); + f.fd := Unix.Open(f.workName, Unix.rdwr + Unix.creat + Unix.trunc, {2, 4,5, 7,8}); + done := f.fd >= 0 + END ; + IF done THEN + IF f.fd >= fileTabSize THEN errno := Unix.Close(f.fd); Err("too many files open", f, 0) + ELSE fileTab[f.fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.state := open; f.pos := 0; errno := Unix.Fstat(f.fd, 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" + ELSIF (errno = Unix.ENFILE) OR (errno = Unix.EMFILE) THEN err := "too many files open" + ELSE err := "file not created" + END ; + Err(err, f, errno) + END + END + END Create; + + PROCEDURE Flush(buf: Buffer); + VAR res: LONGINT; f: File; 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 Err("error in writing file", f, Unix.errno()) END ; + f.pos := buf.org + buf.size; + buf.chg := FALSE; + res := Unix.Fstat(f.fd, stat); + f.mtime := stat.mtime + END + END Flush; + + PROCEDURE Close* (f: File); + VAR i, res: LONGINT; + 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 < 0 THEN Err("error in writing file", f, Unix.errno()) 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; + BEGIN + NEW(f); f.workName := ""; COPY(name, 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); (* supports ~, ~user and blanks inside path *) + VAR i: INTEGER; ch: CHAR; home: ARRAY 256 OF CHAR; + BEGIN + i := 0; ch := Kernel.OBERON[pos]; + WHILE (ch = " ") OR (ch = ":") DO INC(pos); ch := Kernel.OBERON[pos] END ; + IF ch = "~" THEN + INC(pos); ch := Kernel.OBERON[pos]; + home := ""; Args.GetEnv("HOME", home); + WHILE home[i] # 0X DO dir[i] := home[i]; INC(i) END ; + IF (ch # "/") & (ch # 0X) & (ch # ":") & (ch # " ") THEN + WHILE (i > 0) & (dir[i-1] # "/") DO DEC(i) END + END + END ; + WHILE (ch # 0X) & (ch # ":") DO dir[i] := ch; INC(i); INC(pos); ch := Kernel.OBERON[pos] END ; + WHILE (i > 0) & (dir[i-1] = " ") DO DEC(i) 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, 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: INTEGER; done: BOOLEAN; + dir, path: ARRAY 256 OF CHAR; + stat: Unix.Status; + BEGIN + IF name # "" THEN + IF 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(path, Unix.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(TRUE); + fd := Unix.Open(path, Unix.rdwr, {}); + done := fd >= 0; errno := Unix.errno(); + IF ~done & ((errno = Unix.ENFILE) OR (errno = Unix.EMFILE)) THEN Err("too many files open", f, errno) END + END ; + IF ~done & ((errno = Unix.EACCES) OR (errno = Unix.EROFS) OR (errno = Unix.EAGAIN)) THEN + (* errno EAGAIN observed on Solaris 2.4 *) + fd := Unix.Open(path, Unix.rdonly, {}); done := fd >= 0; errno := Unix.errno() + END ; +IF (~done) & (errno # Unix.ENOENT) THEN + Console.String("warning Files0.Old "); Console.String(name); + Console.String(" errno = "); Console.Int(errno, 0); Console.Ln; +END ; + IF done THEN + res := Unix.Fstat(fd, 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", f, 0) + ELSE NEW(f); fileTab[fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.fd := fd; f.state := open; f.len := stat.size; f.pos := 0; f.swapper := -1; (*all f.buf[i] = NIL*) + COPY(name, 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, 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, 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: LONGINT; buf: Buffer; + 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.ReadBlk(f.fd, buf.data); + IF n < 0 THEN Err("read from file not done", f, Unix.errno()) 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 ReadByte* (VAR r : Rider; VAR x : ARRAY OF SYSTEM.BYTE); + BEGIN + ReadBytes(r, x, 1); + END ReadByte; + + 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 Delete* (name: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Unlink(name)); + res := SHORT(Unix.errno()) + 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; + BEGIN + r := Unix.Stat(old, ostat); + IF r >= 0 THEN + r := Unix.Stat(new, 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(old, new); + IF r < 0 THEN res := SHORT(Unix.errno()); + IF res = Unix.EXDEV THEN (* cross device link, move the file *) + fdold := Unix.Open(old, Unix.rdonly, {}); + IF fdold < 0 THEN res := 2; RETURN END ; + fdnew := Unix.Open(new, Unix.rdwr + Unix.creat + Unix.trunc, {2, 4,5, 7,8}); + IF fdnew < 0 THEN r := Unix.Close(fdold); res := 3; RETURN 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); + Err("cannot move file", NIL, errno) + END ; + n := Unix.Read(fdold, SYSTEM.ADR(buf), bufsize) + END ; + errno := Unix.errno(); + r := Unix.Close(fdold); r := Unix.Close(fdnew); + IF n = 0 THEN r := Unix.Unlink(old); res := 0 + ELSE Err("cannot move file", NIL, errno) + END ; + ELSE RETURN (* res is Unix.Rename return code *) + END + END ; + res := 0 + ELSE res := 2 (* old file not found *) + END + END Rename; + + PROCEDURE Register* (f: File); + VAR idx, errno: INTEGER; f1: File; file: ARRAY 104 OF CHAR; + BEGIN + IF (f.state = create) & (f.registerName # "") THEN f.state := close (* shortcut renaming *) END ; + 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 ChangeDirectory*(path: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Chdir(path)); + getcwd(Kernel.CWD) + END ChangeDirectory; + + PROCEDURE FlipBytes(VAR src, dest: ARRAY OF SYSTEM.BYTE); + VAR i, j: LONGINT; + BEGIN + IF ~Kernel.littleEndian THEN i := LEN(src); j := 0; + WHILE i > 0 DO DEC(i); dest[j] := src[i]; INC(j) END + ELSE SYSTEM.MOVE(SYSTEM.ADR(src), SYSTEM.ADR(dest), LEN(src)) + 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, b, 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, b, 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, b, 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, b, 4); FlipBytes(b, x) + END ReadReal; + + PROCEDURE ReadLReal* (VAR R: Rider; VAR x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN ReadBytes(R, b, 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, b, 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, b, 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, b, 4); + END WriteSet; + + PROCEDURE WriteReal* (VAR R: Rider; x: REAL); + VAR b: ARRAY 4 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 4) + END WriteReal; + + PROCEDURE WriteLReal* (VAR R: Rider; x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 8) + END WriteLReal; + + PROCEDURE WriteString* (VAR R: Rider; x: ARRAY [1] 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 Finalize(o: SYSTEM.PTR); + VAR f: File; res: LONGINT; + BEGIN + f := SYSTEM.VAL(File, o); + IF f.fd >= 0 THEN + fileTab[f.fd] := 0; res := Unix.Close(f.fd); f.fd := -1; DEC(Kernel.nofiles); + IF f.tempFile THEN res := Unix.Unlink(f.workName) END + END + END Finalize; + + PROCEDURE Init; + VAR i: LONGINT; + BEGIN + i := 0; WHILE i < fileTabSize DO fileTab[i] := 0; INC(i) END ; + tempno := -1; Kernel.nofiles := 0 + END Init; + +BEGIN Init +END Files0. diff --git a/src/lib/system/linux/gcc/x86_64/Files.Mod b/src/lib/system/linux/gcc/x86_64/Files.Mod new file mode 100644 index 00000000..c8f42ca5 --- /dev/null +++ b/src/lib/system/linux/gcc/x86_64/Files.Mod @@ -0,0 +1,664 @@ +MODULE Files; (* J. Templ 1.12. 89/12.4.95 Oberon files mapped onto Unix files *) +(* modified version of Files, which opens only the file provided and does not scan any path in any environment variable, also ReadLine procedure added; -- noch *) + IMPORT SYSTEM, Unix, Kernel, Args, Console; + + (* 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 = 4096; + fileTabSize = 64; + noDesc = -1; + notDone = -1; + + (* file states *) + open = 0; create = 1; close = 2; + + + TYPE + FileName = ARRAY 101 OF CHAR; + File* = POINTER TO Handle; + Buffer = POINTER TO BufDesc; + + Handle = RECORD + workName, registerName: FileName; + tempFile: BOOLEAN; + dev, ino, mtime: LONGINT; + fd-: INTEGER; + 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 ; + + Time = POINTER TO TimeDesc; + TimeDesc = RECORD + sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: LONGINT; +(* sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: INTEGER;*) + END ; + + VAR + fileTab: ARRAY fileTabSize OF LONGINT (*=File*); + tempno: INTEGER; + +(* for localtime *) + PROCEDURE -includetime() + '#include "time.h"'; + + PROCEDURE -localtime(VAR clock: LONGINT): Time + "(Files_Time) localtime(clock)"; + + PROCEDURE -getcwd(VAR cwd: Unix.Name) + "getcwd(cwd, cwd__len)"; + + PROCEDURE -IdxTrap "__HALT(-1)"; + + PROCEDURE^ Finalize(o: SYSTEM.PTR); + + PROCEDURE Err(s: ARRAY OF CHAR; f: File; errno: LONGINT); + BEGIN + Console.Ln; Console.String("-- "); Console.String(s); Console.String(": "); + IF f # NIL THEN + IF f.registerName # "" THEN Console.String(f.registerName) ELSE Console.String(f.workName) END + END ; + IF errno # 0 THEN Console.String(" errno = "); Console.Int(errno, 1) END ; + Console.Ln; + HALT(99) + END Err; + + 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 Kernel.CWD[i] # 0X DO name[i] := Kernel.CWD[i]; INC(i) END; + IF Kernel.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 Create(f: File); + VAR stat: Unix.Status; done: BOOLEAN; + errno: LONGINT; err: ARRAY 32 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 ; + errno := Unix.Unlink(f.workName); (*unlink first to avoid stale NFS handles and to avoid reuse of inodes*) + f.fd := Unix.Open(f.workName, SHORT(SYSTEM.VAL(LONGINT, Unix.rdwr + Unix.creat + Unix.trunc)), SHORT(SYSTEM.VAL(LONGINT, {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(TRUE); + f.fd := Unix.Open(f.workName, SHORT(SYSTEM.VAL(LONGINT, Unix.rdwr + Unix.creat + Unix.trunc)), SHORT(SYSTEM.VAL(LONGINT, {2, 4,5, 7,8}))); + done := f.fd >= 0 + END ; + IF done THEN + IF f.fd >= fileTabSize THEN errno := Unix.Close(f.fd); Err("too many files open", f, 0) + ELSE fileTab[f.fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.state := open; f.pos := 0; errno := Unix.Fstat(f.fd, 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" + ELSIF (errno = Unix.ENFILE) OR (errno = Unix.EMFILE) THEN err := "too many files open" + ELSE err := "file not created" + END ; + Err(err, f, errno) + END + END + END Create; + + PROCEDURE Flush(buf: Buffer); + VAR res: LONGINT; f: File; 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 Err("error in writing file", f, Unix.errno()) END ; + f.pos := buf.org + buf.size; + buf.chg := FALSE; + res := Unix.Fstat(f.fd, stat); + f.mtime := stat.mtime + END + END Flush; + + PROCEDURE Close* (f: File); + VAR i, res: LONGINT; + 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 < 0 THEN Err("error in writing file", f, Unix.errno()) 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; + BEGIN + NEW(f); f.workName := ""; COPY(name, 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); (* supports ~, ~user and blanks inside path *) + VAR i: INTEGER; ch: CHAR; home: ARRAY 256 OF CHAR; + BEGIN + i := 0; ch := Kernel.OBERON[pos]; + WHILE (ch = " ") OR (ch = ":") DO INC(pos); ch := Kernel.OBERON[pos] END ; + IF ch = "~" THEN + INC(pos); ch := Kernel.OBERON[pos]; + home := ""; Args.GetEnv("HOME", home); + WHILE home[i] # 0X DO dir[i] := home[i]; INC(i) END ; + IF (ch # "/") & (ch # 0X) & (ch # ":") & (ch # " ") THEN + WHILE (i > 0) & (dir[i-1] # "/") DO DEC(i) END + END + END ; + WHILE (ch # 0X) & (ch # ":") DO dir[i] := ch; INC(i); INC(pos); ch := Kernel.OBERON[pos] END ; + WHILE (i > 0) & (dir[i-1] = " ") DO DEC(i) 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, 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: INTEGER; res, errno: LONGINT; pos: INTEGER; done: BOOLEAN; + dir, path: ARRAY 256 OF CHAR; + stat: Unix.Status; + BEGIN + IF name # "" THEN + IF HasDir(name) THEN dir := ""; COPY(name, path) + ELSE + pos := 0; + COPY(name, path); (* -- noch *) + (*ScanPath(pos, dir);*) (*MakeFileName(dir, name, path);*) (*ScanPath(pos, dir)*) + END ; + LOOP + fd := Unix.Open(path, SHORT(SYSTEM.VAL(LONGINT, Unix.rdwr)), SHORT(SYSTEM.VAL(LONGINT, {}))); 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(TRUE); + fd := Unix.Open(path, SHORT(SYSTEM.VAL(LONGINT, Unix.rdwr)), SHORT(SYSTEM.VAL(LONGINT, {}))); + done := fd >= 0; errno := Unix.errno(); + IF ~done & ((errno = Unix.ENFILE) OR (errno = Unix.EMFILE)) THEN Err("too many files open", f, errno) END + END ; + IF ~done & ((errno = Unix.EACCES) OR (errno = Unix.EROFS) OR (errno = Unix.EAGAIN)) THEN + (* errno EAGAIN observed on Solaris 2.4 *) + fd := Unix.Open(path, SHORT(SYSTEM.VAL(LONGINT, Unix.rdonly)), SHORT(SYSTEM.VAL(LONGINT, {}))); done := fd >= 0; errno := Unix.errno() + END ; +IF (~done) & (errno # Unix.ENOENT) THEN + Console.String("warning Files.Old "); Console.String(name); + Console.String(" errno = "); Console.Int(errno, 0); Console.Ln; +END ; + IF done THEN + res := Unix.Fstat(fd, 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", f, 0) + ELSE NEW(f); fileTab[fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.fd := fd; f.state := open; f.len := stat.size; f.pos := 0; f.swapper := -1; (*all f.buf[i] = NIL*) + COPY(name, 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)*) + RETURN NIL + 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, 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, 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: LONGINT; buf: Buffer; + 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.ReadBlk(f.fd, buf.data); + IF n < 0 THEN Err("read from file not done", f, Unix.errno()) 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 ReadByte* (VAR r : Rider; VAR x : ARRAY OF SYSTEM.BYTE); + BEGIN + ReadBytes(r, x, 1); + END ReadByte; + + 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 WriteByte* (VAR r : Rider; x : SYSTEM.BYTE); (* added for compatibility with PO 2013, -- noch *) + BEGIN + Write(r, x); + END WriteByte; + + 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 Delete* (name: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Unlink(name)); + res := SHORT(Unix.errno()) + END Delete; + + PROCEDURE Rename* (old, new: ARRAY OF CHAR; VAR res: INTEGER); + VAR fdold, fdnew: INTEGER; n, errno, r: LONGINT; + ostat, nstat: Unix.Status; + buf: ARRAY 4096 OF CHAR; + BEGIN + r := Unix.Stat(old, ostat); + IF r >= 0 THEN + r := Unix.Stat(new, 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(old, new); + IF r < 0 THEN res := SHORT(Unix.errno()); + IF res = Unix.EXDEV THEN (* cross device link, move the file *) + fdold := Unix.Open(old, SHORT(SYSTEM.VAL(LONGINT, Unix.rdonly)), SHORT(SYSTEM.VAL(LONGINT, {}))); + IF fdold < 0 THEN res := 2; RETURN END ; + fdnew := Unix.Open(new, SHORT(SYSTEM.VAL(LONGINT, Unix.rdwr + Unix.creat + Unix.trunc)), SHORT(SYSTEM.VAL(LONGINT, {2, 4,5, 7,8}))); + IF fdnew < 0 THEN r := Unix.Close(fdold); res := 3; RETURN 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); + Err("cannot move file", NIL, errno) + END ; + n := Unix.Read(fdold, SYSTEM.ADR(buf), bufsize) + END ; + errno := Unix.errno(); + r := Unix.Close(fdold); r := Unix.Close(fdnew); + IF n = 0 THEN r := Unix.Unlink(old); res := 0 + ELSE Err("cannot move file", NIL, errno) + END ; + ELSE RETURN (* res is Unix.Rename return code *) + END + END ; + res := 0 + ELSE res := 2 (* old file not found *) + END + END Rename; + + PROCEDURE Register* (f: File); + VAR idx, errno: INTEGER; f1: File; file: ARRAY 104 OF CHAR; + BEGIN + IF (f.state = create) & (f.registerName # "") THEN f.state := close (* shortcut renaming *) END ; + 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 ChangeDirectory*(path: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Chdir(path)); + getcwd(Kernel.CWD) + END ChangeDirectory; + + PROCEDURE FlipBytes(VAR src, dest: ARRAY OF SYSTEM.BYTE); + VAR i, j: LONGINT; + BEGIN + IF ~Kernel.littleEndian THEN i := LEN(src); j := 0; + WHILE i > 0 DO DEC(i); dest[j] := src[i]; INC(j) END + ELSE SYSTEM.MOVE(SYSTEM.ADR(src), SYSTEM.ADR(dest), LEN(src)) + 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, b, 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, b, 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, b, 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, b, 4); FlipBytes(b, x) + END ReadReal; + + PROCEDURE ReadLReal* (VAR R: Rider; VAR x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN ReadBytes(R, b, 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; + + (* need to read line; -- noch *) + PROCEDURE ReadLine* (VAR R: Rider; VAR x: ARRAY OF CHAR); + VAR i: INTEGER; ch: CHAR; b : BOOLEAN; + BEGIN i := 0; + b := FALSE; + REPEAT + Read(R, ch); + IF ((ch = 0X) OR (ch = 0AX) OR (ch = 0DX)) THEN + b := TRUE + ELSE + x[i] := ch; + INC(i); + END; + UNTIL b + END ReadLine; + + 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, b, 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, b, 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, b, 4); + END WriteSet; + + PROCEDURE WriteReal* (VAR R: Rider; x: REAL); + VAR b: ARRAY 4 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 4) + END WriteReal; + + PROCEDURE WriteLReal* (VAR R: Rider; x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 8) + END WriteLReal; + + PROCEDURE WriteString* (VAR R: Rider; x: ARRAY [1] 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 GetName*(f: File; VAR name: ARRAY OF CHAR); + BEGIN + COPY (f.workName, name); + END GetName; + + PROCEDURE Finalize(o: SYSTEM.PTR); + VAR f: File; res: LONGINT; + BEGIN + f := SYSTEM.VAL(File, o); + IF f.fd >= 0 THEN + fileTab[f.fd] := 0; res := Unix.Close(f.fd); f.fd := -1; DEC(Kernel.nofiles); + IF f.tempFile THEN res := Unix.Unlink(f.workName) END + END + END Finalize; + + PROCEDURE Init; + VAR i: LONGINT; + BEGIN + i := 0; WHILE i < fileTabSize DO fileTab[i] := 0; INC(i) END ; + tempno := -1; Kernel.nofiles := 0 + END Init; + +BEGIN Init +END Files. diff --git a/src/lib/system/linux/gcc/x86_64/Files0.Mod b/src/lib/system/linux/gcc/x86_64/Files0.Mod new file mode 100644 index 00000000..1d9cd953 --- /dev/null +++ b/src/lib/system/linux/gcc/x86_64/Files0.Mod @@ -0,0 +1,636 @@ +MODULE Files0; (* J. Templ 1.12. 89/12.4.95 Oberon files mapped onto Unix files *) + +(* this module is not for use by developers and inteded to bootstrap voc *) +(* for general use import Files module *) + + IMPORT SYSTEM, Unix, Kernel := Kernel0, Args, Console; + + (* 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 = 4096; + fileTabSize = 64; + noDesc = -1; + notDone = -1; + + (* file states *) + open = 0; create = 1; close = 2; + + + TYPE + FileName = ARRAY 101 OF CHAR; + File* = POINTER TO Handle; + Buffer = POINTER TO BufDesc; + + Handle = RECORD + workName, registerName: FileName; + tempFile: BOOLEAN; + dev, ino, mtime: LONGINT; + fd-: INTEGER; + 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 ; + + Time = POINTER TO TimeDesc; + TimeDesc = RECORD + sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: LONGINT; +(* sec*, min*, hour*, mday*, mon*, year*, wday*, isdst*, zone*, gmtoff*: INTEGER;*) + END ; + + VAR + fileTab: ARRAY fileTabSize OF LONGINT (*=File*); + tempno: INTEGER; + +(* for localtime *) + PROCEDURE -includetime() + '#include "time.h"'; + + PROCEDURE -localtime(VAR clock: LONGINT): Time + "(Files0_Time) localtime(clock)"; + + PROCEDURE -getcwd(VAR cwd: Unix.Name) + "getcwd(cwd, cwd__len)"; + + PROCEDURE -IdxTrap "__HALT(-1)"; + + PROCEDURE^ Finalize(o: SYSTEM.PTR); + + PROCEDURE Err(s: ARRAY OF CHAR; f: File; errno: LONGINT); + BEGIN + Console.Ln; Console.String("-- "); Console.String(s); Console.String(": "); + IF f # NIL THEN + IF f.registerName # "" THEN Console.String(f.registerName) ELSE Console.String(f.workName) END + END ; + IF errno # 0 THEN Console.String(" errno = "); Console.Int(errno, 1) END ; + Console.Ln; + HALT(99) + END Err; + + 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 Kernel.CWD[i] # 0X DO name[i] := Kernel.CWD[i]; INC(i) END; + IF Kernel.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 Create(f: File); + VAR stat: Unix.Status; done: BOOLEAN; + errno: LONGINT; err: ARRAY 32 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 ; + errno := Unix.Unlink(f.workName); (*unlink first to avoid stale NFS handles and to avoid reuse of inodes*) +f.fd := Unix.Open(f.workName, SHORT(SYSTEM.VAL(LONGINT, (Unix.rdwr + Unix.creat + Unix.trunc))), SHORT(SYSTEM.VAL(LONGINT, ({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(TRUE); + f.fd := Unix.Open(f.workName, SHORT(SYSTEM.VAL(LONGINT, (Unix.rdwr + Unix.creat + Unix.trunc))), SHORT(SYSTEM.VAL(LONGINT, {2, 4,5, 7,8}))); + done := f.fd >= 0 + END ; + IF done THEN + IF f.fd >= fileTabSize THEN errno := Unix.Close(f.fd); Err("too many files open", f, 0) + ELSE fileTab[f.fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.state := open; f.pos := 0; errno := Unix.Fstat(f.fd, 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" + ELSIF (errno = Unix.ENFILE) OR (errno = Unix.EMFILE) THEN err := "too many files open" + ELSE err := "file not created" + END ; + Err(err, f, errno) + END + END + END Create; + + PROCEDURE Flush(buf: Buffer); + VAR res: LONGINT; f: File; 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 Err("error in writing file", f, Unix.errno()) END ; + f.pos := buf.org + buf.size; + buf.chg := FALSE; + res := Unix.Fstat(f.fd, stat); + f.mtime := stat.mtime + END + END Flush; + + PROCEDURE Close* (f: File); + VAR i, res: LONGINT; + 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 < 0 THEN Err("error in writing file", f, Unix.errno()) 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; + BEGIN + NEW(f); f.workName := ""; COPY(name, 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); (* supports ~, ~user and blanks inside path *) + VAR i: INTEGER; ch: CHAR; home: ARRAY 256 OF CHAR; + BEGIN + i := 0; ch := Kernel.OBERON[pos]; + WHILE (ch = " ") OR (ch = ":") DO INC(pos); ch := Kernel.OBERON[pos] END ; + IF ch = "~" THEN + INC(pos); ch := Kernel.OBERON[pos]; + home := ""; Args.GetEnv("HOME", home); + WHILE home[i] # 0X DO dir[i] := home[i]; INC(i) END ; + IF (ch # "/") & (ch # 0X) & (ch # ":") & (ch # " ") THEN + WHILE (i > 0) & (dir[i-1] # "/") DO DEC(i) END + END + END ; + WHILE (ch # 0X) & (ch # ":") DO dir[i] := ch; INC(i); INC(pos); ch := Kernel.OBERON[pos] END ; + WHILE (i > 0) & (dir[i-1] = " ") DO DEC(i) 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, 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: INTEGER; res, errno: LONGINT; pos: INTEGER; done: BOOLEAN; + dir, path: ARRAY 256 OF CHAR; + stat: Unix.Status; + BEGIN + IF name # "" THEN + IF 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(path, SHORT(SYSTEM.VAL(LONGINT, Unix.rdwr)), SHORT(SYSTEM.VAL(LONGINT, {}))); 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(TRUE); + fd := Unix.Open(path, SHORT(SYSTEM.VAL(LONGINT, Unix.rdwr)), SHORT(SYSTEM.VAL(LONGINT, {}))); + done := fd >= 0; errno := Unix.errno(); + IF ~done & ((errno = Unix.ENFILE) OR (errno = Unix.EMFILE)) THEN Err("too many files open", f, errno) END + END ; + IF ~done & ((errno = Unix.EACCES) OR (errno = Unix.EROFS) OR (errno = Unix.EAGAIN)) THEN + (* errno EAGAIN observed on Solaris 2.4 *) + fd := Unix.Open(path, SHORT(SYSTEM.VAL(LONGINT, Unix.rdonly)), SHORT(SYSTEM.VAL(LONGINT, {}))); done := fd >= 0; errno := Unix.errno() + END ; +IF (~done) & (errno # Unix.ENOENT) THEN + Console.String("warning Files0.Old "); Console.String(name); + Console.String(" errno = "); Console.Int(errno, 0); Console.Ln; +END ; + IF done THEN + res := Unix.Fstat(fd, 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", f, 0) + ELSE NEW(f); fileTab[fd] := SYSTEM.VAL(LONGINT, f); INC(Kernel.nofiles); Kernel.RegisterObject(f, Finalize); + f.fd := fd; f.state := open; f.len := stat.size; f.pos := 0; f.swapper := -1; (*all f.buf[i] = NIL*) + COPY(name, 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, 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, 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: LONGINT; buf: Buffer; + 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.ReadBlk(f.fd, buf.data); + IF n < 0 THEN Err("read from file not done", f, Unix.errno()) 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 ReadByte* (VAR r : Rider; VAR x : ARRAY OF SYSTEM.BYTE); + BEGIN + ReadBytes(r, x, 1); + END ReadByte; + + 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 Delete* (name: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Unlink(name)); + res := SHORT(Unix.errno()) + END Delete; + + PROCEDURE Rename* (old, new: ARRAY OF CHAR; VAR res: INTEGER); + VAR fdold, fdnew: INTEGER; n, errno, r: LONGINT; + ostat, nstat: Unix.Status; + buf: ARRAY 4096 OF CHAR; + BEGIN + r := Unix.Stat(old, ostat); + IF r >= 0 THEN + r := Unix.Stat(new, 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(old, new); + IF r < 0 THEN res := SHORT(Unix.errno()); + IF res = Unix.EXDEV THEN (* cross device link, move the file *) + fdold := Unix.Open(old, SHORT(SYSTEM.VAL(LONGINT, Unix.rdonly)), SHORT(SYSTEM.VAL(LONGINT, {}))); + IF fdold < 0 THEN res := 2; RETURN END ; + fdnew := Unix.Open(new, SHORT(SYSTEM.VAL(LONGINT, Unix.rdwr + Unix.creat + Unix.trunc)), SHORT(SYSTEM.VAL(LONGINT, {2, 4,5, 7,8}))); + IF fdnew < 0 THEN r := Unix.Close(fdold); res := 3; RETURN 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); + Err("cannot move file", NIL, errno) + END ; + n := Unix.Read(fdold, SYSTEM.ADR(buf), bufsize) + END ; + errno := Unix.errno(); + r := Unix.Close(fdold); r := Unix.Close(fdnew); + IF n = 0 THEN r := Unix.Unlink(old); res := 0 + ELSE Err("cannot move file", NIL, errno) + END ; + ELSE RETURN (* res is Unix.Rename return code *) + END + END ; + res := 0 + ELSE res := 2 (* old file not found *) + END + END Rename; + + PROCEDURE Register* (f: File); + VAR idx, errno: INTEGER; f1: File; file: ARRAY 104 OF CHAR; + BEGIN + IF (f.state = create) & (f.registerName # "") THEN f.state := close (* shortcut renaming *) END ; + 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 ChangeDirectory*(path: ARRAY OF CHAR; VAR res: INTEGER); + BEGIN + res := SHORT(Unix.Chdir(path)); + getcwd(Kernel.CWD) + END ChangeDirectory; + + PROCEDURE FlipBytes(VAR src, dest: ARRAY OF SYSTEM.BYTE); + VAR i, j: LONGINT; + BEGIN + IF ~Kernel.littleEndian THEN i := LEN(src); j := 0; + WHILE i > 0 DO DEC(i); dest[j] := src[i]; INC(j) END + ELSE SYSTEM.MOVE(SYSTEM.ADR(src), SYSTEM.ADR(dest), LEN(src)) + 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, b, 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, b, 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, b, 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, b, 4); FlipBytes(b, x) + END ReadReal; + + PROCEDURE ReadLReal* (VAR R: Rider; VAR x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN ReadBytes(R, b, 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, b, 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, b, 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, b, 4); + END WriteSet; + + PROCEDURE WriteReal* (VAR R: Rider; x: REAL); + VAR b: ARRAY 4 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 4) + END WriteReal; + + PROCEDURE WriteLReal* (VAR R: Rider; x: LONGREAL); + VAR b: ARRAY 8 OF CHAR; + BEGIN FlipBytes(x, b); WriteBytes(R, b, 8) + END WriteLReal; + + PROCEDURE WriteString* (VAR R: Rider; x: ARRAY [1] 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 Finalize(o: SYSTEM.PTR); + VAR f: File; res: LONGINT; + BEGIN + f := SYSTEM.VAL(File, o); + IF f.fd >= 0 THEN + fileTab[f.fd] := 0; res := Unix.Close(f.fd); f.fd := -1; DEC(Kernel.nofiles); + IF f.tempFile THEN res := Unix.Unlink(f.workName) END + END + END Finalize; + + PROCEDURE Init; + VAR i: LONGINT; + BEGIN + i := 0; WHILE i < fileTabSize DO fileTab[i] := 0; INC(i) END ; + tempno := -1; Kernel.nofiles := 0 + END Init; + +BEGIN Init +END Files0. diff --git a/src/lib/system/linux/gcc/x86_64/Unix.Mod b/src/lib/system/linux/gcc/x86_64/Unix.Mod index 5bbea785..195bb41d 100644 --- a/src/lib/system/linux/gcc/x86_64/Unix.Mod +++ b/src/lib/system/linux/gcc/x86_64/Unix.Mod @@ -292,16 +292,18 @@ from man gettimeofday END ; Sockaddr* = RECORD - family*: INTEGER; - port*: INTEGER; - internetAddr*: LONGINT; - pad*: ARRAY 8 OF CHAR; + family0*, family1*: SHORTINT; + pad0, pad1: SHORTINT; + pad2 : INTEGER; + (*port*: INTEGER; + internetAddr*: LONGINT;*) + pad*: ARRAY 14 OF CHAR; END ; HostEntry* = POINTER [1] TO Hostent; Hostent* = RECORD name*, aliases*: LONGINT; - addrtype*, length*: LONGINT; + addrtype*, length*: INTEGER; addrlist*: LONGINT; (*POINTER TO POINTER TO LONGINT, network byte order*) END; @@ -337,71 +339,71 @@ from man gettimeofday RETURN err() END errno; - PROCEDURE -Exit*(n: LONGINT) + PROCEDURE -Exit*(n: INTEGER) "exit(n)"; - PROCEDURE -Fork*(): LONGINT + PROCEDURE -Fork*(): INTEGER "fork()"; - PROCEDURE -Wait*(VAR status: LONGINT): LONGINT + PROCEDURE -Wait*(VAR status: INTEGER): INTEGER "wait(status)"; - PROCEDURE -Select*(width: LONGINT; VAR readfds, writefds, exceptfds: FdSet; VAR timeout: Timeval): LONGINT + PROCEDURE -Select*(width: INTEGER; VAR readfds, writefds, exceptfds: FdSet; VAR timeout: Timeval): INTEGER "select(width, readfds, writefds, exceptfds, timeout)"; - PROCEDURE -Gettimeofday* (VAR tv: Timeval; VAR tz: Timezone) : LONGINT + PROCEDURE -Gettimeofday* (VAR tv: Timeval; VAR tz: Timezone) : INTEGER "gettimeofday(tv, tz)"; - PROCEDURE -Read* (fd, buf, nbyte: LONGINT): LONGINT + PROCEDURE -Read* (fd: INTEGER; buf, nbyte: LONGINT): LONGINT "read(fd, buf, nbyte)"; - PROCEDURE -ReadBlk* (fd: LONGINT; VAR buf: ARRAY OF SYSTEM.BYTE): LONGINT + PROCEDURE -ReadBlk* (fd: INTEGER; VAR buf: ARRAY OF SYSTEM.BYTE): LONGINT "read(fd, buf, buf__len)"; - PROCEDURE -Write* (fd, buf, nbyte: LONGINT): LONGINT + PROCEDURE -Write* (fd: INTEGER; buf, nbyte: LONGINT): LONGINT "write(fd, buf, nbyte)"; - PROCEDURE -WriteBlk* (fd: LONGINT; VAR buf: ARRAY OF SYSTEM.BYTE): LONGINT + PROCEDURE -WriteBlk* (fd: INTEGER; VAR buf: ARRAY OF SYSTEM.BYTE): LONGINT "write(fd, buf, buf__len)"; - PROCEDURE -Dup*(fd: LONGINT): LONGINT + PROCEDURE -Dup*(fd: INTEGER): INTEGER "dup(fd)"; - PROCEDURE -Dup2*(fd1, fd2: LONGINT): LONGINT + PROCEDURE -Dup2*(fd1, fd2: INTEGER): INTEGER "dup(fd1, fd2)"; - PROCEDURE -Pipe*(fds : LONGINT): LONGINT + PROCEDURE -Pipe*(fds : LONGINT): INTEGER "pipe(fds)"; - PROCEDURE -Getpid*(): LONGINT + PROCEDURE -Getpid*(): INTEGER "getpid()"; - PROCEDURE -Getuid*(): LONGINT + PROCEDURE -Getuid*(): INTEGER "getuid()"; - PROCEDURE -Geteuid*(): LONGINT + PROCEDURE -Geteuid*(): INTEGER "geteuid()"; - PROCEDURE -Getgid*(): LONGINT + PROCEDURE -Getgid*(): INTEGER "getgid()"; - PROCEDURE -Getegid*(): LONGINT + PROCEDURE -Getegid*(): INTEGER "getegid()"; - PROCEDURE -Unlink*(name: Name): LONGINT + PROCEDURE -Unlink*(name: Name): INTEGER "unlink(name)"; - PROCEDURE -Open*(name: Name; flag, mode: SET): LONGINT + PROCEDURE -Open*(name: Name; flag: INTEGER; mode: LONGINT): INTEGER "open(name, flag, mode)"; - PROCEDURE -Close*(fd: LONGINT): LONGINT + PROCEDURE -Close*(fd: INTEGER): INTEGER "close(fd)"; - PROCEDURE -stat(name: Name; VAR statbuf: Status): LONGINT + PROCEDURE -stat(name: Name; VAR statbuf: Status): INTEGER "stat((const char*)name, (struct stat*)statbuf)"; - PROCEDURE Stat*(name: Name; VAR statbuf: Status): LONGINT; - VAR res: LONGINT; + PROCEDURE Stat*(name: Name; VAR statbuf: Status): INTEGER; + VAR res: INTEGER; BEGIN res := stat(name, statbuf); (* make the first 4 bytes as unique as possible (used in module Files for caching!) *) @@ -411,11 +413,11 @@ from man gettimeofday RETURN res; END Stat; - PROCEDURE -fstat(fd: LONGINT; VAR statbuf: Status): LONGINT + PROCEDURE -fstat(fd: INTEGER; VAR statbuf: Status): INTEGER "fstat(fd, (struct stat*)statbuf)"; - PROCEDURE Fstat*(fd: LONGINT; VAR statbuf: Status): LONGINT; - VAR res: LONGINT; + PROCEDURE Fstat*(fd: INTEGER; VAR statbuf: Status): INTEGER; + VAR res: INTEGER; BEGIN res := fstat(fd, statbuf); (* make the first 4 bytes as unique as possible (used in module Files for caching!) *) @@ -424,81 +426,81 @@ from man gettimeofday RETURN res; END Fstat; - PROCEDURE -Fchmod*(fd, mode: LONGINT): LONGINT + PROCEDURE -Fchmod*(fd, mode: INTEGER): INTEGER "fchmod(fd, mode)"; - PROCEDURE -Chmod*(path: Name; mode: LONGINT): LONGINT + PROCEDURE -Chmod*(path: Name; mode: INTEGER): INTEGER "chmod(path, mode)"; - PROCEDURE -Lseek*(fd, offset, origin: LONGINT): LONGINT + PROCEDURE -Lseek*(fd: INTEGER; offset: LONGINT; origin: INTEGER): LONGINT "lseek(fd, offset, origin)"; - PROCEDURE -Fsync*(fd: LONGINT): LONGINT + PROCEDURE -Fsync*(fd: INTEGER): INTEGER "fsync(fd)"; - PROCEDURE -Fcntl*(fd, cmd, arg: LONGINT ): LONGINT + PROCEDURE -Fcntl*(fd: INTEGER; cmd: INTEGER; arg: LONGINT ): INTEGER "fcntl(fd, cmd, arg)"; - PROCEDURE -Flock*(fd, operation: LONGINT): LONGINT + PROCEDURE -Flock*(fd, operation: INTEGER): INTEGER "flock(fd, operation)"; - PROCEDURE -Ftruncate*(fd, length: LONGINT): LONGINT + PROCEDURE -Ftruncate*(fd: INTEGER; length: LONGINT): INTEGER "ftruncate(fd, length)"; - PROCEDURE -Readblk*(fd: LONGINT; VAR buf: ARRAY OF SYSTEM.BYTE; len: LONGINT): LONGINT + PROCEDURE -Readblk*(fd: INTEGER; VAR buf: ARRAY OF SYSTEM.BYTE; len: LONGINT): LONGINT "read(fd, buf, len)"; - PROCEDURE -Rename*(old, new: Name): LONGINT + PROCEDURE -Rename*(old, new: Name): INTEGER "rename(old, new)"; - PROCEDURE -Chdir*(path: Name): LONGINT + PROCEDURE -Chdir*(path: Name): INTEGER "chdir(path)"; - PROCEDURE -Ioctl*(fd, request, arg: LONGINT): LONGINT + PROCEDURE -Ioctl*(fd: INTEGER; request, arg: LONGINT): INTEGER "ioctl(fd, request, arg)"; - PROCEDURE -Kill*(pid, sig: LONGINT): LONGINT + PROCEDURE -Kill*(pid, sig: INTEGER): INTEGER "kill(pid, sig)"; - PROCEDURE -Sigsetmask*(mask: LONGINT): LONGINT + PROCEDURE -Sigsetmask*(mask: INTEGER): INTEGER "sigsetmask(mask)"; PROCEDURE -Sleep*(ms : INTEGER): INTEGER - "sleep(ms)"; + "(INTEGER)sleep(ms)"; PROCEDURE -Nanosleep*(VAR req : Timeval; VAR rem : Timeval): INTEGER - "nanosleep(req, rem)"; + "(INTEGER)nanosleep(req, rem)"; (* TCP/IP networking *) PROCEDURE -Gethostbyname*(name: Name): HostEntry "(Unix_HostEntry)gethostbyname(name)"; - PROCEDURE -Gethostname*(VAR name: Name): LONGINT + PROCEDURE -Gethostname*(VAR name: Name): INTEGER "gethostname(name, name__len)"; - PROCEDURE -Socket*(af, type, protocol: LONGINT): LONGINT + PROCEDURE -Socket*(af, type, protocol: INTEGER): INTEGER "socket(af, type, protocol)"; - PROCEDURE -Connect*(socket: LONGINT; name: Sockaddr; namelen: LONGINT): LONGINT + PROCEDURE -Connect*(socket: INTEGER; name: Sockaddr; namelen: INTEGER): INTEGER "connect(socket, &(name), namelen)"; - PROCEDURE -Getsockname*(socket: LONGINT; VAR name: Sockaddr; VAR namelen: LONGINT): LONGINT + PROCEDURE -Getsockname*(socket: INTEGER; VAR name: Sockaddr; VAR namelen: INTEGER): INTEGER "getsockname(socket, name, namelen)"; - PROCEDURE -Bind*(socket: LONGINT; name: Sockaddr; namelen: LONGINT): LONGINT + PROCEDURE -Bind*(socket: INTEGER; name: Sockaddr; namelen: INTEGER): INTEGER "bind(socket, &(name), namelen)"; - PROCEDURE -Listen*(socket, backlog: LONGINT): LONGINT + PROCEDURE -Listen*(socket, backlog: INTEGER): INTEGER "listen(socket, backlog)"; - PROCEDURE -Accept*(socket: LONGINT; VAR addr: Sockaddr; VAR addrlen: LONGINT): LONGINT + PROCEDURE -Accept*(socket: INTEGER; VAR addr: Sockaddr; VAR addrlen: INTEGER): LONGINT "accept(socket, addr, addrlen)"; - PROCEDURE -Recv*(socket, bufadr, buflen, flags: LONGINT): LONGINT + PROCEDURE -Recv*(socket: INTEGER; bufadr, buflen: LONGINT; flags: INTEGER): LONGINT "recv(socket, bufadr, buflen, flags)"; - PROCEDURE -Send*(socket, bufadr, buflen, flags: LONGINT): LONGINT + PROCEDURE -Send*(socket: INTEGER; bufadr, buflen: LONGINT; flags: INTEGER): LONGINT "send(socket, bufadr, buflen, flags)"; PROCEDURE -sys(str: ARRAY OF CHAR): INTEGER (* need this to call external tools like gcc or gas; noch *) diff --git a/src/lib/ulm/armv6j/ulmSysConversions.Mod b/src/lib/ulm/armv6j/ulmSysConversions.Mod deleted file mode 100644 index f8ea3fbb..00000000 --- a/src/lib/ulm/armv6j/ulmSysConversions.Mod +++ /dev/null @@ -1,574 +0,0 @@ -(* Ulm's Oberon Library - Copyright (C) 1989-1994 by University of Ulm, SAI, D-89069 Ulm, Germany - ---------------------------------------------------------------------------- - Ulm's Oberon Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either version - 2 of the License, or (at your option) any later version. - - Ulm's Oberon Library is distributed in the hope that it will be - useful, but WITHOUT ANY WARRANTY; without even the implied warranty - of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - ---------------------------------------------------------------------------- - E-mail contact: oberon@mathematik.uni-ulm.de - ---------------------------------------------------------------------------- - $Id: SysConversi.om,v 1.2 1997/07/30 09:38:16 borchert Exp $ - ---------------------------------------------------------------------------- - $Log: SysConversi.om,v $ - Revision 1.2 1997/07/30 09:38:16 borchert - bug in ReadConv fixed: cv.flags was used but not set for - counts > 1 - - Revision 1.1 1994/02/23 07:58:28 borchert - Initial revision - - ---------------------------------------------------------------------------- - AFB 8/90 - adapted to linux cae 02/01 - ---------------------------------------------------------------------------- -*) - -MODULE ulmSysConversions; - - (* convert Oberon records to/from C structures *) - - IMPORT Events := ulmEvents, Objects := ulmObjects, Priorities := ulmPriorities, Streams := ulmStreams, Strings := ulmStrings, - SYS := SYSTEM, SysTypes := ulmSysTypes, Texts := ulmTexts; - - TYPE - Address* = SysTypes.Address; - Size* = Address; - - (* format: - - Format = Conversion { "/" Conversion } . - Conversion = [ Factors ] ConvChars [ Comment ] . - Factors = Array | Factor | Array Factor | Factor Array . - Array = Integer ":" . - Factor = Integer "*" . - ConvChars = OberonType CType | Skip CType | OberonType Skip . - OberonType = "a" | "b" | "c" | "s" | "i" | "l" | "S" . - CType = "a" | "c" | "s" | "i" | "l" . - Integer = Digit { Digit } . - Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" . - Skip = "-" . - Comment = "=" { AnyChar } . - AnyChar = (* all characters except "/" *) . - - Oberon data types: - - a: Address - b: SYS.BYTE - B: BOOLEAN - c: CHAR - s: SHORTINT - i: INTEGER - l: LONGINT - S: SET - - C data types: - - a: char * - c: /* signed */ char - C: unsigned char - s: short int - S: unsigned short int - i: int - I: unsigned int - u: unsigned int - l: long int - L: unsigned long int - - example: - - conversion from - - Rec = - RECORD - a, b: INTEGER; - c: CHAR; - s: SET; - f: ARRAY 3 OF INTEGER; - END; - - to - - struct rec { - short a, b; - char c; - int xx; /* to be skipped on conversion */ - int s; - int f[3]; - }; - - or vice versa: - - "2*is=a,b/cc=c/-i=xx/Si=s/3:ii=f" - - The comments allow to give the field names. - *) - - CONST - (* conversion flags *) - unsigned = 0; (* suppress sign extension *) - boolean = 1; (* convert anything # 0 to 1 *) - TYPE - Flags = SET; - Event* = POINTER TO EventRec; - EventRec* = - RECORD - (Events.EventRec) - format*: Events.Message; - END; - ConvStream = POINTER TO ConvStreamRec; - ConvStreamRec = - RECORD - fmt: Texts.Text; - char: CHAR; - eof: BOOLEAN; - (* 1: Oberon type - 2: C type - *) - type1, type2: CHAR; length: INTEGER; left: INTEGER; - offset1, offset2: Address; - size1, size2: Address; elementsleft: INTEGER; flags: Flags; - END; - - Format* = POINTER TO FormatRec; - FormatRec* = - RECORD - (Objects.ObjectRec) - offset1, offset2: Address; - size1, size2: Address; - flags: Flags; - next: Format; - END; - VAR - badformat*: Events.EventType; - - PROCEDURE Error(cv: ConvStream; msg: ARRAY OF CHAR); - VAR - event: Event; - BEGIN - NEW(event); - event.type := badformat; - event.message := "SysConversions: "; - Strings.Concatenate(event.message, msg); - Strings.Read(event.format, cv.fmt); - Events.Raise(event); - cv.eof := TRUE; - cv.char := 0X; - cv.left := 0; - cv.elementsleft := 0; - END Error; - - PROCEDURE SizeError(msg, format: ARRAY OF CHAR); - VAR - event: Event; - BEGIN - NEW(event); - event.type := badformat; - event.message := "SysConversions: "; - Strings.Concatenate(event.message, msg); - COPY(format, event.format); - Events.Raise(event); - END SizeError; - - PROCEDURE NextCh(cv: ConvStream); - BEGIN - cv.eof := cv.eof OR ~Streams.ReadByte(cv.fmt, cv.char) OR (cv.char = 0X); - IF cv.eof THEN - cv.char := 0X; - END; - END NextCh; - - PROCEDURE IsDigit(ch: CHAR) : BOOLEAN; - BEGIN - RETURN (ch >= "0") & (ch <= "9") - END IsDigit; - - PROCEDURE ReadInt(cv: ConvStream; VAR i: INTEGER); - BEGIN - i := 0; - REPEAT - i := 10 * i + ORD(cv.char) - ORD("0"); - NextCh(cv); - UNTIL ~IsDigit(cv.char); - END ReadInt; - - PROCEDURE Open(VAR cv: ConvStream; format: ARRAY OF CHAR); - BEGIN - NEW(cv); - Texts.Open(SYS.VAL(Streams.Stream, cv.fmt)); - Strings.Write(cv.fmt, format); - cv.left := 0; cv.elementsleft := 0; - cv.offset1 := 0; cv.offset2 := 0; - cv.eof := FALSE; - NextCh(cv); - END Open; - - PROCEDURE Close(VAR cv: ConvStream); - BEGIN - IF ~Streams.Close(cv.fmt) THEN END; - END Close; - - PROCEDURE ScanConv(cv: ConvStream; - VAR type1, type2: CHAR; - VAR length: INTEGER) : BOOLEAN; - VAR - i: INTEGER; - factor: INTEGER; - BEGIN - IF cv.left > 0 THEN - type1 := cv.type1; - type2 := cv.type2; - length := cv.length; - DEC(cv.left); - RETURN TRUE - END; - IF cv.char = "/" THEN - NextCh(cv); - END; - IF cv.eof THEN - RETURN FALSE - END; - factor := 0; length := 0; - WHILE IsDigit(cv.char) DO - ReadInt(cv, i); - IF i <= 0 THEN - Error(cv, "integer must be positive"); RETURN FALSE - END; - IF cv.char = ":" THEN - IF length # 0 THEN - Error(cv, "multiple length specification"); RETURN FALSE - END; - length := i; - NextCh(cv); - ELSIF cv.char = "*" THEN - IF factor # 0 THEN - Error(cv, "multiple factor specification"); RETURN FALSE - END; - factor := i; cv.left := factor - 1; - NextCh(cv); - ELSE - Error(cv, "factor or length expected"); RETURN FALSE - END; - END; - type1 := cv.char; NextCh(cv); - type2 := cv.char; NextCh(cv); - IF cv.left > 0 THEN - cv.type1 := type1; cv.type2 := type2; cv.length := length; - END; - IF cv.char = "=" THEN (* comment *) - REPEAT - NextCh(cv); - UNTIL cv.eof OR (cv.char = "/"); - END; - RETURN TRUE - END ScanConv; - - PROCEDURE Align(VAR offset: Address; boundary: Address); - BEGIN - IF SYS.VAL (INTEGER, offset) MOD SYS.VAL (INTEGER, boundary) # 0 THEN - offset := SYS.VAL (INTEGER, offset) + (SYS.VAL (INTEGER, boundary) - SYS.VAL (INTEGER, offset) MOD SYS.VAL (INTEGER, boundary)); - END; - END Align; - - PROCEDURE ReadConv(cv: ConvStream; - VAR offset1, offset2: Address; - VAR size1, size2: Address; - VAR flags: Flags) : BOOLEAN; - VAR - type1, type2: CHAR; - length: INTEGER; - align: BOOLEAN; - boundary: INTEGER; - BEGIN - IF cv.elementsleft > 0 THEN - DEC(cv.elementsleft); - - (* Oberon type *) - IF size1 > SIZE(SYS.BYTE) THEN - Align(cv.offset1, SIZE(INTEGER)); - END; - offset1 := cv.offset1; cv.offset1 := SYS.VAL (INTEGER, cv.offset1) + size1; - size1 := cv.size1; size2 := cv.size2; flags := cv.flags; - IF (size1 > 0) & (cv.elementsleft = 0) THEN - Align(cv.offset1, SIZE(INTEGER)); - END; - - (* C type *) - IF size2 > 1 THEN - Align(cv.offset2, 2); - END; - offset2 := cv.offset2; cv.offset2 := SYS.VAL (INTEGER, cv.offset2) + SYS.VAL (INTEGER, size2); - - RETURN TRUE - END; - IF ScanConv(cv, type1, type2, length) THEN - flags := {}; - (* Oberon type *) - CASE type1 OF - | "a": size1 := SIZE(Address); INCL(flags, unsigned); - | "b": size1 := SIZE(SYS.BYTE); INCL(flags, unsigned); - | "B": size1 := SIZE(BOOLEAN); INCL(flags, boolean); - | "c": size1 := SIZE(CHAR); INCL(flags, unsigned); - | "s": size1 := SIZE(SHORTINT); - | "i": size1 := SIZE(INTEGER); - | "l": size1 := SIZE(LONGINT); - | "S": size1 := SIZE(SET); INCL(flags, unsigned); - | "-": size1 := 0; - ELSE Error(cv, "bad Oberon type specifier"); RETURN FALSE - END; - IF size1 > 0 THEN - IF length > 0 THEN - Align(cv.offset1, SIZE(INTEGER)); - ELSIF size1 > SIZE(SYS.BYTE) THEN - Align(cv.offset1, SIZE(INTEGER)); - END; - END; - offset1 := cv.offset1; cv.offset1 := SYS.VAL (INTEGER, cv.offset1) + size1; - - (* C type *) - CASE type2 OF - | "a": size2 := 4; INCL(flags, unsigned); (* char* *) - | "c": size2 := 1; (* /* signed */ char *) - | "C": size2 := 1; INCL(flags, unsigned); (* unsigned char *) - | "s": size2 := 2; (* short int *) - | "S": size2 := 2; INCL(flags, unsigned); (* unsigned short int *) - | "i": size2 := 4; (* int *) - | "I": size2 := 4; INCL(flags, unsigned); (* unsigned int *) - | "u": size2 := 4; INCL(flags, unsigned); (* unsigned int *) - | "l": size2 := 4; (* long int *) - | "L": size2 := 4; INCL(flags, unsigned); (* long int *) - | "-": size2 := 0; - ELSE Error(cv, "bad C type specifier"); RETURN FALSE - END; - IF size2 > 1 THEN - Align(cv.offset2, size2); - END; - offset2 := cv.offset2; cv.offset2 := SYS.VAL (INTEGER, cv.offset2) + SYS.VAL (INTEGER, size2); - - cv.size1 := size1; cv.size2 := size2; - IF length > 0 THEN - cv.elementsleft := length - 1; - cv.flags := flags; - END; - RETURN TRUE - ELSE - RETURN FALSE - END; - END ReadConv; - - PROCEDURE Convert(from, to: Address; ssize, dsize: Address; flags: Flags); - TYPE - Bytes = ARRAY 8 OF CHAR; - Pointer = POINTER TO Bytes; - VAR - dest, source: Pointer; - dindex, sindex: INTEGER; - nonzero: BOOLEAN; - fill : CHAR; - BEGIN - IF ssize > 0 THEN - dest := SYS.VAL(Pointer, to); - source := SYS.VAL(Pointer, from); - dindex := 0; sindex := 0; - IF boolean IN flags THEN - nonzero := FALSE; - WHILE ssize > 0 DO - nonzero := nonzero OR (source[sindex] # 0X); - INC(sindex); ssize := SYS.VAL (INTEGER, ssize) - 1; - END; - IF dsize > 0 THEN - IF nonzero THEN - dest[dindex] := 1X; - ELSE - dest[dindex] := 0X; - END; - dsize := dsize - 1; INC (dindex); - END; - WHILE dsize > 0 DO - dest[dindex] := 0X; - dsize := SYS.VAL (INTEGER, dsize) - 1; INC(dindex); - END; - ELSE - WHILE (dsize > 0) & (ssize > 0) DO - dest[dindex] := source[sindex]; - ssize := SYS.VAL (INTEGER, ssize) - 1; - dsize := dsize - 1; - INC(dindex); INC(sindex); - END; - IF dsize > 0 THEN - (* sindex has been incremented at least once because - * ssize and dsize were greater than 0, i.e. sindex-1 - * is a valid inex. *) - fill := 0X; - IF ~(unsigned IN flags) & (source[sindex-1] >= 080X) THEN - fill := 0FFX; - END; - END; - WHILE dsize > 0 DO - dest[dindex] := fill; - dsize := SYS.VAL (INTEGER, dsize) - 1; INC(dindex); - END; - END; - END; - END Convert; - - PROCEDURE ByAddrToC*(from, to: Address; format: ARRAY OF CHAR); - VAR - cv: ConvStream; - offset1, offset2, size1, size2: Address; - flags: Flags; - BEGIN - Open(cv, format); - WHILE ReadConv(cv, offset1, offset2, size1, size2, flags) DO - Convert(from + offset1, to + offset2, size1, size2, flags); - END; - Close(cv); - END ByAddrToC; - - PROCEDURE ByAddrFromC*(from, to: Address; format: ARRAY OF CHAR); - VAR - cv: ConvStream; - offset1, offset2, size1, size2: Address; - flags: Flags; - BEGIN - Open(cv, format); - WHILE ReadConv(cv, offset1, offset2, size1, size2, flags) DO - Convert(from + offset2, to + offset1, size2, size1, flags); - END; - Close(cv); - END ByAddrFromC; - - PROCEDURE CSize*(format: ARRAY OF CHAR) : Size; - (* returns the size of the C-structure described by `format' *) - VAR - cv: ConvStream; - offset1, offset2, size1, size2: Address; - size: Address; - flags: Flags; - BEGIN - Open(cv, format); - WHILE ReadConv(cv, offset1, offset2, size1, size2, flags) DO END; - Close(cv); - size := offset2 + size2; - Align(size, 2); - RETURN size - END CSize; - - PROCEDURE OberonSize*(format: ARRAY OF CHAR) : Size; - (* returns the size of the Oberon-structure described by `format' *) - VAR - cv: ConvStream; - offset1, offset2, size1, size2: Address; - size: Address; - flags: Flags; - BEGIN - Open(cv, format); - WHILE ReadConv(cv, offset1, offset2, size1, size2, flags) DO END; - Close(cv); - size := offset1 + size1; - Align(size, SIZE(INTEGER)); - RETURN size - END OberonSize; - - PROCEDURE ToC*(VAR from, to: ARRAY OF SYS.BYTE; format: ARRAY OF CHAR); - BEGIN - IF OberonSize(format) > LEN(from) THEN - SizeError("Oberon record is too small", format); RETURN - END; - IF CSize(format) > LEN(to) THEN - SizeError("C structure is too small", format); RETURN - END; - ByAddrToC(SYS.ADR(from), SYS.ADR(to), format); - END ToC; - - PROCEDURE FromC*(VAR from, to: ARRAY OF SYS.BYTE; format: ARRAY OF CHAR); - BEGIN - IF OberonSize(format) > LEN(to) THEN - SizeError("Oberon record is too small", format); RETURN - END; - IF CSize(format) > LEN(from) THEN - SizeError("C structure is too small", format); RETURN - END; - ByAddrFromC(SYS.ADR(from), SYS.ADR(to), format); - END FromC; - - PROCEDURE Compile*(VAR fmt: Format; format: ARRAY OF CHAR); - (* translate format into an internal representation - which is later referenced by fmt; - ByFmtToC and ByFmtFromC are faster than ToC and FromC - *) - VAR - cv: ConvStream; - offset1, offset2, size1, size2: Address; - flags: Flags; - element: Format; - head, tail: Format; - BEGIN - Open(cv, format); - head := NIL; tail := NIL; - WHILE ReadConv(cv, offset1, offset2, size1, size2, flags) DO - NEW(element); - element.offset1 := offset1; - element.offset2 := offset2; - element.size1 := size1; - element.size2 := size2; - element.flags := flags; - element.next := NIL; - IF tail # NIL THEN - tail.next := element; - ELSE - head := element; - END; - tail := element; - END; - fmt := head; - Close(cv); - END Compile; - - PROCEDURE ByFmtAndAddrToC*(from, to: Address; format: Format); - VAR - offset1, offset2, size1, size2: Address; - flags: Flags; - BEGIN - WHILE format # NIL DO - Convert(from + format.offset1, to + format.offset2, - format.size1, format.size2, format.flags); - format := format.next; - END; - END ByFmtAndAddrToC; - - PROCEDURE ByFmtAndAddrFromC*(from, to: Address; format: Format); - VAR - offset1, offset2, size1, size2: Address; - flags: Flags; - BEGIN - WHILE format # NIL DO - Convert(from + format.offset2, to + format.offset1, - format.size2, format.size1, format.flags); - format := format.next; - END; - END ByFmtAndAddrFromC; - - PROCEDURE ByFmtToC*(VAR from, to: ARRAY OF SYS.BYTE; format: Format); - BEGIN - ByFmtAndAddrToC(SYS.ADR(from), SYS.ADR(to), format); - END ByFmtToC; - - PROCEDURE ByFmtFromC*(VAR from, to: ARRAY OF SYS.BYTE; format: Format); - BEGIN - ByFmtAndAddrFromC(SYS.ADR(from), SYS.ADR(to), format); - END ByFmtFromC; - -BEGIN - Events.Define(badformat); - Events.SetPriority(badformat, Priorities.liberrors); -END ulmSysConversions. diff --git a/src/lib/ulm/armv6j/ulmSysStat.Mod b/src/lib/ulm/armv6j/ulmSysStat.Mod deleted file mode 100644 index c7f00f04..00000000 --- a/src/lib/ulm/armv6j/ulmSysStat.Mod +++ /dev/null @@ -1,201 +0,0 @@ -(* Ulm's Oberon Library - Copyright (C) 1989-1994 by University of Ulm, SAI, D-89069 Ulm, Germany - ---------------------------------------------------------------------------- - Ulm's Oberon Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either version - 2 of the License, or (at your option) any later version. - - Ulm's Oberon Library is distributed in the hope that it will be - useful, but WITHOUT ANY WARRANTY; without even the implied warranty - of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - ---------------------------------------------------------------------------- - E-mail contact: oberon@mathematik.uni-ulm.de - ---------------------------------------------------------------------------- - $Id: SysStat.om,v 1.3 2000/11/12 13:02:09 borchert Exp $ - ---------------------------------------------------------------------------- - $Log: SysStat.om,v $ - Revision 1.3 2000/11/12 13:02:09 borchert - door file type added - - Revision 1.2 2000/11/12 12:48:07 borchert - - conversion adapted to Solaris 2.x - - Lstat added - - Revision 1.1 1994/02/23 08:00:48 borchert - Initial revision - - ---------------------------------------------------------------------------- - AFB 9/89 - ---------------------------------------------------------------------------- -*) - -MODULE ulmSysStat; - - (* examine inode: stat(2) and fstat(2) *) - - IMPORT RelatedEvents := ulmRelatedEvents, Sys := ulmSys, SYS := SYSTEM, uSYS := ulmSYSTEM, SysConversions := ulmSysConversions, SysErrors := ulmSysErrors, - SysTypes := ulmSysTypes; - - CONST - (* file mode: - bit 0 = 1<<0 bit 31 = 1<<31 - - user group other - 3 1 1111 11 - 1 ... 6 5432 109 876 543 210 - +--------+------+-----+-----+-----+-----+ - | unused | type | sst | rwx | rwx | rwx | - +--------+------+-----+-----+-----+-----+ - *) - - type* = {12..15}; - prot* = {0..8}; - - (* file types; example: (stat.mode * type = dir) *) - reg* = {15}; (* regular *) - dir* = {14}; (* directory *) - chr* = {13}; (* character special *) - fifo* = {12}; (* fifo *) - blk* = {13..14}; (* block special *) - symlink* = {13, 15}; (* symbolic link *) - socket* = {14, 15}; (* socket *) - - (* special *) - setuid* = 11; (* set user id on execution *) - setgid* = 10; (* set group id on execution *) - savetext* = 9; (* save swapped text even after use *) - - (* protection *) - uread* = 8; (* read permission owner *) - uwrite* = 7; (* write permission owner *) - uexec* = 6; (* execute/search permission owner *) - gread* = 5; (* read permission group *) - gwrite* = 4; (* write permission group *) - gexec* = 3; (* execute/search permission group *) - oread* = 2; (* read permission other *) - owrite* = 1; (* write permission other *) - oexec* = 0; (* execute/search permission other *) - - (* example for "r-xr-x---": (read + exec) * (owner + group) *) - owner* = {uread, uwrite, uexec}; - group* = {gread, gwrite, gexec}; - other* = {oread, owrite, oexec}; - read* = {uread, gread, oread}; - write* = {uwrite, gwrite, owrite}; - exec* = {uexec, gexec, oexec}; - rwx* = prot; - - TYPE - StatRec* = (* result of stat(2) and fstat(2) *) - RECORD - device*: SysTypes.Device; (* ID of device containing - a directory entry for this file *) - inode*: SysTypes.Inode; (* inode number *) - mode*: SET; (* file mode; see mknod(2) *) - nlinks*: LONGINT; (* number of links *) - uid*: LONGINT; (* user id of the file's owner *) - gid*: LONGINT; (* group id of the file's group *) - rdev*: SysTypes.Device; (* ID of device - this entry is defined only for - character special or block - special files - *) - size*: SysTypes.Offset; (* file size in bytes *) - blksize*: LONGINT; (* preferred blocksize *) - blocks*: LONGINT; (* # of blocks allocated *) - atime*: SysTypes.Time; (* time of last access *) - mtime*: SysTypes.Time; (* time of last data modification *) - ctime*: SysTypes.Time; (* time of last file status change *) - END; - -(* Linux kernel struct stat (2.2.17) - struct stat { - unsigned short st_dev; - unsigned short __pad1; - unsigned long st_ino; - unsigned short st_mode; - unsigned short st_nlink; - unsigned short st_uid; - unsigned short st_gid; - unsigned short st_rdev; - unsigned short __pad2; - unsigned long st_size; - unsigned long st_blksize; - unsigned long st_blocks; - unsigned long st_atime; - unsigned long __unused1; - unsigned long st_mtime; - unsigned long __unused2; - unsigned long st_ctime; - unsigned long __unused3; - unsigned long __unused4; - unsigned long __unused5; - }; -*) - - CONST - statbufsize = 88(*64*); (* see *) (* sizeof struct stat gives us 144 on x86_64 and 88 on x86 *) - TYPE - UnixStatRec = ARRAY statbufsize OF SYS.BYTE; - CONST - statbufconv = - (*"is=dev/-s=pad1/ll=ino/Ss=mode/4*is=nlink+uid+gid+rdev/-s=pad2/ll=size/2*ll=blksize,blocks/il=atime/-l/il=mtime/-l/il=ctime/3*-l";*) - (*"ls=dev/-s=pad1/lL=ino/Ss=mode/4*is=nlink+uid+gid+rdev/-s=pad2/lL=size/2*lL=blksize,blocks/lL=atime/-l/lL=mtime/-l/lL=ctime/3*-l";*) - "ll=dev/-l=devx/-s=pad1/ll=ino/Sl=mode/ll=nlink/ll=uid/ll=gid/ll=rdev/-l=rdevx/-s=pad2/ll=size/2*ll=blksize,blocks/lL=atime/-l/lL=mtime/-l/lL=ctime/3*-l"; - VAR - statbuffmt: SysConversions.Format; - - PROCEDURE Stat*(path: ARRAY OF CHAR; VAR buf: StatRec; - errors: RelatedEvents.Object) : BOOLEAN; - VAR - d0, d1, d2: LONGINT; - origbuf: UnixStatRec; - BEGIN - IF uSYS.UNIXCALL(Sys.newstat, d0, d1, SYS.ADR(path), SYS.ADR(origbuf), d2) THEN - SysConversions.ByFmtFromC(origbuf, buf, statbuffmt); - RETURN TRUE - ELSE - SysErrors.Raise(errors, d0, Sys.newstat, path); - RETURN FALSE - END; - END Stat; -(* - PROCEDURE Lstat*(path: ARRAY OF CHAR; VAR buf: StatRec; - errors: RelatedEvents.Object) : BOOLEAN; - VAR - d0, d1: INTEGER; - origbuf: UnixStatRec; - BEGIN - IF SYS.UNIXCALL(Sys.newlstat, d0, d1, SYS.ADR(path), SYS.ADR(origbuf)) THEN - SysConversions.ByFmtFromC(origbuf, buf, statbuffmt); - RETURN TRUE - ELSE - SysErrors.Raise(errors, d0, Sys.newlstat, path); - RETURN FALSE - END; - END Lstat; -*) - PROCEDURE Fstat*(fd: SysTypes.File; VAR buf: StatRec; - errors: RelatedEvents.Object) : BOOLEAN; - VAR - d0, d1, d2: LONGINT; - origbuf: UnixStatRec; - BEGIN - IF uSYS.UNIXCALL(Sys.newfstat, d0, d1, fd, SYS.ADR(origbuf), d2) THEN - SysConversions.ByFmtFromC(origbuf, buf, statbuffmt); - RETURN TRUE - ELSE - SysErrors.Raise(errors, d0, Sys.newfstat, ""); - RETURN FALSE - END; - END Fstat; - -BEGIN - SysConversions.Compile(statbuffmt, statbufconv); -END ulmSysStat. diff --git a/src/lib/ulm/armv6j/ulmSysTypes.Mod b/src/lib/ulm/armv6j/ulmSysTypes.Mod deleted file mode 100644 index 174140e7..00000000 --- a/src/lib/ulm/armv6j/ulmSysTypes.Mod +++ /dev/null @@ -1,70 +0,0 @@ -(* Ulm's Oberon Library - Copyright (C) 1989-1994 by University of Ulm, SAI, D-89069 Ulm, Germany - ---------------------------------------------------------------------------- - Ulm's Oberon Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either version - 2 of the License, or (at your option) any later version. - - Ulm's Oberon Library is distributed in the hope that it will be - useful, but WITHOUT ANY WARRANTY; without even the implied warranty - of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - ---------------------------------------------------------------------------- - E-mail contact: oberon@mathematik.uni-ulm.de - ---------------------------------------------------------------------------- - $Id: SysTypes.om,v 1.1 1994/02/23 08:01:38 borchert Exp $ - ---------------------------------------------------------------------------- - $Log: SysTypes.om,v $ - Revision 1.1 1994/02/23 08:01:38 borchert - Initial revision - - ---------------------------------------------------------------------------- - AFB 9/89 - ---------------------------------------------------------------------------- -*) - -MODULE ulmSysTypes; - - IMPORT Types := ulmTypes; - - TYPE - Address* = Types.Address; - UntracedAddress* = Types.UntracedAddress; - Count* = Types.Count; - Size* = Types.Size; - Byte* = Types.Byte; - - File* = (*INTEGER*)LONGINT; (* in ulm's system both INTEGER and LONGINT are 4 bytes long *) - Offset* = LONGINT; - Device* = LONGINT; - Inode* = LONGINT; - Time* = LONGINT; - - Word* = INTEGER; (* must have the size of C's int-type *) - - (* Note: linux supports wait4 but not waitid, i.e. these - * constants aren't needed. *) - (* - CONST - (* possible values of the idtype parameter (4 bytes), - see - *) - idPid = 0; (* a process identifier *) - idPpid = 1; (* a parent process identifier *) - idPgid = 2; (* a process group (job control group) identifier *) - idSid = 3; (* a session identifier *) - idCid = 4; (* a scheduling class identifier *) - idUid = 5; (* a user identifier *) - idGid = 6; (* a group identifier *) - idAll = 7; (* all processes *) - idLwpid = 8; (* an LWP identifier *) - TYPE - IdType = INTEGER; (* idPid .. idLwpid *) - *) - -END ulmSysTypes. diff --git a/src/lib/ulm/armv6j/ulmTypes.Mod b/src/lib/ulm/armv6j/ulmTypes.Mod deleted file mode 100644 index fe2d6eca..00000000 --- a/src/lib/ulm/armv6j/ulmTypes.Mod +++ /dev/null @@ -1,133 +0,0 @@ -(* Ulm's Oberon Library - Copyright (C) 1989-2000 by University of Ulm, SAI, D-89069 Ulm, Germany - ---------------------------------------------------------------------------- - Ulm's Oberon Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either version - 2 of the License, or (at your option) any later version. - - Ulm's Oberon Library is distributed in the hope that it will be - useful, but WITHOUT ANY WARRANTY; without even the implied warranty - of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - ---------------------------------------------------------------------------- - E-mail contact: oberon@mathematik.uni-ulm.de - ---------------------------------------------------------------------------- - $Id: Types.om,v 1.5 2000/12/13 10:03:00 borchert Exp $ - ---------------------------------------------------------------------------- - $Log: Types.om,v $ - Revision 1.5 2000/12/13 10:03:00 borchert - SetInt type used in msb constant - - Revision 1.4 2000/12/13 09:51:57 borchert - constants and types for the relationship of INTEGER and SET added - - Revision 1.3 1998/09/25 15:23:09 borchert - Real32..Real128 added - - Revision 1.2 1994/07/01 11:08:04 borchert - IntAddress, Int8/16/32, ToInt8/16/32 and bit/little endian stuff added - - Revision 1.1 1994/02/22 20:12:14 borchert - Initial revision - - ---------------------------------------------------------------------------- - AFB 9/93 - ---------------------------------------------------------------------------- -*) - -MODULE ulmTypes; - - (* compiler-dependent type definitions; - this version works for Ulm's Oberon Compilers on - following architectures: m68k and sparc - *) - - IMPORT SYS := SYSTEM; - - TYPE - Address* = LONGINT (*SYS.ADDRESS*); - (* ulm compiler can accept - VAR p : SYSTEM.ADDRESS; // SYSTEM.PTR in ETH and V4 versions - ... - p := SYSTEM.ADR(something); - and this is how it is used in ulm oberon system library, - while SYSTEM.ADR returns LONGINT in ETH and V4 versions. - Thus I leave it as LONGINT for now, before coming up with better solution -- noch *) - UntracedAddress* = POINTER[1] TO UntracedAddressDesc; (*SYS.UNTRACEDADDRESS;*) - UntracedAddressDesc* = RECORD[1] END; - Count* = LONGINT; - Size* = Count; - Byte* = SYS.BYTE; - IntAddress* = LONGINT; - Int8* = SHORTINT; - Int16* = INTEGER; - Int32* = LONGINT; - Real32* = REAL; - Real64* = LONGREAL; - - CONST - bigEndian* = 0; (* SPARC, M68K etc *) - littleEndian* = 1; (* Intel 80x86, VAX etc *) - byteorder* = littleEndian; (* machine-dependent constant *) - TYPE - ByteOrder* = SHORTINT; (* bigEndian or littleEndian *) - - (* following constants and type definitions try to make - conversions from INTEGER to SET and vice versa more portable - to allow for bit operations on INTEGER values - *) - TYPE - SetInt* = LONGINT; (* INTEGER type that corresponds to SET *) - VAR msb* : SET; - msbIsMax*, msbIs0*: SHORTINT; - msbindex*, lsbindex*, nofbits*: LONGINT; - - PROCEDURE ToInt8*(int: LONGINT) : Int8; - BEGIN - RETURN SHORT(SHORT(int)) - END ToInt8; - - PROCEDURE ToInt16*(int: LONGINT) : Int16; - BEGIN - RETURN SYS.VAL(Int16, int) - END ToInt16; - - PROCEDURE ToInt32*(int: LONGINT) : Int32; - BEGIN - RETURN int - END ToInt32; - - PROCEDURE ToReal32*(real: LONGREAL) : Real32; - BEGIN - RETURN SHORT(real) - END ToReal32; - - PROCEDURE ToReal64*(real: LONGREAL) : Real64; - BEGIN - RETURN real - END ToReal64; - -BEGIN - msb := SYS.VAL(SET, MIN(SetInt)); - (* most significant bit, converted to a SET *) - (* we expect msbIsMax XOR msbIs0 to be 1; - this is checked for by an assertion - *) - msbIsMax := SYS.VAL(SHORTINT, (msb = {MAX(SET)})); - (* is 1, if msb equals {MAX(SET)} *) - msbIs0 := SYS.VAL(SHORTINT, (msb = {0})); - (* is 0, if msb equals {0} *) - msbindex := msbIsMax * MAX(SET); - (* set element that corresponds to the most-significant-bit *) - lsbindex := MAX(SET) - msbindex; - (* set element that corresponds to the lowest-significant-bit *) - nofbits := MAX(SET) + 1; - (* number of elements in SETs *) - - ASSERT((msbIs0 = 1) & (msbIsMax = 0) OR (msbIs0 = 0) & (msbIsMax = 1)); -END ulmTypes. diff --git a/src/lib/ulm/ulmSYSTEM.Mod b/src/lib/ulm/armv6j_hardfp/ulmSYSTEM.Mod similarity index 100% rename from src/lib/ulm/ulmSYSTEM.Mod rename to src/lib/ulm/armv6j_hardfp/ulmSYSTEM.Mod diff --git a/src/lib/ulm/armv7a_hardfp/ulmSysConversions.Mod b/src/lib/ulm/armv7a_hardfp/ulmSysConversions.Mod deleted file mode 100644 index f8ea3fbb..00000000 --- a/src/lib/ulm/armv7a_hardfp/ulmSysConversions.Mod +++ /dev/null @@ -1,574 +0,0 @@ -(* Ulm's Oberon Library - Copyright (C) 1989-1994 by University of Ulm, SAI, D-89069 Ulm, Germany - ---------------------------------------------------------------------------- - Ulm's Oberon Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either version - 2 of the License, or (at your option) any later version. - - Ulm's Oberon Library is distributed in the hope that it will be - useful, but WITHOUT ANY WARRANTY; without even the implied warranty - of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - ---------------------------------------------------------------------------- - E-mail contact: oberon@mathematik.uni-ulm.de - ---------------------------------------------------------------------------- - $Id: SysConversi.om,v 1.2 1997/07/30 09:38:16 borchert Exp $ - ---------------------------------------------------------------------------- - $Log: SysConversi.om,v $ - Revision 1.2 1997/07/30 09:38:16 borchert - bug in ReadConv fixed: cv.flags was used but not set for - counts > 1 - - Revision 1.1 1994/02/23 07:58:28 borchert - Initial revision - - ---------------------------------------------------------------------------- - AFB 8/90 - adapted to linux cae 02/01 - ---------------------------------------------------------------------------- -*) - -MODULE ulmSysConversions; - - (* convert Oberon records to/from C structures *) - - IMPORT Events := ulmEvents, Objects := ulmObjects, Priorities := ulmPriorities, Streams := ulmStreams, Strings := ulmStrings, - SYS := SYSTEM, SysTypes := ulmSysTypes, Texts := ulmTexts; - - TYPE - Address* = SysTypes.Address; - Size* = Address; - - (* format: - - Format = Conversion { "/" Conversion } . - Conversion = [ Factors ] ConvChars [ Comment ] . - Factors = Array | Factor | Array Factor | Factor Array . - Array = Integer ":" . - Factor = Integer "*" . - ConvChars = OberonType CType | Skip CType | OberonType Skip . - OberonType = "a" | "b" | "c" | "s" | "i" | "l" | "S" . - CType = "a" | "c" | "s" | "i" | "l" . - Integer = Digit { Digit } . - Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" . - Skip = "-" . - Comment = "=" { AnyChar } . - AnyChar = (* all characters except "/" *) . - - Oberon data types: - - a: Address - b: SYS.BYTE - B: BOOLEAN - c: CHAR - s: SHORTINT - i: INTEGER - l: LONGINT - S: SET - - C data types: - - a: char * - c: /* signed */ char - C: unsigned char - s: short int - S: unsigned short int - i: int - I: unsigned int - u: unsigned int - l: long int - L: unsigned long int - - example: - - conversion from - - Rec = - RECORD - a, b: INTEGER; - c: CHAR; - s: SET; - f: ARRAY 3 OF INTEGER; - END; - - to - - struct rec { - short a, b; - char c; - int xx; /* to be skipped on conversion */ - int s; - int f[3]; - }; - - or vice versa: - - "2*is=a,b/cc=c/-i=xx/Si=s/3:ii=f" - - The comments allow to give the field names. - *) - - CONST - (* conversion flags *) - unsigned = 0; (* suppress sign extension *) - boolean = 1; (* convert anything # 0 to 1 *) - TYPE - Flags = SET; - Event* = POINTER TO EventRec; - EventRec* = - RECORD - (Events.EventRec) - format*: Events.Message; - END; - ConvStream = POINTER TO ConvStreamRec; - ConvStreamRec = - RECORD - fmt: Texts.Text; - char: CHAR; - eof: BOOLEAN; - (* 1: Oberon type - 2: C type - *) - type1, type2: CHAR; length: INTEGER; left: INTEGER; - offset1, offset2: Address; - size1, size2: Address; elementsleft: INTEGER; flags: Flags; - END; - - Format* = POINTER TO FormatRec; - FormatRec* = - RECORD - (Objects.ObjectRec) - offset1, offset2: Address; - size1, size2: Address; - flags: Flags; - next: Format; - END; - VAR - badformat*: Events.EventType; - - PROCEDURE Error(cv: ConvStream; msg: ARRAY OF CHAR); - VAR - event: Event; - BEGIN - NEW(event); - event.type := badformat; - event.message := "SysConversions: "; - Strings.Concatenate(event.message, msg); - Strings.Read(event.format, cv.fmt); - Events.Raise(event); - cv.eof := TRUE; - cv.char := 0X; - cv.left := 0; - cv.elementsleft := 0; - END Error; - - PROCEDURE SizeError(msg, format: ARRAY OF CHAR); - VAR - event: Event; - BEGIN - NEW(event); - event.type := badformat; - event.message := "SysConversions: "; - Strings.Concatenate(event.message, msg); - COPY(format, event.format); - Events.Raise(event); - END SizeError; - - PROCEDURE NextCh(cv: ConvStream); - BEGIN - cv.eof := cv.eof OR ~Streams.ReadByte(cv.fmt, cv.char) OR (cv.char = 0X); - IF cv.eof THEN - cv.char := 0X; - END; - END NextCh; - - PROCEDURE IsDigit(ch: CHAR) : BOOLEAN; - BEGIN - RETURN (ch >= "0") & (ch <= "9") - END IsDigit; - - PROCEDURE ReadInt(cv: ConvStream; VAR i: INTEGER); - BEGIN - i := 0; - REPEAT - i := 10 * i + ORD(cv.char) - ORD("0"); - NextCh(cv); - UNTIL ~IsDigit(cv.char); - END ReadInt; - - PROCEDURE Open(VAR cv: ConvStream; format: ARRAY OF CHAR); - BEGIN - NEW(cv); - Texts.Open(SYS.VAL(Streams.Stream, cv.fmt)); - Strings.Write(cv.fmt, format); - cv.left := 0; cv.elementsleft := 0; - cv.offset1 := 0; cv.offset2 := 0; - cv.eof := FALSE; - NextCh(cv); - END Open; - - PROCEDURE Close(VAR cv: ConvStream); - BEGIN - IF ~Streams.Close(cv.fmt) THEN END; - END Close; - - PROCEDURE ScanConv(cv: ConvStream; - VAR type1, type2: CHAR; - VAR length: INTEGER) : BOOLEAN; - VAR - i: INTEGER; - factor: INTEGER; - BEGIN - IF cv.left > 0 THEN - type1 := cv.type1; - type2 := cv.type2; - length := cv.length; - DEC(cv.left); - RETURN TRUE - END; - IF cv.char = "/" THEN - NextCh(cv); - END; - IF cv.eof THEN - RETURN FALSE - END; - factor := 0; length := 0; - WHILE IsDigit(cv.char) DO - ReadInt(cv, i); - IF i <= 0 THEN - Error(cv, "integer must be positive"); RETURN FALSE - END; - IF cv.char = ":" THEN - IF length # 0 THEN - Error(cv, "multiple length specification"); RETURN FALSE - END; - length := i; - NextCh(cv); - ELSIF cv.char = "*" THEN - IF factor # 0 THEN - Error(cv, "multiple factor specification"); RETURN FALSE - END; - factor := i; cv.left := factor - 1; - NextCh(cv); - ELSE - Error(cv, "factor or length expected"); RETURN FALSE - END; - END; - type1 := cv.char; NextCh(cv); - type2 := cv.char; NextCh(cv); - IF cv.left > 0 THEN - cv.type1 := type1; cv.type2 := type2; cv.length := length; - END; - IF cv.char = "=" THEN (* comment *) - REPEAT - NextCh(cv); - UNTIL cv.eof OR (cv.char = "/"); - END; - RETURN TRUE - END ScanConv; - - PROCEDURE Align(VAR offset: Address; boundary: Address); - BEGIN - IF SYS.VAL (INTEGER, offset) MOD SYS.VAL (INTEGER, boundary) # 0 THEN - offset := SYS.VAL (INTEGER, offset) + (SYS.VAL (INTEGER, boundary) - SYS.VAL (INTEGER, offset) MOD SYS.VAL (INTEGER, boundary)); - END; - END Align; - - PROCEDURE ReadConv(cv: ConvStream; - VAR offset1, offset2: Address; - VAR size1, size2: Address; - VAR flags: Flags) : BOOLEAN; - VAR - type1, type2: CHAR; - length: INTEGER; - align: BOOLEAN; - boundary: INTEGER; - BEGIN - IF cv.elementsleft > 0 THEN - DEC(cv.elementsleft); - - (* Oberon type *) - IF size1 > SIZE(SYS.BYTE) THEN - Align(cv.offset1, SIZE(INTEGER)); - END; - offset1 := cv.offset1; cv.offset1 := SYS.VAL (INTEGER, cv.offset1) + size1; - size1 := cv.size1; size2 := cv.size2; flags := cv.flags; - IF (size1 > 0) & (cv.elementsleft = 0) THEN - Align(cv.offset1, SIZE(INTEGER)); - END; - - (* C type *) - IF size2 > 1 THEN - Align(cv.offset2, 2); - END; - offset2 := cv.offset2; cv.offset2 := SYS.VAL (INTEGER, cv.offset2) + SYS.VAL (INTEGER, size2); - - RETURN TRUE - END; - IF ScanConv(cv, type1, type2, length) THEN - flags := {}; - (* Oberon type *) - CASE type1 OF - | "a": size1 := SIZE(Address); INCL(flags, unsigned); - | "b": size1 := SIZE(SYS.BYTE); INCL(flags, unsigned); - | "B": size1 := SIZE(BOOLEAN); INCL(flags, boolean); - | "c": size1 := SIZE(CHAR); INCL(flags, unsigned); - | "s": size1 := SIZE(SHORTINT); - | "i": size1 := SIZE(INTEGER); - | "l": size1 := SIZE(LONGINT); - | "S": size1 := SIZE(SET); INCL(flags, unsigned); - | "-": size1 := 0; - ELSE Error(cv, "bad Oberon type specifier"); RETURN FALSE - END; - IF size1 > 0 THEN - IF length > 0 THEN - Align(cv.offset1, SIZE(INTEGER)); - ELSIF size1 > SIZE(SYS.BYTE) THEN - Align(cv.offset1, SIZE(INTEGER)); - END; - END; - offset1 := cv.offset1; cv.offset1 := SYS.VAL (INTEGER, cv.offset1) + size1; - - (* C type *) - CASE type2 OF - | "a": size2 := 4; INCL(flags, unsigned); (* char* *) - | "c": size2 := 1; (* /* signed */ char *) - | "C": size2 := 1; INCL(flags, unsigned); (* unsigned char *) - | "s": size2 := 2; (* short int *) - | "S": size2 := 2; INCL(flags, unsigned); (* unsigned short int *) - | "i": size2 := 4; (* int *) - | "I": size2 := 4; INCL(flags, unsigned); (* unsigned int *) - | "u": size2 := 4; INCL(flags, unsigned); (* unsigned int *) - | "l": size2 := 4; (* long int *) - | "L": size2 := 4; INCL(flags, unsigned); (* long int *) - | "-": size2 := 0; - ELSE Error(cv, "bad C type specifier"); RETURN FALSE - END; - IF size2 > 1 THEN - Align(cv.offset2, size2); - END; - offset2 := cv.offset2; cv.offset2 := SYS.VAL (INTEGER, cv.offset2) + SYS.VAL (INTEGER, size2); - - cv.size1 := size1; cv.size2 := size2; - IF length > 0 THEN - cv.elementsleft := length - 1; - cv.flags := flags; - END; - RETURN TRUE - ELSE - RETURN FALSE - END; - END ReadConv; - - PROCEDURE Convert(from, to: Address; ssize, dsize: Address; flags: Flags); - TYPE - Bytes = ARRAY 8 OF CHAR; - Pointer = POINTER TO Bytes; - VAR - dest, source: Pointer; - dindex, sindex: INTEGER; - nonzero: BOOLEAN; - fill : CHAR; - BEGIN - IF ssize > 0 THEN - dest := SYS.VAL(Pointer, to); - source := SYS.VAL(Pointer, from); - dindex := 0; sindex := 0; - IF boolean IN flags THEN - nonzero := FALSE; - WHILE ssize > 0 DO - nonzero := nonzero OR (source[sindex] # 0X); - INC(sindex); ssize := SYS.VAL (INTEGER, ssize) - 1; - END; - IF dsize > 0 THEN - IF nonzero THEN - dest[dindex] := 1X; - ELSE - dest[dindex] := 0X; - END; - dsize := dsize - 1; INC (dindex); - END; - WHILE dsize > 0 DO - dest[dindex] := 0X; - dsize := SYS.VAL (INTEGER, dsize) - 1; INC(dindex); - END; - ELSE - WHILE (dsize > 0) & (ssize > 0) DO - dest[dindex] := source[sindex]; - ssize := SYS.VAL (INTEGER, ssize) - 1; - dsize := dsize - 1; - INC(dindex); INC(sindex); - END; - IF dsize > 0 THEN - (* sindex has been incremented at least once because - * ssize and dsize were greater than 0, i.e. sindex-1 - * is a valid inex. *) - fill := 0X; - IF ~(unsigned IN flags) & (source[sindex-1] >= 080X) THEN - fill := 0FFX; - END; - END; - WHILE dsize > 0 DO - dest[dindex] := fill; - dsize := SYS.VAL (INTEGER, dsize) - 1; INC(dindex); - END; - END; - END; - END Convert; - - PROCEDURE ByAddrToC*(from, to: Address; format: ARRAY OF CHAR); - VAR - cv: ConvStream; - offset1, offset2, size1, size2: Address; - flags: Flags; - BEGIN - Open(cv, format); - WHILE ReadConv(cv, offset1, offset2, size1, size2, flags) DO - Convert(from + offset1, to + offset2, size1, size2, flags); - END; - Close(cv); - END ByAddrToC; - - PROCEDURE ByAddrFromC*(from, to: Address; format: ARRAY OF CHAR); - VAR - cv: ConvStream; - offset1, offset2, size1, size2: Address; - flags: Flags; - BEGIN - Open(cv, format); - WHILE ReadConv(cv, offset1, offset2, size1, size2, flags) DO - Convert(from + offset2, to + offset1, size2, size1, flags); - END; - Close(cv); - END ByAddrFromC; - - PROCEDURE CSize*(format: ARRAY OF CHAR) : Size; - (* returns the size of the C-structure described by `format' *) - VAR - cv: ConvStream; - offset1, offset2, size1, size2: Address; - size: Address; - flags: Flags; - BEGIN - Open(cv, format); - WHILE ReadConv(cv, offset1, offset2, size1, size2, flags) DO END; - Close(cv); - size := offset2 + size2; - Align(size, 2); - RETURN size - END CSize; - - PROCEDURE OberonSize*(format: ARRAY OF CHAR) : Size; - (* returns the size of the Oberon-structure described by `format' *) - VAR - cv: ConvStream; - offset1, offset2, size1, size2: Address; - size: Address; - flags: Flags; - BEGIN - Open(cv, format); - WHILE ReadConv(cv, offset1, offset2, size1, size2, flags) DO END; - Close(cv); - size := offset1 + size1; - Align(size, SIZE(INTEGER)); - RETURN size - END OberonSize; - - PROCEDURE ToC*(VAR from, to: ARRAY OF SYS.BYTE; format: ARRAY OF CHAR); - BEGIN - IF OberonSize(format) > LEN(from) THEN - SizeError("Oberon record is too small", format); RETURN - END; - IF CSize(format) > LEN(to) THEN - SizeError("C structure is too small", format); RETURN - END; - ByAddrToC(SYS.ADR(from), SYS.ADR(to), format); - END ToC; - - PROCEDURE FromC*(VAR from, to: ARRAY OF SYS.BYTE; format: ARRAY OF CHAR); - BEGIN - IF OberonSize(format) > LEN(to) THEN - SizeError("Oberon record is too small", format); RETURN - END; - IF CSize(format) > LEN(from) THEN - SizeError("C structure is too small", format); RETURN - END; - ByAddrFromC(SYS.ADR(from), SYS.ADR(to), format); - END FromC; - - PROCEDURE Compile*(VAR fmt: Format; format: ARRAY OF CHAR); - (* translate format into an internal representation - which is later referenced by fmt; - ByFmtToC and ByFmtFromC are faster than ToC and FromC - *) - VAR - cv: ConvStream; - offset1, offset2, size1, size2: Address; - flags: Flags; - element: Format; - head, tail: Format; - BEGIN - Open(cv, format); - head := NIL; tail := NIL; - WHILE ReadConv(cv, offset1, offset2, size1, size2, flags) DO - NEW(element); - element.offset1 := offset1; - element.offset2 := offset2; - element.size1 := size1; - element.size2 := size2; - element.flags := flags; - element.next := NIL; - IF tail # NIL THEN - tail.next := element; - ELSE - head := element; - END; - tail := element; - END; - fmt := head; - Close(cv); - END Compile; - - PROCEDURE ByFmtAndAddrToC*(from, to: Address; format: Format); - VAR - offset1, offset2, size1, size2: Address; - flags: Flags; - BEGIN - WHILE format # NIL DO - Convert(from + format.offset1, to + format.offset2, - format.size1, format.size2, format.flags); - format := format.next; - END; - END ByFmtAndAddrToC; - - PROCEDURE ByFmtAndAddrFromC*(from, to: Address; format: Format); - VAR - offset1, offset2, size1, size2: Address; - flags: Flags; - BEGIN - WHILE format # NIL DO - Convert(from + format.offset2, to + format.offset1, - format.size2, format.size1, format.flags); - format := format.next; - END; - END ByFmtAndAddrFromC; - - PROCEDURE ByFmtToC*(VAR from, to: ARRAY OF SYS.BYTE; format: Format); - BEGIN - ByFmtAndAddrToC(SYS.ADR(from), SYS.ADR(to), format); - END ByFmtToC; - - PROCEDURE ByFmtFromC*(VAR from, to: ARRAY OF SYS.BYTE; format: Format); - BEGIN - ByFmtAndAddrFromC(SYS.ADR(from), SYS.ADR(to), format); - END ByFmtFromC; - -BEGIN - Events.Define(badformat); - Events.SetPriority(badformat, Priorities.liberrors); -END ulmSysConversions. diff --git a/src/lib/ulm/armv7a_hardfp/ulmSysStat.Mod b/src/lib/ulm/armv7a_hardfp/ulmSysStat.Mod deleted file mode 100644 index c7f00f04..00000000 --- a/src/lib/ulm/armv7a_hardfp/ulmSysStat.Mod +++ /dev/null @@ -1,201 +0,0 @@ -(* Ulm's Oberon Library - Copyright (C) 1989-1994 by University of Ulm, SAI, D-89069 Ulm, Germany - ---------------------------------------------------------------------------- - Ulm's Oberon Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either version - 2 of the License, or (at your option) any later version. - - Ulm's Oberon Library is distributed in the hope that it will be - useful, but WITHOUT ANY WARRANTY; without even the implied warranty - of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - ---------------------------------------------------------------------------- - E-mail contact: oberon@mathematik.uni-ulm.de - ---------------------------------------------------------------------------- - $Id: SysStat.om,v 1.3 2000/11/12 13:02:09 borchert Exp $ - ---------------------------------------------------------------------------- - $Log: SysStat.om,v $ - Revision 1.3 2000/11/12 13:02:09 borchert - door file type added - - Revision 1.2 2000/11/12 12:48:07 borchert - - conversion adapted to Solaris 2.x - - Lstat added - - Revision 1.1 1994/02/23 08:00:48 borchert - Initial revision - - ---------------------------------------------------------------------------- - AFB 9/89 - ---------------------------------------------------------------------------- -*) - -MODULE ulmSysStat; - - (* examine inode: stat(2) and fstat(2) *) - - IMPORT RelatedEvents := ulmRelatedEvents, Sys := ulmSys, SYS := SYSTEM, uSYS := ulmSYSTEM, SysConversions := ulmSysConversions, SysErrors := ulmSysErrors, - SysTypes := ulmSysTypes; - - CONST - (* file mode: - bit 0 = 1<<0 bit 31 = 1<<31 - - user group other - 3 1 1111 11 - 1 ... 6 5432 109 876 543 210 - +--------+------+-----+-----+-----+-----+ - | unused | type | sst | rwx | rwx | rwx | - +--------+------+-----+-----+-----+-----+ - *) - - type* = {12..15}; - prot* = {0..8}; - - (* file types; example: (stat.mode * type = dir) *) - reg* = {15}; (* regular *) - dir* = {14}; (* directory *) - chr* = {13}; (* character special *) - fifo* = {12}; (* fifo *) - blk* = {13..14}; (* block special *) - symlink* = {13, 15}; (* symbolic link *) - socket* = {14, 15}; (* socket *) - - (* special *) - setuid* = 11; (* set user id on execution *) - setgid* = 10; (* set group id on execution *) - savetext* = 9; (* save swapped text even after use *) - - (* protection *) - uread* = 8; (* read permission owner *) - uwrite* = 7; (* write permission owner *) - uexec* = 6; (* execute/search permission owner *) - gread* = 5; (* read permission group *) - gwrite* = 4; (* write permission group *) - gexec* = 3; (* execute/search permission group *) - oread* = 2; (* read permission other *) - owrite* = 1; (* write permission other *) - oexec* = 0; (* execute/search permission other *) - - (* example for "r-xr-x---": (read + exec) * (owner + group) *) - owner* = {uread, uwrite, uexec}; - group* = {gread, gwrite, gexec}; - other* = {oread, owrite, oexec}; - read* = {uread, gread, oread}; - write* = {uwrite, gwrite, owrite}; - exec* = {uexec, gexec, oexec}; - rwx* = prot; - - TYPE - StatRec* = (* result of stat(2) and fstat(2) *) - RECORD - device*: SysTypes.Device; (* ID of device containing - a directory entry for this file *) - inode*: SysTypes.Inode; (* inode number *) - mode*: SET; (* file mode; see mknod(2) *) - nlinks*: LONGINT; (* number of links *) - uid*: LONGINT; (* user id of the file's owner *) - gid*: LONGINT; (* group id of the file's group *) - rdev*: SysTypes.Device; (* ID of device - this entry is defined only for - character special or block - special files - *) - size*: SysTypes.Offset; (* file size in bytes *) - blksize*: LONGINT; (* preferred blocksize *) - blocks*: LONGINT; (* # of blocks allocated *) - atime*: SysTypes.Time; (* time of last access *) - mtime*: SysTypes.Time; (* time of last data modification *) - ctime*: SysTypes.Time; (* time of last file status change *) - END; - -(* Linux kernel struct stat (2.2.17) - struct stat { - unsigned short st_dev; - unsigned short __pad1; - unsigned long st_ino; - unsigned short st_mode; - unsigned short st_nlink; - unsigned short st_uid; - unsigned short st_gid; - unsigned short st_rdev; - unsigned short __pad2; - unsigned long st_size; - unsigned long st_blksize; - unsigned long st_blocks; - unsigned long st_atime; - unsigned long __unused1; - unsigned long st_mtime; - unsigned long __unused2; - unsigned long st_ctime; - unsigned long __unused3; - unsigned long __unused4; - unsigned long __unused5; - }; -*) - - CONST - statbufsize = 88(*64*); (* see *) (* sizeof struct stat gives us 144 on x86_64 and 88 on x86 *) - TYPE - UnixStatRec = ARRAY statbufsize OF SYS.BYTE; - CONST - statbufconv = - (*"is=dev/-s=pad1/ll=ino/Ss=mode/4*is=nlink+uid+gid+rdev/-s=pad2/ll=size/2*ll=blksize,blocks/il=atime/-l/il=mtime/-l/il=ctime/3*-l";*) - (*"ls=dev/-s=pad1/lL=ino/Ss=mode/4*is=nlink+uid+gid+rdev/-s=pad2/lL=size/2*lL=blksize,blocks/lL=atime/-l/lL=mtime/-l/lL=ctime/3*-l";*) - "ll=dev/-l=devx/-s=pad1/ll=ino/Sl=mode/ll=nlink/ll=uid/ll=gid/ll=rdev/-l=rdevx/-s=pad2/ll=size/2*ll=blksize,blocks/lL=atime/-l/lL=mtime/-l/lL=ctime/3*-l"; - VAR - statbuffmt: SysConversions.Format; - - PROCEDURE Stat*(path: ARRAY OF CHAR; VAR buf: StatRec; - errors: RelatedEvents.Object) : BOOLEAN; - VAR - d0, d1, d2: LONGINT; - origbuf: UnixStatRec; - BEGIN - IF uSYS.UNIXCALL(Sys.newstat, d0, d1, SYS.ADR(path), SYS.ADR(origbuf), d2) THEN - SysConversions.ByFmtFromC(origbuf, buf, statbuffmt); - RETURN TRUE - ELSE - SysErrors.Raise(errors, d0, Sys.newstat, path); - RETURN FALSE - END; - END Stat; -(* - PROCEDURE Lstat*(path: ARRAY OF CHAR; VAR buf: StatRec; - errors: RelatedEvents.Object) : BOOLEAN; - VAR - d0, d1: INTEGER; - origbuf: UnixStatRec; - BEGIN - IF SYS.UNIXCALL(Sys.newlstat, d0, d1, SYS.ADR(path), SYS.ADR(origbuf)) THEN - SysConversions.ByFmtFromC(origbuf, buf, statbuffmt); - RETURN TRUE - ELSE - SysErrors.Raise(errors, d0, Sys.newlstat, path); - RETURN FALSE - END; - END Lstat; -*) - PROCEDURE Fstat*(fd: SysTypes.File; VAR buf: StatRec; - errors: RelatedEvents.Object) : BOOLEAN; - VAR - d0, d1, d2: LONGINT; - origbuf: UnixStatRec; - BEGIN - IF uSYS.UNIXCALL(Sys.newfstat, d0, d1, fd, SYS.ADR(origbuf), d2) THEN - SysConversions.ByFmtFromC(origbuf, buf, statbuffmt); - RETURN TRUE - ELSE - SysErrors.Raise(errors, d0, Sys.newfstat, ""); - RETURN FALSE - END; - END Fstat; - -BEGIN - SysConversions.Compile(statbuffmt, statbufconv); -END ulmSysStat. diff --git a/src/lib/ulm/armv7a_hardfp/ulmSysTypes.Mod b/src/lib/ulm/armv7a_hardfp/ulmSysTypes.Mod deleted file mode 100644 index 174140e7..00000000 --- a/src/lib/ulm/armv7a_hardfp/ulmSysTypes.Mod +++ /dev/null @@ -1,70 +0,0 @@ -(* Ulm's Oberon Library - Copyright (C) 1989-1994 by University of Ulm, SAI, D-89069 Ulm, Germany - ---------------------------------------------------------------------------- - Ulm's Oberon Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either version - 2 of the License, or (at your option) any later version. - - Ulm's Oberon Library is distributed in the hope that it will be - useful, but WITHOUT ANY WARRANTY; without even the implied warranty - of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - ---------------------------------------------------------------------------- - E-mail contact: oberon@mathematik.uni-ulm.de - ---------------------------------------------------------------------------- - $Id: SysTypes.om,v 1.1 1994/02/23 08:01:38 borchert Exp $ - ---------------------------------------------------------------------------- - $Log: SysTypes.om,v $ - Revision 1.1 1994/02/23 08:01:38 borchert - Initial revision - - ---------------------------------------------------------------------------- - AFB 9/89 - ---------------------------------------------------------------------------- -*) - -MODULE ulmSysTypes; - - IMPORT Types := ulmTypes; - - TYPE - Address* = Types.Address; - UntracedAddress* = Types.UntracedAddress; - Count* = Types.Count; - Size* = Types.Size; - Byte* = Types.Byte; - - File* = (*INTEGER*)LONGINT; (* in ulm's system both INTEGER and LONGINT are 4 bytes long *) - Offset* = LONGINT; - Device* = LONGINT; - Inode* = LONGINT; - Time* = LONGINT; - - Word* = INTEGER; (* must have the size of C's int-type *) - - (* Note: linux supports wait4 but not waitid, i.e. these - * constants aren't needed. *) - (* - CONST - (* possible values of the idtype parameter (4 bytes), - see - *) - idPid = 0; (* a process identifier *) - idPpid = 1; (* a parent process identifier *) - idPgid = 2; (* a process group (job control group) identifier *) - idSid = 3; (* a session identifier *) - idCid = 4; (* a scheduling class identifier *) - idUid = 5; (* a user identifier *) - idGid = 6; (* a group identifier *) - idAll = 7; (* all processes *) - idLwpid = 8; (* an LWP identifier *) - TYPE - IdType = INTEGER; (* idPid .. idLwpid *) - *) - -END ulmSysTypes. diff --git a/src/lib/ulm/armv7a_hardfp/ulmTypes.Mod b/src/lib/ulm/armv7a_hardfp/ulmTypes.Mod deleted file mode 100644 index fe2d6eca..00000000 --- a/src/lib/ulm/armv7a_hardfp/ulmTypes.Mod +++ /dev/null @@ -1,133 +0,0 @@ -(* Ulm's Oberon Library - Copyright (C) 1989-2000 by University of Ulm, SAI, D-89069 Ulm, Germany - ---------------------------------------------------------------------------- - Ulm's Oberon Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either version - 2 of the License, or (at your option) any later version. - - Ulm's Oberon Library is distributed in the hope that it will be - useful, but WITHOUT ANY WARRANTY; without even the implied warranty - of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - ---------------------------------------------------------------------------- - E-mail contact: oberon@mathematik.uni-ulm.de - ---------------------------------------------------------------------------- - $Id: Types.om,v 1.5 2000/12/13 10:03:00 borchert Exp $ - ---------------------------------------------------------------------------- - $Log: Types.om,v $ - Revision 1.5 2000/12/13 10:03:00 borchert - SetInt type used in msb constant - - Revision 1.4 2000/12/13 09:51:57 borchert - constants and types for the relationship of INTEGER and SET added - - Revision 1.3 1998/09/25 15:23:09 borchert - Real32..Real128 added - - Revision 1.2 1994/07/01 11:08:04 borchert - IntAddress, Int8/16/32, ToInt8/16/32 and bit/little endian stuff added - - Revision 1.1 1994/02/22 20:12:14 borchert - Initial revision - - ---------------------------------------------------------------------------- - AFB 9/93 - ---------------------------------------------------------------------------- -*) - -MODULE ulmTypes; - - (* compiler-dependent type definitions; - this version works for Ulm's Oberon Compilers on - following architectures: m68k and sparc - *) - - IMPORT SYS := SYSTEM; - - TYPE - Address* = LONGINT (*SYS.ADDRESS*); - (* ulm compiler can accept - VAR p : SYSTEM.ADDRESS; // SYSTEM.PTR in ETH and V4 versions - ... - p := SYSTEM.ADR(something); - and this is how it is used in ulm oberon system library, - while SYSTEM.ADR returns LONGINT in ETH and V4 versions. - Thus I leave it as LONGINT for now, before coming up with better solution -- noch *) - UntracedAddress* = POINTER[1] TO UntracedAddressDesc; (*SYS.UNTRACEDADDRESS;*) - UntracedAddressDesc* = RECORD[1] END; - Count* = LONGINT; - Size* = Count; - Byte* = SYS.BYTE; - IntAddress* = LONGINT; - Int8* = SHORTINT; - Int16* = INTEGER; - Int32* = LONGINT; - Real32* = REAL; - Real64* = LONGREAL; - - CONST - bigEndian* = 0; (* SPARC, M68K etc *) - littleEndian* = 1; (* Intel 80x86, VAX etc *) - byteorder* = littleEndian; (* machine-dependent constant *) - TYPE - ByteOrder* = SHORTINT; (* bigEndian or littleEndian *) - - (* following constants and type definitions try to make - conversions from INTEGER to SET and vice versa more portable - to allow for bit operations on INTEGER values - *) - TYPE - SetInt* = LONGINT; (* INTEGER type that corresponds to SET *) - VAR msb* : SET; - msbIsMax*, msbIs0*: SHORTINT; - msbindex*, lsbindex*, nofbits*: LONGINT; - - PROCEDURE ToInt8*(int: LONGINT) : Int8; - BEGIN - RETURN SHORT(SHORT(int)) - END ToInt8; - - PROCEDURE ToInt16*(int: LONGINT) : Int16; - BEGIN - RETURN SYS.VAL(Int16, int) - END ToInt16; - - PROCEDURE ToInt32*(int: LONGINT) : Int32; - BEGIN - RETURN int - END ToInt32; - - PROCEDURE ToReal32*(real: LONGREAL) : Real32; - BEGIN - RETURN SHORT(real) - END ToReal32; - - PROCEDURE ToReal64*(real: LONGREAL) : Real64; - BEGIN - RETURN real - END ToReal64; - -BEGIN - msb := SYS.VAL(SET, MIN(SetInt)); - (* most significant bit, converted to a SET *) - (* we expect msbIsMax XOR msbIs0 to be 1; - this is checked for by an assertion - *) - msbIsMax := SYS.VAL(SHORTINT, (msb = {MAX(SET)})); - (* is 1, if msb equals {MAX(SET)} *) - msbIs0 := SYS.VAL(SHORTINT, (msb = {0})); - (* is 0, if msb equals {0} *) - msbindex := msbIsMax * MAX(SET); - (* set element that corresponds to the most-significant-bit *) - lsbindex := MAX(SET) - msbindex; - (* set element that corresponds to the lowest-significant-bit *) - nofbits := MAX(SET) + 1; - (* number of elements in SETs *) - - ASSERT((msbIs0 = 1) & (msbIsMax = 0) OR (msbIs0 = 0) & (msbIsMax = 1)); -END ulmTypes. diff --git a/src/lib/ulm/powerpc/ulmSYSTEM.Mod b/src/lib/ulm/powerpc/ulmSYSTEM.Mod new file mode 100644 index 00000000..814c0607 --- /dev/null +++ b/src/lib/ulm/powerpc/ulmSYSTEM.Mod @@ -0,0 +1,137 @@ +MODULE ulmSYSTEM; +IMPORT SYSTEM, Unix, Sys := ulmSys; + +TYPE pchar = POINTER TO ARRAY 1 OF CHAR; + pstring = POINTER TO ARRAY 1024 OF CHAR; + pstatus = POINTER TO Unix.Status; + + TYPE bytearray* = ARRAY SIZE(LONGINT) OF SYSTEM.BYTE; (* need this because voc does not convert implicitly LONGINT to ARRAY OF BYTE; -- noch *) + pbytearray* = POINTER TO bytearray; + TYPE longrealarray* = ARRAY SIZE(LONGREAL) OF SYSTEM.BYTE; (* need this because voc does not convert implicitly LONGINT to ARRAY OF BYTE; -- noch *) + plongrealarray* = POINTER TO bytearray; + + PROCEDURE LongToByteArr* ( l : LONGINT; VAR bar : bytearray); (* noch *) + VAR b : SYSTEM.BYTE; + p : pbytearray; + i : LONGINT; + BEGIN + p := SYSTEM.VAL(pbytearray, SYSTEM.ADR(l)); + FOR i := 0 TO SIZE(LONGINT) -1 DO + b := p^[i]; bar[i] := b; + END + END LongToByteArr; + + PROCEDURE LRealToByteArr* ( l : LONGREAL; VAR lar : longrealarray); (* noch *) + VAR b : SYSTEM.BYTE; + p : plongrealarray; + i : LONGINT; + BEGIN + p := SYSTEM.VAL(plongrealarray, SYSTEM.ADR(l)); + FOR i := 0 TO SIZE(LONGREAL) -1 DO + b := p^[i]; lar[i] := b; + END + END LRealToByteArr; + + +(* + PROCEDURE -Write(adr, n: LONGINT): LONGINT + "write(1/*stdout*/, adr, n)"; + + PROCEDURE -read(VAR ch: CHAR): LONGINT + "read(0/*stdin*/, ch, 1)"; +*) + + PROCEDURE TAS*(VAR flag:BOOLEAN): BOOLEAN; (* added for compatibility with ulmSYSTEM module; noch *) + VAR oldflag : BOOLEAN; + BEGIN + oldflag := flag; + flag := TRUE; + RETURN oldflag; + END TAS; + + PROCEDURE UNIXCALL*(syscall: LONGINT; VAR d0, d1: LONGINT; (* in ulm version both LONGINT and INTEGER are 4 byte size *) + arg1, arg2, arg3: LONGINT) : BOOLEAN; + VAR + n : LONGINT; + ch : CHAR; + pch : pchar; + pstr : pstring; + pst : pstatus; + BEGIN + + IF syscall = Sys.read THEN + d0 := Unix.Read(arg1, arg2, arg3); + IF d0 >= 0 THEN RETURN TRUE ELSE RETURN FALSE END + (*NEW(pch); + pch := SYSTEM.VAL(pchar, arg2); + ch := pch^[0]; + n := read(ch); + IF n # 1 THEN + ch := 0X; + RETURN FALSE + ELSE + pch^[0] := ch; + RETURN TRUE + END; + *) + ELSIF syscall = Sys.write THEN + d0 := Unix.Write(arg1, arg2, arg3); + IF d0 >= 0 THEN RETURN TRUE ELSE RETURN FALSE END + (*NEW(pch); + pch := SYSTEM.VAL(pchar, arg2); + n := Write(SYSTEM.VAL(LONGINT, pch), 1); + IF n # 1 THEN RETURN FALSE ELSE RETURN TRUE END + *) + ELSIF syscall = Sys.open THEN + pstr := SYSTEM.VAL(pstring, arg1); + d0 := Unix.Open(pstr^, SYSTEM.VAL(SET, arg3), SYSTEM.VAL(SET, arg2)); + IF d0 >= 0 THEN RETURN TRUE ELSE RETURN FALSE END + ELSIF syscall = Sys.close THEN + d0 := Unix.Close(arg1); + IF d0 = 0 THEN RETURN TRUE ELSE RETURN FALSE END + ELSIF syscall = Sys.lseek THEN + d0 := Unix.Lseek(arg1, arg2, arg3); + IF d0 >= 0 THEN RETURN TRUE ELSE RETURN FALSE END + ELSIF syscall = Sys.ioctl THEN + d0 := Unix.Ioctl(arg1, arg2, arg3); + RETURN d0 >= 0; + ELSIF syscall = Sys.fcntl THEN + d0 := Unix.Fcntl (arg1, arg2, arg3); + RETURN d0 >= 0; + ELSIF syscall = Sys.dup THEN + d0 := Unix.Dup(arg1); + RETURN d0 > 0; + ELSIF syscall = Sys.pipe THEN + d0 := Unix.Pipe(arg1); + RETURN d0 >= 0; + ELSIF syscall = Sys.newstat THEN + pst := SYSTEM.VAL(pstatus, arg2); + pstr := SYSTEM.VAL(pstring, arg1); + d0 := Unix.Stat(pstr^, pst^); + RETURN d0 >= 0 + ELSIF syscall = Sys.newfstat THEN + pst := SYSTEM.VAL(pstatus, arg2); + d0 := Unix.Fstat(arg1, pst^); + RETURN d0 >= 0; + END + + END UNIXCALL; + + + PROCEDURE UNIXFORK(VAR pid: LONGINT) : BOOLEAN; + BEGIN + + END UNIXFORK; + + PROCEDURE UNIXSIGNAL(signo: INTEGER; p: PROCEDURE; + VAR old: PROCEDURE; VAR error: INTEGER) : BOOLEAN; + BEGIN + + END UNIXSIGNAL; + + PROCEDURE WMOVE*(from, to, n : LONGINT); + VAR l : LONGINT; + BEGIN + SYSTEM.MOVE(from, to, n); + END WMOVE; +END ulmSYSTEM. diff --git a/src/lib/ulm/x86/ulmSYSTEM.Mod b/src/lib/ulm/x86/ulmSYSTEM.Mod new file mode 100644 index 00000000..814c0607 --- /dev/null +++ b/src/lib/ulm/x86/ulmSYSTEM.Mod @@ -0,0 +1,137 @@ +MODULE ulmSYSTEM; +IMPORT SYSTEM, Unix, Sys := ulmSys; + +TYPE pchar = POINTER TO ARRAY 1 OF CHAR; + pstring = POINTER TO ARRAY 1024 OF CHAR; + pstatus = POINTER TO Unix.Status; + + TYPE bytearray* = ARRAY SIZE(LONGINT) OF SYSTEM.BYTE; (* need this because voc does not convert implicitly LONGINT to ARRAY OF BYTE; -- noch *) + pbytearray* = POINTER TO bytearray; + TYPE longrealarray* = ARRAY SIZE(LONGREAL) OF SYSTEM.BYTE; (* need this because voc does not convert implicitly LONGINT to ARRAY OF BYTE; -- noch *) + plongrealarray* = POINTER TO bytearray; + + PROCEDURE LongToByteArr* ( l : LONGINT; VAR bar : bytearray); (* noch *) + VAR b : SYSTEM.BYTE; + p : pbytearray; + i : LONGINT; + BEGIN + p := SYSTEM.VAL(pbytearray, SYSTEM.ADR(l)); + FOR i := 0 TO SIZE(LONGINT) -1 DO + b := p^[i]; bar[i] := b; + END + END LongToByteArr; + + PROCEDURE LRealToByteArr* ( l : LONGREAL; VAR lar : longrealarray); (* noch *) + VAR b : SYSTEM.BYTE; + p : plongrealarray; + i : LONGINT; + BEGIN + p := SYSTEM.VAL(plongrealarray, SYSTEM.ADR(l)); + FOR i := 0 TO SIZE(LONGREAL) -1 DO + b := p^[i]; lar[i] := b; + END + END LRealToByteArr; + + +(* + PROCEDURE -Write(adr, n: LONGINT): LONGINT + "write(1/*stdout*/, adr, n)"; + + PROCEDURE -read(VAR ch: CHAR): LONGINT + "read(0/*stdin*/, ch, 1)"; +*) + + PROCEDURE TAS*(VAR flag:BOOLEAN): BOOLEAN; (* added for compatibility with ulmSYSTEM module; noch *) + VAR oldflag : BOOLEAN; + BEGIN + oldflag := flag; + flag := TRUE; + RETURN oldflag; + END TAS; + + PROCEDURE UNIXCALL*(syscall: LONGINT; VAR d0, d1: LONGINT; (* in ulm version both LONGINT and INTEGER are 4 byte size *) + arg1, arg2, arg3: LONGINT) : BOOLEAN; + VAR + n : LONGINT; + ch : CHAR; + pch : pchar; + pstr : pstring; + pst : pstatus; + BEGIN + + IF syscall = Sys.read THEN + d0 := Unix.Read(arg1, arg2, arg3); + IF d0 >= 0 THEN RETURN TRUE ELSE RETURN FALSE END + (*NEW(pch); + pch := SYSTEM.VAL(pchar, arg2); + ch := pch^[0]; + n := read(ch); + IF n # 1 THEN + ch := 0X; + RETURN FALSE + ELSE + pch^[0] := ch; + RETURN TRUE + END; + *) + ELSIF syscall = Sys.write THEN + d0 := Unix.Write(arg1, arg2, arg3); + IF d0 >= 0 THEN RETURN TRUE ELSE RETURN FALSE END + (*NEW(pch); + pch := SYSTEM.VAL(pchar, arg2); + n := Write(SYSTEM.VAL(LONGINT, pch), 1); + IF n # 1 THEN RETURN FALSE ELSE RETURN TRUE END + *) + ELSIF syscall = Sys.open THEN + pstr := SYSTEM.VAL(pstring, arg1); + d0 := Unix.Open(pstr^, SYSTEM.VAL(SET, arg3), SYSTEM.VAL(SET, arg2)); + IF d0 >= 0 THEN RETURN TRUE ELSE RETURN FALSE END + ELSIF syscall = Sys.close THEN + d0 := Unix.Close(arg1); + IF d0 = 0 THEN RETURN TRUE ELSE RETURN FALSE END + ELSIF syscall = Sys.lseek THEN + d0 := Unix.Lseek(arg1, arg2, arg3); + IF d0 >= 0 THEN RETURN TRUE ELSE RETURN FALSE END + ELSIF syscall = Sys.ioctl THEN + d0 := Unix.Ioctl(arg1, arg2, arg3); + RETURN d0 >= 0; + ELSIF syscall = Sys.fcntl THEN + d0 := Unix.Fcntl (arg1, arg2, arg3); + RETURN d0 >= 0; + ELSIF syscall = Sys.dup THEN + d0 := Unix.Dup(arg1); + RETURN d0 > 0; + ELSIF syscall = Sys.pipe THEN + d0 := Unix.Pipe(arg1); + RETURN d0 >= 0; + ELSIF syscall = Sys.newstat THEN + pst := SYSTEM.VAL(pstatus, arg2); + pstr := SYSTEM.VAL(pstring, arg1); + d0 := Unix.Stat(pstr^, pst^); + RETURN d0 >= 0 + ELSIF syscall = Sys.newfstat THEN + pst := SYSTEM.VAL(pstatus, arg2); + d0 := Unix.Fstat(arg1, pst^); + RETURN d0 >= 0; + END + + END UNIXCALL; + + + PROCEDURE UNIXFORK(VAR pid: LONGINT) : BOOLEAN; + BEGIN + + END UNIXFORK; + + PROCEDURE UNIXSIGNAL(signo: INTEGER; p: PROCEDURE; + VAR old: PROCEDURE; VAR error: INTEGER) : BOOLEAN; + BEGIN + + END UNIXSIGNAL; + + PROCEDURE WMOVE*(from, to, n : LONGINT); + VAR l : LONGINT; + BEGIN + SYSTEM.MOVE(from, to, n); + END WMOVE; +END ulmSYSTEM. diff --git a/src/lib/ulm/x86_64/ulmSYSTEM.Mod b/src/lib/ulm/x86_64/ulmSYSTEM.Mod new file mode 100644 index 00000000..fa6c66a6 --- /dev/null +++ b/src/lib/ulm/x86_64/ulmSYSTEM.Mod @@ -0,0 +1,137 @@ +MODULE ulmSYSTEM; +IMPORT SYSTEM, Unix, Sys := ulmSys; + +TYPE pchar = POINTER TO ARRAY 1 OF CHAR; + pstring = POINTER TO ARRAY 1024 OF CHAR; + pstatus = POINTER TO Unix.Status; + + TYPE bytearray* = ARRAY SIZE(LONGINT) OF SYSTEM.BYTE; (* need this because voc does not convert implicitly LONGINT to ARRAY OF BYTE; -- noch *) + pbytearray* = POINTER TO bytearray; + TYPE longrealarray* = ARRAY SIZE(LONGREAL) OF SYSTEM.BYTE; (* need this because voc does not convert implicitly LONGINT to ARRAY OF BYTE; -- noch *) + plongrealarray* = POINTER TO bytearray; + + PROCEDURE LongToByteArr* ( l : LONGINT; VAR bar : bytearray); (* noch *) + VAR b : SYSTEM.BYTE; + p : pbytearray; + i : LONGINT; + BEGIN + p := SYSTEM.VAL(pbytearray, SYSTEM.ADR(l)); + FOR i := 0 TO SIZE(LONGINT) -1 DO + b := p^[i]; bar[i] := b; + END + END LongToByteArr; + + PROCEDURE LRealToByteArr* ( l : LONGREAL; VAR lar : longrealarray); (* noch *) + VAR b : SYSTEM.BYTE; + p : plongrealarray; + i : LONGINT; + BEGIN + p := SYSTEM.VAL(plongrealarray, SYSTEM.ADR(l)); + FOR i := 0 TO SIZE(LONGREAL) -1 DO + b := p^[i]; lar[i] := b; + END + END LRealToByteArr; + + +(* + PROCEDURE -Write(adr, n: LONGINT): LONGINT + "write(1/*stdout*/, adr, n)"; + + PROCEDURE -read(VAR ch: CHAR): LONGINT + "read(0/*stdin*/, ch, 1)"; +*) + + PROCEDURE TAS*(VAR flag:BOOLEAN): BOOLEAN; (* added for compatibility with ulmSYSTEM module; noch *) + VAR oldflag : BOOLEAN; + BEGIN + oldflag := flag; + flag := TRUE; + RETURN oldflag; + END TAS; + + PROCEDURE UNIXCALL*(syscall: LONGINT; VAR d0, d1: LONGINT; (* in ulm version both LONGINT and INTEGER are 4 byte size *) + arg1, arg2, arg3: LONGINT) : BOOLEAN; + VAR + n : LONGINT; + ch : CHAR; + pch : pchar; + pstr : pstring; + pst : pstatus; + BEGIN + + IF syscall = Sys.read THEN + d0 := Unix.Read(SHORT(arg1), arg2, arg3); + IF d0 >= 0 THEN RETURN TRUE ELSE RETURN FALSE END + (*NEW(pch); + pch := SYSTEM.VAL(pchar, arg2); + ch := pch^[0]; + n := read(ch); + IF n # 1 THEN + ch := 0X; + RETURN FALSE + ELSE + pch^[0] := ch; + RETURN TRUE + END; + *) + ELSIF syscall = Sys.write THEN + d0 := Unix.Write(SHORT(arg1), arg2, arg3); + IF d0 >= 0 THEN RETURN TRUE ELSE RETURN FALSE END + (*NEW(pch); + pch := SYSTEM.VAL(pchar, arg2); + n := Write(SYSTEM.VAL(LONGINT, pch), 1); + IF n # 1 THEN RETURN FALSE ELSE RETURN TRUE END + *) + ELSIF syscall = Sys.open THEN + pstr := SYSTEM.VAL(pstring, arg1); + d0 := Unix.Open(pstr^, SHORT(arg3), arg2); + IF d0 >= 0 THEN RETURN TRUE ELSE RETURN FALSE END + ELSIF syscall = Sys.close THEN + d0 := Unix.Close(SHORT(arg1)); + IF d0 = 0 THEN RETURN TRUE ELSE RETURN FALSE END + ELSIF syscall = Sys.lseek THEN + d0 := Unix.Lseek(SHORT(arg1), arg2, SHORT(arg3)); + IF d0 >= 0 THEN RETURN TRUE ELSE RETURN FALSE END + ELSIF syscall = Sys.ioctl THEN + d0 := Unix.Ioctl(SHORT(arg1), SHORT(arg2), arg3); + RETURN d0 >= 0; + ELSIF syscall = Sys.fcntl THEN + d0 := Unix.Fcntl (SHORT(arg1), SHORT(arg2), arg3); + RETURN d0 >= 0; + ELSIF syscall = Sys.dup THEN + d0 := Unix.Dup(SHORT(arg1)); + RETURN d0 > 0; + ELSIF syscall = Sys.pipe THEN + d0 := Unix.Pipe(arg1); + RETURN d0 >= 0; + ELSIF syscall = Sys.newstat THEN + pst := SYSTEM.VAL(pstatus, arg2); + pstr := SYSTEM.VAL(pstring, arg1); + d0 := Unix.Stat(pstr^, pst^); + RETURN d0 >= 0 + ELSIF syscall = Sys.newfstat THEN + pst := SYSTEM.VAL(pstatus, arg2); + d0 := Unix.Fstat(SHORT(arg1), pst^); + RETURN d0 >= 0; + END + + END UNIXCALL; + + + PROCEDURE UNIXFORK(VAR pid: LONGINT) : BOOLEAN; + BEGIN + + END UNIXFORK; + + PROCEDURE UNIXSIGNAL(signo: INTEGER; p: PROCEDURE; + VAR old: PROCEDURE; VAR error: INTEGER) : BOOLEAN; + BEGIN + + END UNIXSIGNAL; + + PROCEDURE WMOVE*(from, to, n : LONGINT); + VAR l : LONGINT; + BEGIN + SYSTEM.MOVE(from, to, n); + END WMOVE; +END ulmSYSTEM. diff --git a/src/test/files/testFiles.Mod b/src/test/files/testFiles.Mod index cc3b46ad..f6361b89 100644 --- a/src/test/files/testFiles.Mod +++ b/src/test/files/testFiles.Mod @@ -3,7 +3,7 @@ MODULE testFiles; IMPORT Files, Texts, Console; -CONST file="makefile"; +CONST file="testFiles.Mod"; VAR T : Texts.Text; @@ -22,7 +22,6 @@ IF F # NIL THEN WHILE ~R.eot DO Texts.Read (R, ch); Console.Char(ch); - END; ELSE diff --git a/voc b/voc index 2d1dfe090159d8b5a764dbb1bc3053389429baea..390483e096444c9abc5a2a3d5bef2ca31a078305 100755 GIT binary patch delta 251349 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%0Douz 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 aOYhL)p2$6GE_&MpdHj3+^R0K#`u_puE&L|{ delta 250706 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 z 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