Windows.Oberon.Files.Mod 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
  2. MODULE Files IN Oberon; (* pjm *)
  3. (** AUTHOR "pjm"; PURPOSE "Oberon for Aos files"; *)
  4. IMPORT SYSTEM, KernelLog IN A2, AosKernel := Kernel IN A2, Files IN A2, Kernel;
  5. CONST
  6. BufSize = 4096;
  7. MaxBufs = 4;
  8. Slow = FALSE;
  9. Trace = FALSE;
  10. TYPE
  11. File* = POINTER TO RECORD
  12. buf: Buffer; (* circular list of buffers *)
  13. bufs: LONGINT; (* number of buffers allocated *)
  14. alen, blen: LONGINT; (* file size = alen*BufSize + blen, 0 <= blen <= BufSize *)
  15. r: Files.Rider; (* rider on underlying Aos file *)
  16. checktime, checkdate, checklen: LONGINT
  17. END;
  18. Rider* = RECORD
  19. buf: Buffer; (* buffer hint *)
  20. apos, bpos: LONGINT;
  21. eof*: BOOLEAN; (** has end of file been passed *)
  22. res*: LONGINT; (** leftover byte count for ReadBytes/WriteBytes *)
  23. f: File
  24. END;
  25. Buffer = POINTER TO RECORD
  26. apos, lim: LONGINT;
  27. mod: BOOLEAN;
  28. next: Buffer;
  29. data: ARRAY BufSize OF CHAR
  30. END;
  31. Bytes4 = ARRAY 4 OF SYSTEM.BYTE;
  32. Bytes8 = ARRAY 8 OF SYSTEM.BYTE;
  33. VAR
  34. files: AosKernel.FinalizedCollection; (* all open files - cleaned up by GC *)
  35. search: Files.File; (* file being searched for *)
  36. found: File; (* file found *)
  37. (* Update our copy of the underlying file's time and length. *)
  38. PROCEDURE UpdateFile(f: File);
  39. BEGIN
  40. f.r.file.GetDate(f.checktime, f.checkdate); f.checklen := f.r.file.Length()
  41. END UpdateFile;
  42. (* Check if our copy of the underlying file's time and length match the reality. *)
  43. PROCEDURE FileChanged(f: File): BOOLEAN;
  44. VAR time, date: LONGINT;
  45. BEGIN
  46. f.r.file.GetDate(time, date);
  47. RETURN (time # f.checktime) OR (date # f.checkdate) OR (f.r.file.Length() # f.checklen)
  48. END FileChanged;
  49. (* Enumerator used in Old to search files collection for existing file handle using Files file as key. *)
  50. PROCEDURE Search(f: ANY; VAR cont: BOOLEAN);
  51. BEGIN
  52. IF f(File).r.file = search THEN
  53. found := f(File); cont := FALSE
  54. END
  55. END Search;
  56. (** Creates a new file with the specified name. *)
  57. PROCEDURE New*(name: ARRAY OF CHAR): File;
  58. VAR f: File; file: Files.File;
  59. BEGIN
  60. Kernel.CheckOberonLock; (* can only be called from Oberon *)
  61. file := Files.New(name);
  62. IF file # NIL THEN
  63. NEW(f); f.bufs := 1; f.alen := 0; f.blen := 0;
  64. NEW(f.buf); f.buf.apos := 0; f.buf.lim := 0; f.buf.next := f.buf; f.buf.mod := FALSE;
  65. file.Set(f.r, 0); UpdateFile(f);
  66. IF name # "" THEN
  67. files.Add(f, NIL) (* add to collection *)
  68. (* it is ok to add it here, and not only in Register, as in underlying file systems, because the underlying file system will take care of the case where an Old is attempted on a file that has been New'ed, but not Register'ed (Old will fail). *)
  69. END
  70. ELSE
  71. f := NIL
  72. END;
  73. RETURN f
  74. END New;
  75. (** Open an existing file. The same file descriptor is returned if a file is opened multiple times. *)
  76. PROCEDURE Old*(name: ARRAY OF CHAR): File;
  77. VAR f: File; file: Files.File; len: LONGINT;
  78. BEGIN
  79. Kernel.CheckOberonLock; (* can only be called from Oberon *)
  80. file := Files.Old(name);
  81. IF file # NIL THEN
  82. search := file; found := NIL; (* search for existing handle *)
  83. files.Enumerate(Search); (* modify global found *)
  84. search := NIL; f := found; found := NIL;
  85. IF (f # NIL) & FileChanged(f) THEN (* underlying file changed *)
  86. IF Trace THEN
  87. KernelLog.String("Files: Stale "); WriteFile(f); KernelLog.Ln
  88. END;
  89. files.Remove(f); f := NIL (* throw away old record (even though user may still have a copy; that is his fault) *)
  90. END;
  91. IF f = NIL THEN (* none found, create new handle *)
  92. len := file.Length();
  93. NEW(f); f.bufs := 1; f.alen := len DIV BufSize; f.blen := len MOD BufSize;
  94. NEW(f.buf); f.buf.apos := 0; f.buf.next := f.buf; f.buf.mod := FALSE;
  95. file.Set(f.r, 0); file.ReadBytes(f.r, f.buf.data, 0, BufSize);
  96. IF f.alen = 0 THEN f.buf.lim := f.blen ELSE f.buf.lim := BufSize END;
  97. UpdateFile(f);
  98. files.Add(f, NIL) (* add to collection *)
  99. ELSE
  100. (* return existing handle *)
  101. END
  102. ELSE
  103. f := NIL
  104. END;
  105. RETURN f
  106. END Old;
  107. (** Register a file created with New in the directory, replacing the previous file in the directory with the same name. The file is automatically closed. *)
  108. PROCEDURE Register*(f: File);
  109. BEGIN
  110. Update(f); Files.Register(f.r.file)
  111. END Register;
  112. (** Flushes the changes made to a file to disk. Register will automatically Close a file. *)
  113. PROCEDURE Close*(f: File);
  114. BEGIN
  115. IF f # NIL THEN Update(f) END
  116. END Close;
  117. (** Returns the current length of a file. *)
  118. PROCEDURE Length*(f: File): LONGINT;
  119. BEGIN
  120. RETURN f.alen*BufSize + f.blen
  121. END Length;
  122. (** Returns the time (t) and date (d) when a file was last modified. *)
  123. PROCEDURE GetDate*(f: File; VAR t, d: LONGINT);
  124. BEGIN
  125. f.r.file.GetDate(t, d)
  126. END GetDate;
  127. (** Sets the modification time (t) and date (d) of a file. *)
  128. PROCEDURE SetDate*(f: File; t, d: LONGINT);
  129. BEGIN
  130. Update(f); (* otherwise later updating will modify time/date again *)
  131. f.r.file.SetDate(t, d)
  132. END SetDate;
  133. (** Positions a Rider at a certain position in a file. Multiple Riders can be positioned at different locations in a file. A Rider cannot be positioned beyond the end of a file. *)
  134. PROCEDURE Set*(VAR r: Rider; f: File; pos: LONGINT);
  135. BEGIN
  136. IF f # NIL THEN
  137. r.eof := FALSE; r.res := 0; r.buf := f.buf; r.f := f;
  138. IF pos < 0 THEN
  139. r.apos := 0; r.bpos := 0
  140. ELSIF pos < f.alen*BufSize + f.blen THEN
  141. r.apos := pos DIV BufSize; r.bpos := pos MOD BufSize
  142. ELSE
  143. r.apos := f.alen; r.bpos := f.blen (* blen may be BufSize *)
  144. END
  145. ELSE
  146. r.buf := NIL; r.f := NIL
  147. END
  148. END Set;
  149. (** Returns the offset of a Rider positioned on a file. *)
  150. PROCEDURE Pos*(VAR r: Rider): LONGINT;
  151. BEGIN
  152. RETURN r.apos*BufSize + r.bpos
  153. END Pos;
  154. (** Returns the File a Rider is based on. *)
  155. PROCEDURE Base*(VAR r: Rider): File;
  156. BEGIN
  157. RETURN r.f
  158. END Base;
  159. (** Read a byte from a file, advancing the Rider one byte further. R.eof indicates if the end of the file has been passed. *)
  160. PROCEDURE Read*(VAR r: Rider; VAR x: SYSTEM.BYTE);
  161. VAR buf: Buffer;
  162. BEGIN
  163. buf := r.buf;
  164. IF r.apos # buf.apos THEN buf := GetBuf(r.f, r.apos); r.buf := buf END;
  165. IF r.bpos < buf.lim THEN
  166. x := buf.data[r.bpos]; INC(r.bpos)
  167. ELSIF r.apos < r.f.alen THEN
  168. INC(r.apos);
  169. buf := SearchBuf(r.f, r.apos);
  170. IF buf = NIL THEN (* replace a buffer *)
  171. buf := r.buf;
  172. IF buf.mod THEN WriteBuf(r.f, buf) END;
  173. ReadBuf(r.f, buf, r.apos)
  174. ELSE
  175. r.buf := buf
  176. END;
  177. IF buf.lim > 0 THEN
  178. x := buf.data[0]; r.bpos := 1
  179. ELSE
  180. x := 0X; r.eof := TRUE
  181. END
  182. ELSE
  183. x := 0X; r.eof := TRUE
  184. END
  185. END Read;
  186. (** Reads a sequence of length n bytes into the buffer x, advancing the Rider. Less bytes will be read when reading over the length of the file. r.res indicates the number of unread bytes. x must be big enough to hold n bytes. *)
  187. PROCEDURE ReadBytes*(VAR r: Rider; VAR x: ARRAY OF SYSTEM.BYTE; len: LONGINT);
  188. VAR src, dst: ADDRESS; m: LONGINT; buf: Buffer; ch: CHAR;
  189. BEGIN
  190. IF LEN(x) < len THEN SYSTEM.HALT(19) END;
  191. IF Slow THEN
  192. m := 0;
  193. LOOP
  194. IF len <= 0 THEN EXIT END;
  195. Read(r, ch);
  196. IF r.eof THEN EXIT END;
  197. x[m] := ch; INC(m); DEC(len)
  198. END;
  199. r.res := len
  200. ELSE
  201. IF len > 0 THEN
  202. dst := ADDRESSOF(x[0]); buf := r.buf;
  203. IF r.apos # buf.apos THEN buf := GetBuf(r.f, r.apos); r.buf := buf END;
  204. LOOP
  205. IF len <= 0 THEN EXIT END;
  206. src := ADDRESSOF(buf.data[0]) + r.bpos; m := r.bpos + len;
  207. IF m <= buf.lim THEN
  208. SYSTEM.MOVE(src, dst, len); r.bpos := m; r.res := 0; EXIT
  209. ELSIF buf.lim = BufSize THEN
  210. m := buf.lim - r.bpos;
  211. IF m > 0 THEN SYSTEM.MOVE(src, dst, m); INC(dst, m); DEC(len, m) END;
  212. IF r.apos < r.f.alen THEN
  213. INC(r.apos); r.bpos := 0; buf := SearchBuf(r.f, r.apos);
  214. IF buf = NIL THEN
  215. buf := r.buf;
  216. IF buf.mod THEN WriteBuf(r.f, buf) END;
  217. ReadBuf(r.f, buf, r.apos)
  218. ELSE
  219. r.buf := buf
  220. END
  221. ELSE
  222. r.bpos := buf.lim; r.res := len; r.eof := TRUE; EXIT
  223. END
  224. ELSE
  225. m := buf.lim - r.bpos;
  226. IF m > 0 THEN SYSTEM.MOVE(src, dst, m); r.bpos := buf.lim END;
  227. r.res := len - m; r.eof := TRUE; EXIT
  228. END
  229. END
  230. ELSE
  231. r.res := 0
  232. END
  233. END
  234. END ReadBytes;
  235. PROCEDURE Copy*(src,dest: ARRAY OF CHAR; VAR res: INTEGER);
  236. CONST BufLen = 8192;
  237. VAR f, g: File; Rf, Rg: Rider; buf : ARRAY BufLen OF CHAR; i: LONGINT;
  238. BEGIN
  239. res := 0;
  240. f := Old(src);
  241. IF f = NIL THEN res := -1; RETURN END;
  242. g := New(dest);
  243. IF g = NIL THEN res := -2; RETURN END;
  244. Set(Rf, f, 0); Set(Rg, g, 0);
  245. i := 0;
  246. WHILE i < Length(f) DIV BufLen DO
  247. ReadBytes(Rf,buf,BufLen); WriteBytes(Rg,buf,BufLen); INC(i)
  248. END;
  249. ReadBytes(Rf, buf, Length(f) MOD BufLen);
  250. WriteBytes(Rg, buf, Length(f) MOD BufLen);
  251. Register(g)
  252. END Copy;
  253. (**
  254. Portable routines to read the standard Oberon types.
  255. *)
  256. PROCEDURE ReadInt*(VAR r: Rider; VAR x: INTEGER);
  257. VAR x0, x1: SHORTINT;
  258. BEGIN
  259. Read(r, x0); Read(r, x1);
  260. x := LONG(x1) * 100H + LONG(x0) MOD 100H
  261. END ReadInt;
  262. PROCEDURE ReadLInt*(VAR r: Rider; VAR x: LONGINT);
  263. BEGIN
  264. ReadBytes(r, SYSTEM.VAL(Bytes4, x), 4)
  265. END ReadLInt;
  266. PROCEDURE ReadSet*(VAR r: Rider; VAR x: SET);
  267. BEGIN
  268. ReadBytes(r, SYSTEM.VAL(Bytes4, x), 4)
  269. END ReadSet;
  270. PROCEDURE ReadBool*(VAR r: Rider; VAR x: BOOLEAN);
  271. VAR s: SHORTINT;
  272. BEGIN
  273. Read(r, s); x := s # 0
  274. END ReadBool;
  275. PROCEDURE ReadReal*(VAR r: Rider; VAR x: REAL);
  276. BEGIN
  277. ReadBytes(r, SYSTEM.VAL(Bytes4, x), 4)
  278. END ReadReal;
  279. PROCEDURE ReadLReal*(VAR r: Rider; VAR x: LONGREAL);
  280. BEGIN
  281. ReadBytes(r, SYSTEM.VAL(Bytes8, x), 8)
  282. END ReadLReal;
  283. PROCEDURE ReadString*(VAR r: Rider; VAR x: ARRAY OF CHAR);
  284. VAR i: INTEGER; ch: CHAR;
  285. BEGIN i := 0;
  286. LOOP
  287. Read(r, ch); x[i] := ch; INC(i);
  288. IF ch = 0X THEN EXIT END;
  289. IF i = LEN(x) THEN x[i-1] := 0X;
  290. REPEAT Read(r, ch) UNTIL ch = 0X;
  291. EXIT
  292. END
  293. END
  294. END ReadString;
  295. (** Reads a number in compressed variable length notation using the minimum amount of bytes. *)
  296. PROCEDURE ReadNum*(VAR r: Rider; VAR x: LONGINT);
  297. VAR ch: CHAR; n: INTEGER; y: LONGINT;
  298. BEGIN
  299. n := 0; y := 0; Read(r, ch);
  300. WHILE ch >= 80X DO INC(y, LSH(LONG(ORD(ch)) - 128, n)); INC(n, 7); Read(r, ch) END;
  301. x := ASH(LSH(LONG(ORD(ch)), 25), n-25) + y
  302. END ReadNum;
  303. (** Writes a byte into the file at the Rider position, advancing the Rider by one. *)
  304. PROCEDURE Write*(VAR r: Rider; x: SYSTEM.BYTE);
  305. VAR buf: Buffer;
  306. BEGIN
  307. buf := r.buf;
  308. IF r.apos # buf.apos THEN buf := GetBuf(r.f, r.apos); r.buf := buf END;
  309. IF r.bpos >= buf.lim THEN
  310. IF r.bpos < BufSize THEN
  311. INC(buf.lim); INC(r.f.blen) (* blen may become BufSize *)
  312. ELSE
  313. buf.lim := BufSize; (* used by WriteBuf *)
  314. WriteBuf(r.f, buf); INC(r.apos); buf := SearchBuf(r.f, r.apos);
  315. IF buf = NIL THEN
  316. buf := r.buf;
  317. IF r.apos <= r.f.alen THEN
  318. ReadBuf(r.f, buf, r.apos)
  319. ELSE
  320. buf.apos := r.apos; buf.lim := 1; INC(r.f.alen); r.f.blen := 1
  321. END
  322. ELSE
  323. r.buf := buf
  324. END;
  325. r.bpos := 0
  326. END
  327. END;
  328. buf.data[r.bpos] := CHR(x); INC(r.bpos); buf.mod := TRUE
  329. END Write;
  330. (** Writes the buffer x containing n bytes into a file at the Rider position. *)
  331. PROCEDURE WriteBytes*(VAR r: Rider; VAR x: ARRAY OF SYSTEM.BYTE; len: LONGINT);
  332. VAR src, dst: ADDRESS; m: LONGINT; buf: Buffer;
  333. BEGIN
  334. IF LEN(x) < len THEN SYSTEM.HALT(19) END;
  335. IF Slow THEN
  336. m := 0;
  337. WHILE len > 0 DO
  338. Write(r, x[m]); INC(m); DEC(len)
  339. END;
  340. r.res := len
  341. ELSE
  342. IF len > 0 THEN
  343. src := ADDRESSOF(x[0]);
  344. buf := r.buf;
  345. IF r.apos # buf.apos THEN buf := GetBuf(r.f, r.apos); r.buf := buf END;
  346. LOOP
  347. IF len <= 0 THEN EXIT END;
  348. buf.mod := TRUE; dst := ADDRESSOF(buf.data[0]) + r.bpos; m := r.bpos + len;
  349. IF m <= buf.lim THEN
  350. SYSTEM.MOVE(src, dst, len); r.bpos := m; EXIT
  351. ELSIF m <= BufSize THEN
  352. SYSTEM.MOVE(src, dst, len); r.bpos := m;
  353. r.f.blen := m; buf.lim := m; EXIT
  354. ELSE
  355. buf.lim := BufSize; (* used by WriteBuf *)
  356. m := BufSize - r.bpos;
  357. IF m > 0 THEN SYSTEM.MOVE(src, dst, m); INC(src, m); DEC(len, m) END;
  358. WriteBuf(r.f, buf); INC(r.apos); r.bpos := 0; buf := SearchBuf(r.f, r.apos);
  359. IF buf = NIL THEN
  360. buf := r.buf;
  361. IF r.apos <= r.f.alen THEN
  362. ReadBuf(r.f, buf, r.apos)
  363. ELSE
  364. buf.apos := r.apos; buf.lim := 0; INC(r.f.alen); r.f.blen := 0
  365. END
  366. ELSE
  367. r.buf := buf
  368. END
  369. END
  370. END
  371. END
  372. END
  373. END WriteBytes;
  374. (**
  375. Portable routines to write the standard Oberon types.
  376. *)
  377. PROCEDURE WriteInt*(VAR r: Rider; x: INTEGER);
  378. BEGIN
  379. Write(r, SHORT(x)); Write(r, SHORT(x DIV 100H))
  380. END WriteInt;
  381. PROCEDURE WriteLInt*(VAR r: Rider; x: LONGINT);
  382. BEGIN
  383. WriteBytes(r, SYSTEM.VAL(Bytes4, x), 4)
  384. END WriteLInt;
  385. PROCEDURE WriteSet*(VAR r: Rider; x: SET);
  386. BEGIN
  387. WriteBytes(r, SYSTEM.VAL(Bytes4, x), 4)
  388. END WriteSet;
  389. PROCEDURE WriteBool*(VAR r: Rider; x: BOOLEAN);
  390. BEGIN
  391. IF x THEN Write(r, 1) ELSE Write(r, 0) END
  392. END WriteBool;
  393. PROCEDURE WriteReal*(VAR r: Rider; x: REAL);
  394. BEGIN
  395. WriteBytes(r, SYSTEM.VAL(Bytes4, x), 4)
  396. END WriteReal;
  397. PROCEDURE WriteLReal*(VAR r: Rider; x: LONGREAL);
  398. BEGIN
  399. WriteBytes(r, SYSTEM.VAL(Bytes8, x), 8)
  400. END WriteLReal;
  401. PROCEDURE WriteString*(VAR r: Rider; x: ARRAY OF CHAR);
  402. VAR i: INTEGER; ch: CHAR;
  403. BEGIN
  404. i := 0;
  405. LOOP ch := x[i]; Write(r, ch); INC(i);
  406. IF ch = 0X THEN EXIT END;
  407. IF i = LEN(x) THEN Write(r, 0X); EXIT END
  408. END
  409. END WriteString;
  410. (** Writes a number in a compressed format. *)
  411. PROCEDURE WriteNum*(VAR r: Rider; x: LONGINT);
  412. BEGIN
  413. WHILE (x < - 64) OR (x > 63) DO Write(r, CHR(x MOD 128 + 128)); x := x DIV 128 END;
  414. Write(r, CHR(x MOD 128))
  415. END WriteNum;
  416. (** Deletes a file. res = 0 indicates success. *)
  417. PROCEDURE Delete*(name: ARRAY OF CHAR; VAR res: INTEGER);
  418. VAR r: LONGINT;
  419. BEGIN
  420. Files.Delete(name, r);
  421. IF (r >= MIN(INTEGER)) & (r <= MAX(INTEGER)) THEN res := SHORT(r) ELSE res := -1 END
  422. END Delete;
  423. (** Renames a file. res = 0 indicates success. *)
  424. PROCEDURE Rename*(old, new: ARRAY OF CHAR; VAR res: INTEGER);
  425. VAR r: LONGINT;
  426. BEGIN
  427. Files.Rename(old, new, r);
  428. IF (r >= MIN(INTEGER)) & (r <= MAX(INTEGER)) THEN res := SHORT(r) ELSE res := -1 END
  429. END Rename;
  430. (** Returns the full name of a file. *)
  431. PROCEDURE GetName*(f: File; VAR name: ARRAY OF CHAR);
  432. BEGIN
  433. f.r.file.GetName(name)
  434. END GetName;
  435. PROCEDURE ReadBuf(f: File; buf: Buffer; pos: LONGINT);
  436. VAR file: Files.File; name: ARRAY 256 OF CHAR;
  437. BEGIN
  438. file := f.r.file;
  439. file.Set(f.r, pos*BufSize);
  440. IF file.Pos(f.r) # pos*BufSize THEN file.GetName(name); KernelLog.String("name="); KernelLog.String(name); KernelLog.Ln; END;
  441. ASSERT(file.Pos(f.r) = pos*BufSize);
  442. file.ReadBytes(f.r, buf.data, 0, BufSize);
  443. IF pos < f.alen THEN buf.lim := BufSize ELSE buf.lim := f.blen END;
  444. buf.apos := pos; buf.mod := FALSE;
  445. END ReadBuf;
  446. PROCEDURE WriteBuf(f: File; buf: Buffer);
  447. VAR pos, n: LONGINT; file: Files.File;
  448. BEGIN
  449. file := f.r.file;
  450. pos := buf.apos*BufSize;
  451. n := pos - file.Length();
  452. IF n > 0 THEN (* pos is past current eof, extend file *)
  453. file.Set(f.r, file.Length());
  454. WHILE n > 0 DO file.Write(f.r, 0X); DEC(n) END
  455. END;
  456. file.Set(f.r, pos);
  457. ASSERT(file.Pos(f.r) = pos);
  458. file.WriteBytes(f.r, buf.data, 0, buf.lim);
  459. UpdateFile(f);
  460. buf.mod := FALSE
  461. END WriteBuf;
  462. PROCEDURE SearchBuf(f: File; pos: LONGINT): Buffer;
  463. VAR buf: Buffer;
  464. BEGIN
  465. buf := f.buf;
  466. LOOP
  467. IF buf.apos = pos THEN EXIT END;
  468. buf := buf.next;
  469. IF buf = f.buf THEN buf := NIL; EXIT END
  470. END;
  471. RETURN buf
  472. END SearchBuf;
  473. PROCEDURE GetBuf(f: File; pos: LONGINT): Buffer;
  474. VAR buf: Buffer;
  475. BEGIN
  476. buf := f.buf;
  477. LOOP
  478. IF buf.apos = pos THEN EXIT END;
  479. IF buf.next = f.buf THEN
  480. IF f.bufs < MaxBufs THEN
  481. NEW(buf); buf.next := f.buf.next; f.buf.next := buf;
  482. INC(f.bufs)
  483. ELSE
  484. f.buf := buf;
  485. IF buf.mod THEN WriteBuf(f, buf) END
  486. END;
  487. buf.apos := pos;
  488. IF pos <= f.alen THEN ReadBuf(f, buf, pos) END; (* ELSE? *)
  489. EXIT
  490. END;
  491. buf := buf.next
  492. END;
  493. RETURN buf
  494. END GetBuf;
  495. PROCEDURE Update(f: File);
  496. VAR buf: Buffer;
  497. BEGIN
  498. buf := f.buf;
  499. REPEAT
  500. IF buf.mod THEN WriteBuf(f, buf) END;
  501. buf := buf.next
  502. UNTIL buf = f.buf;
  503. f.r.file.Update(); (* update the underlying file also *)
  504. UpdateFile(f)
  505. END Update;
  506. PROCEDURE WriteFile(f: File);
  507. VAR name: ARRAY 256 OF CHAR;
  508. BEGIN
  509. IF Trace THEN
  510. KernelLog.Hex(SYSTEM.VAL(LONGINT, f), 8); KernelLog.Char(" ");
  511. KernelLog.Hex(SYSTEM.VAL(LONGINT, f.r.file), 1); KernelLog.Char(" ");
  512. KernelLog.Int(Length(f), 1); KernelLog.Char(" ");
  513. KernelLog.Int(f.r.file.Length(), 1); KernelLog.Char(" ");
  514. GetName(f, name);
  515. KernelLog.String(name)
  516. END
  517. END WriteFile;
  518. (* debugging *)
  519. (*
  520. PROCEDURE ShowList*;
  521. VAR
  522. enum: OBJECT
  523. VAR i: LONGINT;
  524. PROCEDURE EnumFile(f: ANY; VAR cont: BOOLEAN);
  525. BEGIN
  526. WITH f: File DO
  527. KernelLog.Int(i, 1); KernelLog.Char(" ");
  528. WriteFile(f); KernelLog.Ln;
  529. INC(i)
  530. END
  531. END EnumFile;
  532. END;
  533. BEGIN
  534. NEW(enum); enum.i := 0; KernelLog.Ln;
  535. files.Enumerate(enum.EnumFile)
  536. END ShowList;
  537. *)
  538. BEGIN
  539. NEW(files)
  540. END Files.
  541. (** Remarks:
  542. 1. Oberon uses the little-endian byte ordering for exchanging files between different Oberon platforms.
  543. 2. Files are separate entities from directory entries. Files may be anonymous by having no name and not being registered in a directory. Files only become visible to other clients of the Files module by explicitly passing a File descriptor or by registering a file and then opening it from the other client. Deleting a file of which a file descriptor is still available, results in the file becoming anonymous. The deleted file may be re-registered at any time.
  544. 3. Files and their access mechanism (Riders) are separated. A file might have more than one rider operating on it at different offsets in the file.
  545. 4. The garbage collector will automatically close files when they are not required any more. File buffers will be discarded without flushing them to disk. Use the Close procedure to update modified files on disk.
  546. 5. Relative and absolute filenames written in the directory syntax of the host operating system are used. By convention, Oberon filenames consists of the letters A..Z, a..z, 0..9, and ".". The directory separator is typically / or :. Oberon filenames are case sensitive. *)
  547. (*
  548. to do:
  549. o Rename duplicate methods/procedures in Files (e.g. Register0 method)
  550. o remove Read/Write methods to encourage buffering (bad idea?)
  551. - handle case where underlying file is changed by someone else (e.g. a log file being written by an active object)
  552. - check if file handle is a good "key" (yes, because it can not be re-used while we hold it in the list, through the rider)
  553. *)