home *** CD-ROM | disk | FTP | other *** search
/ Meeting Pearls 1 / Meeting Pearls Vol 1 (1994).iso / installed_progs / dev / fsystem-1.2 / fsystem.mod < prev    next >
Encoding:
Text File  |  1994-05-22  |  31.2 KB  |  1,121 lines

  1. (* (* $VER: FSystem 1.2 (24-Nov-93) Copyright © by Lars Düning *) *)
  2.  
  3. MODULE FSystem;
  4.  
  5. (*---------------------------------------------------------------------------
  6. ** File handling library module.
  7. **
  8. ** Copyright © 1991-1993  Lars Düning  -  All rights reserved.
  9. ** Permission granted for non-commercial use.
  10. **---------------------------------------------------------------------------
  11. ** The module evolved from the Filesystem module of Amiga-Oberon v1.17.1.
  12. ** It was developed independantly, though being compatible on source level.
  13. **
  14. ** BUGS:
  15. **   Doesn't use the OS-2.0 buffered files.
  16. **   Doesn't know multi-assigns.
  17. **---------------------------------------------------------------------------
  18. ** Oberon-2: Amiga-Oberon v3.10, F. Siebert / A+L AG
  19. **---------------------------------------------------------------------------
  20. ** [lars] Lars Düning; Am Wendenwehr 25; D-38114-Braunschweig;
  21. **                     Germany; Tel. 49-531-345692
  22. **---------------------------------------------------------------------------
  23. ** 25-Feb-91 [lars]
  24. ** 25-May-91 [lars] variable buffer sizes
  25. ** 30-Nov-91 [lars] removed a type, quick adaption for v2.13.
  26. ** 01-Dec-91 [lars] where suiting, LONGINT replaced by SYSTEM.ADDRESS.
  27. ** 21-Mar-93 [lars] recompiled for Oberon v3.00, ReadLongString() new
  28. ** 24-Nov-93 [lars] recompiled for Oberon v3.10
  29. **---------------------------------------------------------------------------
  30. *)
  31.  
  32. (* $OvflChk- $RangeChk- $StackChk- $NilChk- $ReturnChk- $CaseChk- *)
  33.  
  34. IMPORT
  35.   (* $IF Debug *) Debug, (* $END *)
  36.   bt:BasicTypes, sd:SecureDos, ol:OberonLib,
  37.   d:Dos, e:Exec, s:SYSTEM;
  38.  
  39. (*-------------------------------------------------------------------------*)
  40.  
  41. TYPE
  42.   BufPtr    = UNTRACED POINTER TO s.BYTE;
  43.   FilePtr * = POINTER TO File;
  44.  
  45. CONST
  46.  
  47.   StdBufSize *= 1024;  (* Default buffer size *)
  48.   cEof        = 1CX;   (* End-Of-File character *)
  49.  
  50.     (* Open() access modes *)
  51.  
  52.   newFile * = d.newFile;   (* excl. access, erases existing file *)
  53.   oldFile * = d.oldFile;   (* shared access, file has to exist *)
  54.   update  * = d.readWrite; (* excl. access, file may exist *)
  55.  
  56.     (* Open() operation modes *)
  57.  
  58.   writeOnly * = 0;
  59.   readOnly  * = 1;
  60.   readWrite * = 2;
  61.  
  62.     (* File.status, error codes in fact *)
  63.  
  64.   ok        * = 0;  (* no error occured *)
  65.   eof       * = 1;  (* end of file reached *)
  66.   readerr   * = 2;  (* unspecified read error, ask Dos *)
  67.   writeerr  * = 3;  (* unspecified write error, ask Dos *)
  68.   onlyread  * = 4;  (* tried to write a read-only file *)
  69.   onlywrite * = 5;  (* tried to read a write-only file *)
  70.   toofar    * = 6;  (* seeked beyond the file's limits *)
  71.   outofmem  * = 7;  (* run out of memory *)
  72.   cantopen  * = 8;  (* couldn't open file *)
  73.   cantlock  * = 9;  (* couldn't lock file *)
  74.  
  75. TYPE
  76.   File * = RECORD
  77.     handle * : d.FileHandlePtr;
  78.     status * : INTEGER;
  79.     write  * : BOOLEAN;
  80.     read   * : BOOLEAN;
  81.     name   * : POINTER TO ARRAY OF CHAR;
  82.     string * : bt.DynString;
  83.     bufchg   : BOOLEAN;
  84.     buffer   : POINTER TO ARRAY OF s.BYTE;
  85.     bufptr   : LONGINT;  (* pointer to act position in buffer *)
  86.     buflen   : LONGINT;  (* number of bytes in buffer *)
  87.     bpos     : LONGINT;  (* true position of buffer[0] in file *)
  88.     pos      : LONGINT;  (* 'virtual' position of user in file *)
  89.     size     : LONGINT;  (* size of the file *)
  90.   END;
  91.   (* The file structure makes no statements about the 'true' current
  92.    * fileposition as managed by DosS (except .read is TRUE and .buflen 0).
  93.    * That means that prior to every file operation using Dos at least
  94.    * one Dos.Seek(file.pos) should happen, better a call to EmptyBuf() to
  95.    * flush the internal buffers.
  96.    *)
  97.  
  98. (*-------------------------------------------------------------------------*)
  99. (* $CopyArrays- *)
  100. PROCEDURE Use * (VAR    file: File
  101.                 ;       name: ARRAY OF CHAR
  102.                 ;    accMode: INTEGER
  103.                 ;    opMode : INTEGER
  104.                 ;    bufsize: LONGINT
  105.                 ): BOOLEAN;
  106.  
  107.  
  108. (* Open a file according to access and operation mode.
  109. **
  110. ** Arguments:
  111. **   file: the empty(!) File structure to fill in.
  112. **   name: the name of the file to open (will be copied into file).
  113. **   accMode: the access mode to use.
  114. **   opMode : the operation mode to use.
  115. **   bufSize: size of the buffer to allocate, must be at least 1.
  116. **
  117. ** Result:
  118. **   TRUE on success, else FALSE with file.status denoting the error.
  119. **
  120. ** A bufsize of 1 will result in unbuffered io.
  121. *)
  122.  
  123. VAR
  124.   lock: d.FileLockPtr;
  125.   info : d.FileInfoBlock;
  126.  
  127. BEGIN
  128.   file.buffer := NIL;
  129.   file.string := NIL;
  130.   file.handle := NIL;
  131.   file.name   := NIL;
  132.   lock := NIL;
  133.   LOOP
  134.     IF bufsize < 1 THEN
  135.       file.status := outofmem; 
  136.       EXIT;
  137.     END;
  138.     s.ALLOCATE(file.buffer, bufsize);
  139.     s.ALLOCATE(file.name, LEN(name));
  140.     IF (file.buffer = NIL) OR (file.name = NIL) THEN
  141.       file.status := outofmem;
  142.       EXIT;
  143.     END;
  144.     COPY(name, file.name^);
  145.     file.handle := sd.Open(name,accMode);
  146.     IF (file.handle = NIL) & (accMode = update) THEN
  147.       accMode := newFile;
  148.       file.handle := sd.Open(name,accMode);
  149.     END;
  150.     IF file.handle = NIL THEN
  151.       file.status := cantopen;
  152.       EXIT
  153.     END;
  154.     IF accMode = newFile THEN
  155.       file.size := 0;
  156.     ELSE
  157.       lock := sd.Lock(name,d.sharedLock);
  158.       IF (lock = NIL) OR (~d.Examine(lock, info)) THEN
  159.         file.status := cantlock;
  160.         EXIT;
  161.       END;
  162.       file.size := info.size;
  163.       sd.UnLock(lock);
  164.     END;
  165.     file.bufchg := FALSE;
  166.     file.bufptr := 0;
  167.     file.buflen := 0;
  168.     file.bpos   := 0;
  169.     file.pos    := 0;
  170.     file.write  := opMode # readOnly;
  171.     file.read   := opMode # writeOnly;
  172.     file.status := ok;
  173.     RETURN TRUE;
  174.   END;
  175.  
  176.   IF file.buffer # NIL THEN
  177.     (* $IFNOT GarbageCollector *)
  178.       DISPOSE(file.buffer);
  179.     (* $END *)
  180.     file.buffer := NIL;
  181.   END;
  182.   IF file.name # NIL THEN
  183.     (* $IFNOT GarbageCollector *)
  184.       DISPOSE(file.name);
  185.     (* $END *)
  186.     file.name := NIL;
  187.   END;
  188.   IF file.handle # NIL THEN
  189.     sd.Close(file.handle);
  190.     file.handle := NIL;
  191.   END;
  192.   IF lock # NIL THEN
  193.     sd.UnLock(lock);
  194.   END;
  195.   RETURN FALSE;
  196. END Use;
  197.  
  198. (*-------------------------------------------------------------------------*)
  199. (* $CopyArrays- *)
  200. PROCEDURE open * (VAR    file: File
  201.                  ;       name: ARRAY OF CHAR
  202.                  ;    accMode: INTEGER
  203.                  ): BOOLEAN;
  204.  
  205.  
  206. (* Open a file for read/write with a default sized buffer.
  207. **
  208. ** Arguments:
  209. **   file: the empty(!) File structure to fill in.
  210. **   name: the name of the file to open (will be copied into file).
  211. **   accMode: the access mode to use.
  212. **
  213. ** Result:
  214. **   TRUE on success, else FALSE with file.status denoting the error.
  215. **
  216. ** The allocated buffer will be of StdBufSize.
  217. ** The file will be opened for reading and writing.
  218. *)
  219.  
  220. BEGIN
  221.   RETURN Use (file, name, accMode, readWrite, StdBufSize);
  222. END open;
  223.  
  224. (*-------------------------------------------------------------------------*)
  225. (* $CopyArrays- *)
  226. PROCEDURE Open * (VAR  file: File
  227.                  ;     name: ARRAY OF CHAR
  228.                  ;    write: BOOLEAN
  229.                  ): BOOLEAN;
  230.  
  231. (* Open a file for read or write with a default sized buffer.
  232. **
  233. ** Arguments:
  234. **   file: the empty(!) File structure to fill in.
  235. **   name: the name of the file to open (will be copied into file).
  236. **   write: TRUE if the file shall be written, FALSE if it shall be read.
  237. **
  238. ** Result:
  239. **   TRUE on success, else FALSE with file.status denoting the error.
  240. **
  241. ** The allocated buffer will be of StdBufSize. The file will be opened
  242. ** for either reading or writing.
  243. **
  244. ** This is a compatibility function to FileSystem.
  245. *)
  246.  
  247. VAR
  248.   accMode, opMode : INTEGER;
  249. BEGIN
  250.   IF write THEN accMode := newFile; opMode := writeOnly;
  251.            ELSE accMode := oldFile; opMode := readOnly;  END;
  252.   RETURN Use (file, name, accMode, opMode, StdBufSize);
  253. END Open;
  254.  
  255. (*-------------------------------------------------------------------------*)
  256. (* $CopyArrays- *)
  257. PROCEDURE OpenReadWrite* (VAR file: File; name: ARRAY OF CHAR) : BOOLEAN;
  258.  
  259. (* Open a file for read and write with a default sized buffer.
  260. **
  261. ** Arguments:
  262. **   file: the empty(!) File structure to fill in.
  263. **   name: the name of the file to open (will be copied into file).
  264. **
  265. ** Result:
  266. **   TRUE on success, else FALSE with file.status denoting the error.
  267. **
  268. ** The allocated buffer will be of StdBufSize. The file will be opened
  269. ** for reading and writing.
  270. **
  271. ** This is a compatibility function to FileSystem.
  272. *)
  273.  
  274. BEGIN
  275.   RETURN Use (file, name, update, readWrite, StdBufSize);
  276. END OpenReadWrite;
  277.  
  278. (*-------------------------------------------------------------------------*)
  279. PROCEDURE Flush * {"FSystem.FlushBuf"} (VAR file: File): BOOLEAN;
  280. PROCEDURE FlushBuf * (VAR file: File): BOOLEAN;
  281.  
  282. (* Flush the buffers of a file.
  283. **
  284. ** Arguments:
  285. **   file: the file to flush.
  286. **
  287. ** Result:
  288. **   TRUE on success, else FALSE with file.status denoting the error.
  289. **   file will be update appropriately.
  290. **
  291. ** If the file has been changed, this call will flush the internal buffer
  292. ** out to the disk file. To do this, the file.buflen bytes starting
  293. ** from file.buffer[0] are written, then the old file.pos will be seeked.
  294. *)
  295.  
  296. VAR
  297.   p, pos, buflen, l, ptr : LONGINT;
  298.  
  299. BEGIN
  300.   IF ~file.read OR ~file.bufchg THEN
  301.     file.status := ok;
  302.     RETURN TRUE;
  303.   END;
  304.   pos := file.pos; (* file.pos # file.bpos+file.buflen is possible *)
  305.   p := d.Seek (file.handle, file.bpos, d.beginning);
  306.   IF p < 0 THEN
  307.     file.status := writeerr;
  308.     RETURN FALSE;
  309.   END;
  310.   file.pos := file.bpos;
  311.   file.bufptr := 0;
  312.   buflen := file.buflen;
  313.   ptr := 0;
  314.   WHILE buflen > 0 DO
  315.     l := d.Write(file.handle,file.buffer[ptr],buflen);
  316.     IF l<0 THEN
  317.       file.status := writeerr;
  318.       RETURN FALSE;
  319.     END;
  320.     INC(file.pos, l);
  321.     INC(ptr, l);
  322.     file.bufptr := ptr;
  323.     DEC(buflen,l);
  324.   END;
  325.   file.buflen := 0;
  326.   file.bufchg := FALSE;
  327.   file.bufptr := 0;
  328.   file.bpos := file.pos;
  329.   p := d.Seek(file.handle, pos, d.beginning);
  330.   IF p < 0 THEN
  331.     file.status := readerr;
  332.     RETURN FALSE;
  333.   END;
  334.   file.pos := pos;
  335.   file.status := ok;
  336.   RETURN TRUE;
  337. END FlushBuf;
  338.  
  339. (*-------------------------------------------------------------------------*)
  340. PROCEDURE EmptyBuf (VAR file: File): BOOLEAN;
  341.  
  342. (* Internal: Unconditionally flush the buffers of a file.
  343. **
  344. ** Arguments:
  345. **   file: the file to flush.
  346. **
  347. ** Result:
  348. **   TRUE on success, else FALSE with file.status denoting the error.
  349. **   file will be update appropriately.
  350. **
  351. ** The buffer of the file will always be emptied. If necessary, its
  352. ** contents are written to disk using FlushBuf().
  353. ** This function is needed if many data are to be written en bloc,
  354. ** or if a new buffer has to be read.
  355. *)
  356.  
  357. BEGIN
  358.   IF file.write AND file.bufchg THEN
  359.     RETURN FlushBuf(file);
  360.   END;
  361.   IF (file.buflen # 0) & (d.Seek(file.handle, file.pos, d.beginning) < 0) THEN
  362.     file.status := readerr;
  363.     RETURN FALSE;
  364.   END;
  365.   file.buflen := 0;
  366.   file.bufptr := 0;
  367.   file.bpos := file.pos;
  368.   file.bufchg := FALSE;
  369.   file.status := ok;
  370.   RETURN TRUE;
  371. END EmptyBuf;
  372.  
  373. (*-------------------------------------------------------------------------*)
  374. PROCEDURE close * {"FSystem.Close"} (VAR file: File);
  375. PROCEDURE Close * (VAR file: File): BOOLEAN;
  376.  
  377. (* Close the file.
  378. **
  379. ** Arguments:
  380. **   file: the file to close.
  381. **
  382. ** Result:
  383. **   TRUE on success, else FALSE with file.status denoting the error.
  384. **
  385. ** Before closing, all changed data is written out using FlushBuf().
  386. *)
  387.  
  388. VAR res: BOOLEAN;
  389.  
  390. BEGIN
  391.   res := FlushBuf(file);
  392.   (* $IFNOT GarbageCollector *)
  393.     IF file.name # NIL THEN DISPOSE (file.name); END;
  394.     IF file.string # NIL THEN DISPOSE (file.string); END;
  395.     DISPOSE(file.buffer);
  396.   (* $END *)
  397.   file.name := NIL;
  398.   file.string := NIL;
  399.   file.buffer := NIL;
  400.   sd.Close(file.handle); file.handle := NIL;
  401.   RETURN res;
  402. END Close;
  403.  
  404. (*-------------------------------------------------------------------------*)
  405. PROCEDURE read * {"FSystem.Read"}(VAR file: File; VAR to: ARRAY OF s.BYTE);
  406. PROCEDURE Read * (VAR file: File; VAR to: ARRAY OF s.BYTE): BOOLEAN;
  407.  
  408. (* Read data from a file.
  409. **
  410. ** Arguments:
  411. **   file: the file to read.
  412. **   to  : the buffer to read into.
  413. **
  414. ** Result:
  415. **   TRUE on success, else FALSE with file.status denoting the error.
  416. **
  417. ** The function optimizes large reads by circumventing file's buffer then.
  418. *)
  419.  
  420. VAR
  421.   cnt: LONGINT;
  422.   len: LONGINT;
  423.   bufpos: LONGINT;
  424.  
  425. BEGIN
  426.   IF ~file.read THEN file.status := onlywrite; RETURN FALSE; END;
  427.   cnt := 0; bufpos := file.bufptr;
  428.   IF LEN(to) > 2*LEN(file.buffer^) THEN
  429.     IF NOT (EmptyBuf (file)) THEN RETURN FALSE END;
  430.     bufpos := 0;
  431.     WHILE cnt < LEN(to) DO
  432.       len := d.Read(file.handle,to[cnt],LEN(to)-cnt);
  433.       IF len=0 THEN file.status := eof;     RETURN FALSE END;
  434.       IF len<0 THEN file.status := readerr; RETURN FALSE END;
  435.       INC (cnt, len);
  436.       INC (file.pos, len);
  437.       file.bpos := file.pos;
  438.     END;
  439.   ELSE
  440.     WHILE cnt<LEN(to) DO
  441.       IF (bufpos=file.buflen) THEN
  442.         IF NOT (EmptyBuf (file)) THEN RETURN FALSE END;
  443.         bufpos := 0;
  444.         file.buflen := d.Read(file.handle, file.buffer^,LEN(file.buffer^));
  445.         IF file.buflen<0 THEN file.status := readerr; RETURN FALSE END;
  446.         IF file.buflen=0 THEN file.status := eof;     RETURN FALSE END;
  447.       END;
  448.       len := LEN(to) - cnt;
  449.       IF len > file.buflen - bufpos THEN
  450.         len := file.buflen - bufpos;
  451.       END;
  452.       e.CopyMem (file.buffer[bufpos], to[cnt], len);
  453.       INC(cnt, len); INC(bufpos, len);
  454.       INC(file.pos, len);
  455.     END;
  456.   END;
  457.   file.bufptr := bufpos;
  458.   file.status := ok;
  459.   RETURN TRUE;
  460. END Read;
  461.  
  462. (*-------------------------------------------------------------------------*)
  463. PROCEDURE readBlock * {"FSystem.ReadBlock"}
  464.                       ( VAR    file : File
  465.                       ;          to : s.ADDRESS
  466.                       ;         size: LONGINT
  467.                       ; VAR actSize : LONGINT
  468.                       );
  469. PROCEDURE ReadBlock * ( VAR    file : File
  470.                       ;          to : s.ADDRESS
  471.                       ;         size: LONGINT
  472.                       ; VAR actSize : LONGINT
  473.                       ): BOOLEAN;
  474.  
  475. (* Read a block of data from a file.
  476. **
  477. ** Arguments:
  478. **   file   : the file to read.
  479. **   to     : the address of the buffer to read into.
  480. **   size   : the length of the buffer = number of bytes to read.
  481. **   actSize: variable taking the actual number of bytes read.
  482. **
  483. ** Result:
  484. **   TRUE on success, else FALSE with file.status denoting the error.
  485. **
  486. ** The function optimizes large reads by circumventing file's buffer then.
  487. *)
  488.  
  489. VAR
  490.   len: LONGINT;
  491.   bufpos: LONGINT;
  492.   pto: BufPtr;
  493.  
  494. BEGIN
  495.   IF ~file.read THEN file.status := onlywrite; RETURN FALSE; END;
  496.   pto := s.VAL (BufPtr, to);
  497.   actSize := 0; bufpos := file.bufptr;
  498.   IF size > 2*LEN(file.buffer^) THEN
  499.     IF NOT (EmptyBuf (file)) THEN RETURN FALSE END;
  500.     bufpos := 0;
  501.     WHILE actSize < size DO
  502.       len := d.Read(file.handle,pto^,size-actSize);
  503.       IF len=0 THEN file.status := eof;     RETURN FALSE END;
  504.       IF len<0 THEN file.status := readerr; RETURN FALSE END;
  505.       INC (actSize, len);
  506.       pto := s.VAL (BufPtr, s.VAL(LONGINT, pto) + len);
  507.       INC (file.pos, len);
  508.       file.bpos := file.pos;
  509.     END;
  510.   ELSE
  511.     WHILE actSize<size DO
  512.       IF (bufpos=file.buflen) THEN
  513.         IF NOT (EmptyBuf (file)) THEN RETURN FALSE END;
  514.         bufpos := 0;
  515.         file.buflen := d.Read(file.handle,file.buffer^,LEN(file.buffer^));
  516.         IF file.buflen=0 THEN file.status := eof;     RETURN FALSE END;
  517.         IF file.buflen<0 THEN file.status := readerr; RETURN FALSE END;
  518.       END;
  519.       len := size - actSize;
  520.       IF len > file.buflen - bufpos THEN
  521.         len := file.buflen - bufpos;
  522.       END;
  523.       e.CopyMem (file.buffer[bufpos], pto^, len);
  524.       INC(actSize, len);
  525.       pto := s.VAL (BufPtr, s.VAL(LONGINT, pto) + len);
  526.       INC(bufpos, len);
  527.       INC(file.pos, len);
  528.     END;
  529.   END;
  530.   file.bufptr := bufpos;
  531.   file.status := ok;
  532.   RETURN TRUE;
  533. END ReadBlock;
  534.  
  535. (*-------------------------------------------------------------------------*)
  536. PROCEDURE readChar * {"FSystem.ReadChar"}(VAR file: File; VAR ch: CHAR);
  537. PROCEDURE ReadChar * (VAR file: File; VAR ch: CHAR): BOOLEAN;
  538.  
  539. (* Read a character from a file.
  540. **
  541. ** Arguments:
  542. **   file: the file to read.
  543. **   ch  : the variable taking the character read.
  544. **
  545. ** Result:
  546. **   TRUE on success, else FALSE with file.status denoting the error.
  547. **
  548. ** On eof, ch is set to EOF.
  549. *)
  550.  
  551. BEGIN
  552.   IF ~file.read THEN file.status := onlywrite; RETURN FALSE; END;
  553.   ch := cEof;
  554.   IF (file.bufptr=file.buflen) THEN
  555.     IF NOT (EmptyBuf (file)) THEN RETURN FALSE END;
  556.     file.buflen := d.Read(file.handle,file.buffer^,LEN(file.buffer^));
  557.     IF file.buflen=0 THEN file.status := eof;     RETURN FALSE END;
  558.     IF file.buflen<0 THEN file.status := readerr; RETURN FALSE END;
  559.   END;
  560.   ch := CHR(file.buffer[file.bufptr]);
  561.   INC(file.bufptr);
  562.   INC(file.pos);
  563.   file.status := ok;
  564.   RETURN TRUE;
  565. END ReadChar;
  566.  
  567. (*-------------------------------------------------------------------------*)
  568. PROCEDURE readString * {"FSystem.ReadString"}
  569.                        (VAR file: File; VAR to: ARRAY OF CHAR);
  570. PROCEDURE ReadString * (VAR file: File; VAR to: ARRAY OF CHAR): BOOLEAN;
  571.  
  572. (* Read a string from a file.
  573. **
  574. ** Arguments:
  575. **   file: the file to read.
  576. **   to  : the buffer to read the string into.
  577. **
  578. ** Result:
  579. **   TRUE on success, else FALSE with file.status denoting the error.
  580. **
  581. ** The string is read until a \0 or a \n shows up (which is not stored)
  582. ** or the buffer is exhausted. If possible, the read string is terminated
  583. ** with \0.
  584. *)
  585.  
  586. VAR
  587.   cnt: LONGINT;
  588.   bufpos: LONGINT;
  589.   eos: BOOLEAN;
  590.  
  591. BEGIN
  592.   IF ~file.read THEN file.status := onlywrite; RETURN FALSE; END;
  593.   cnt := 0; bufpos := file.bufptr; eos := FALSE;
  594.   WHILE (cnt<LEN(to)) AND NOT eos DO
  595.     IF (bufpos=file.buflen) THEN
  596.       IF NOT (EmptyBuf (file)) THEN RETURN FALSE END;
  597.       bufpos := 0;
  598.       file.buflen := d.Read(file.handle,file.buffer^,LEN(file.buffer^));
  599.       to[cnt] := 0X;
  600.       IF file.buflen=0 THEN file.status := eof; RETURN FALSE END;
  601.       IF file.buflen<0 THEN file.status := readerr; RETURN FALSE END;
  602.     END;
  603.     to[cnt] := CHR(file.buffer[bufpos]);
  604.     CASE to[cnt] OF 0X,0AX: eos := TRUE; to[cnt] := 0X | ELSE END;
  605.     INC(cnt); INC(bufpos);
  606.     INC (file.pos);
  607.   END;
  608.   file.bufptr := bufpos;
  609.   file.status := ok;
  610.   RETURN TRUE;
  611. END ReadString;
  612.  
  613.  
  614. (*-------------------------------------------------------------------------*)
  615. PROCEDURE ReadLongString * {"FSystem.ReadLString"}(VAR file: File): BOOLEAN;
  616. PROCEDURE readLString * {"FSystem.ReadLString"}(VAR file: File);
  617. PROCEDURE ReadLString * (VAR file: File): BOOLEAN;
  618.  
  619. (* Read a long string from a file.
  620. **
  621. ** Arguments:
  622. **   file: the file to read.
  623. **
  624. ** Result:
  625. **   TRUE on success, else FALSE with file.status denoting the error.
  626. **   The read string is store in file.string.
  627. **
  628. ** The string is read until a \0 or a \n shows up (which is not stored).
  629. ** If possible, the read string is terminated with \0.
  630. **
  631. ** ReadLongString() is a compatibility function to FileSystem.
  632. *)
  633.  
  634. VAR
  635.   cnt: LONGINT;
  636.   bufpos: LONGINT;
  637.   eos: BOOLEAN;
  638.   new: bt.DynString;
  639.  
  640. BEGIN
  641.   IF ~file.read THEN file.status := onlywrite; RETURN FALSE; END;
  642.   IF file.string = NIL THEN
  643.     s.ALLOCATE (file.string, 100H);
  644.     IF file.string = NIL THEN file.status := outofmem; RETURN FALSE; END;
  645.   END;
  646.   file.string[0] := 0X;
  647.   cnt := 0; bufpos := file.bufptr; eos := FALSE;
  648.   WHILE NOT eos DO
  649.     IF cnt >= LEN(file.string^) THEN
  650.       s.ALLOCATE (new, 2 * cnt);
  651.       COPY (file.string^, new^);
  652. (* $IFNOT GarbageCollector *)
  653.       DISPOSE (file.string);
  654. (* $END *)
  655.       file.string := new;
  656.     END;
  657.  
  658.     IF (bufpos=file.buflen) THEN
  659.       IF NOT (EmptyBuf (file)) THEN RETURN FALSE END;
  660.       bufpos := 0;
  661.       file.buflen := d.Read(file.handle,file.buffer^,LEN(file.buffer^));
  662.       file.string[cnt] := 0X;
  663.       IF file.buflen=0 THEN file.status := eof; RETURN FALSE END;
  664.       IF file.buflen<0 THEN file.status := readerr; RETURN FALSE END;
  665.     END;
  666.     file.string[cnt] := CHR(file.buffer[bufpos]);
  667.     CASE file.string[cnt] OF 0X,0AX: eos := TRUE; file.string[cnt] := 0X | ELSE END;
  668.     INC (cnt); INC(bufpos);
  669.     INC (file.pos);
  670.   END;
  671.   file.bufptr := bufpos;
  672.   file.status := ok;
  673.   RETURN TRUE;
  674. END ReadLString;
  675.  
  676. (*-------------------------------------------------------------------------*)
  677. (* $CopyArrays- *)
  678. PROCEDURE write * {"FSystem.Write"}(VAR file: File; VAR from: ARRAY OF s.BYTE);
  679. (* $CopyArrays- *)
  680. PROCEDURE Write * (VAR file: File; VAR from: ARRAY OF s.BYTE): BOOLEAN;
  681.  
  682. (* Write data to a file.
  683. **
  684. ** Arguments:
  685. **   file: the file to write.
  686. **   from: the buffer to write from.
  687. **
  688. ** Result:
  689. **   TRUE on success, else FALSE with file.status denoting the error.
  690. **
  691. ** The function optimizes large writes by circumventing file's buffer then.
  692. *)
  693.  
  694. VAR
  695.   cnt: LONGINT;
  696.   len: LONGINT;
  697.   bufpos: LONGINT;
  698.   buflen: LONGINT;
  699.  
  700. BEGIN
  701.   IF ~file.write THEN file.status := onlyread; RETURN FALSE END;
  702.   cnt := 0; bufpos := file.bufptr;
  703.   buflen := file.buflen;
  704.   IF LEN(from) > 2*LEN(file.buffer^) THEN
  705.     IF NOT (EmptyBuf (file)) THEN
  706.       file.status := writeerr; RETURN FALSE
  707.     END;
  708.     bufpos := 0;
  709.     buflen := 0;
  710.     WHILE cnt < LEN(from) DO
  711.       len := d.Write(file.handle,from[cnt],LEN(from)-cnt);
  712.       IF len<0 THEN file.status := writeerr; RETURN FALSE END;
  713.       INC (cnt, len);
  714.       INC (file.pos, len);
  715.       IF (file.pos > file.size) THEN file.size := file.pos END;
  716.       file.bpos := file.pos;
  717.     END;
  718.   ELSE
  719.     WHILE cnt<LEN(from) DO
  720.       IF (bufpos=LEN(file.buffer^)) THEN
  721.         file.buflen := buflen;
  722.         IF NOT EmptyBuf(file) THEN
  723.           file.status := writeerr; RETURN FALSE
  724.         END;
  725.         bufpos := 0;
  726.         buflen := 0;
  727.       END;
  728.       len := LEN(from) - cnt;
  729.       IF len > LEN(file.buffer^) - bufpos THEN
  730.         len := LEN(file.buffer^) - bufpos;
  731.       END;
  732.       e.CopyMem (from[cnt], file.buffer[bufpos], len);
  733.       INC(cnt, len); INC(bufpos, len);
  734.       IF (bufpos > buflen) THEN buflen := bufpos; END;
  735.       INC (file.pos, len);
  736.       IF (file.pos > file.size) THEN file.size := file.pos END;
  737.       file.bufchg := TRUE;
  738.     END;
  739.   END;
  740.   file.bufptr := bufpos;
  741.   file.buflen := buflen;
  742.   file.status := ok;
  743.   RETURN TRUE;
  744. END Write;
  745.  
  746. (*-------------------------------------------------------------------------*)
  747. PROCEDURE writeBlock * {"FSystem.WriteBlock"}
  748.                        ( VAR    file : File
  749.                        ;        from : s.ADDRESS
  750.                        ;        size : LONGINT
  751.                        ; VAR actSize : LONGINT
  752.                        );
  753. PROCEDURE WriteBlock * ( VAR    file : File
  754.                        ;        from : s.ADDRESS
  755.                        ;        size : LONGINT
  756.                        ; VAR actSize : LONGINT
  757.                        ): BOOLEAN;
  758.  
  759. (* Write a block of data to a file.
  760. **
  761. ** Arguments:
  762. **   file    : the file to write.
  763. **   from    : the address of the buffer to write from.
  764. **   size    : the length of the buffer = the number of bytes to write.
  765. **   actSize : variable taking the actual number of bytes written.
  766. **
  767. ** Result:
  768. **   TRUE on success, else FALSE with file.status denoting the error.
  769. **   actsize: the number of bytes written.
  770. **
  771. ** The function optimizes large writes by circumventing file's buffer then.
  772. *)
  773.  
  774. VAR
  775.   len: LONGINT;
  776.   bufpos: LONGINT;
  777.   buflen: LONGINT;
  778.   pfrom : BufPtr;
  779.  
  780. BEGIN
  781.   IF ~file.write THEN file.status := onlyread; RETURN FALSE END;
  782.   actSize := 0; bufpos := file.bufptr;
  783.   buflen := file.buflen;
  784.   pfrom := s.VAL (BufPtr, from);
  785.   IF size > 2 * LEN(file.buffer^) THEN
  786.     IF NOT (EmptyBuf (file)) THEN
  787.       file.status := writeerr; RETURN FALSE
  788.     END;
  789.     bufpos := 0;
  790.     buflen := 0;
  791.     WHILE actSize < size DO
  792.       len := d.Write(file.handle, pfrom^, size-actSize);
  793.       IF len<0 THEN file.status := writeerr; RETURN FALSE END;
  794.       INC (actSize, len);
  795.       pfrom := s.VAL (BufPtr, s.VAL(LONGINT, pfrom) + len);
  796.       INC (file.pos, len);
  797.       IF (file.pos > file.size) THEN file.size := file.pos END;
  798.       file.bpos := file.pos;
  799.     END;
  800.   ELSE
  801.     WHILE actSize<size DO
  802.       IF (bufpos=LEN(file.buffer^)) THEN
  803.         file.buflen := buflen;
  804.         IF NOT EmptyBuf(file) THEN
  805.           file.status := writeerr; RETURN FALSE
  806.         END;
  807.         bufpos := 0;
  808.         buflen := 0;
  809.       END;
  810.       len := size - actSize;
  811.       IF len > LEN(file.buffer^) - bufpos THEN
  812.         len := LEN(file.buffer^) - bufpos;
  813.       END;
  814.       e.CopyMem (pfrom^, file.buffer[bufpos], len);
  815.       INC(actSize, len); INC(bufpos, len);
  816.       pfrom := s.VAL(BufPtr, s.VAL(LONGINT, pfrom) + len);
  817.       IF bufpos > buflen THEN buflen := bufpos END;
  818.       INC(file.pos,len);
  819.       IF file.pos > file.size THEN file.size := file.pos END;
  820.       file.bufchg := TRUE;
  821.     END;
  822.   END;
  823.   file.buflen := buflen;
  824.   file.bufptr := bufpos;
  825.   file.bufchg := TRUE;
  826.   file.status := ok;
  827.   RETURN TRUE;
  828. END WriteBlock;
  829.  
  830. (*-------------------------------------------------------------------------*)
  831. PROCEDURE writeChar * {"FSystem.WriteChar"}(VAR file: File; ch: CHAR);
  832. PROCEDURE WriteChar * (VAR file: File; ch: CHAR): BOOLEAN;
  833.  
  834. (* Write a character to a file.
  835. **
  836. ** Arguments:
  837. **   file: the file to write.
  838. **   ch  : the character to write.
  839. **
  840. ** Result:
  841. **   TRUE on success, else FALSE with file.status denoting the error.
  842. *)
  843.  
  844. VAR
  845.   bufpos : LONGINT;
  846. BEGIN
  847.   IF ~file.write THEN file.status := onlyread; RETURN FALSE END;
  848.   bufpos := file.bufptr;
  849.   IF bufpos=LEN(file.buffer^) THEN
  850.     IF NOT EmptyBuf(file) THEN
  851.       file.status := writeerr; RETURN FALSE
  852.     END;
  853.     bufpos := 0;
  854.   END;
  855.   file.buffer[bufpos] := ch;
  856.   INC (bufpos); file.bufptr := bufpos;
  857.   IF bufpos > file.buflen THEN file.buflen := bufpos END;
  858.   INC(file.pos);
  859.   IF file.pos > file.size THEN file.size := file.pos END;
  860.   file.status := ok;
  861.   file.bufchg := TRUE;
  862.   RETURN TRUE;
  863. END WriteChar;
  864.  
  865. (*-------------------------------------------------------------------------*)
  866. (* $CopyArrays- *)
  867. PROCEDURE writeString * {"FSystem.WriteString"}
  868.                         (VAR file: File; from: ARRAY OF CHAR);
  869. (* $CopyArrays- *)
  870. PROCEDURE WriteString * (VAR file: File; from: ARRAY OF CHAR): BOOLEAN;
  871.  
  872. (* Write a string to a file.
  873. **
  874. ** Arguments:
  875. **   file: the file to write.
  876. **   from: the string to write.
  877. **
  878. ** Result:
  879. **   TRUE on success, else FALSE with file.status denoting the error.
  880. **
  881. ** The function writes the string plus one \n character.
  882. *)
  883.  
  884. VAR
  885.   slen : LONGINT;
  886.   eos: BOOLEAN;
  887.   cnt: LONGINT;
  888.   len: LONGINT;
  889.   bufpos: LONGINT;
  890.   buflen: LONGINT;
  891.  
  892. BEGIN
  893.   IF ~file.write THEN file.status := onlyread; RETURN FALSE END;
  894.  
  895.   slen := 0; eos := FALSE;
  896.   WHILE (slen < LEN (from)) AND NOT eos DO
  897.     IF from[slen] = 0X THEN
  898.       eos := TRUE;
  899.       from[slen] := 0AX;
  900.     END;
  901.     INC (slen);
  902.   END;
  903.  
  904.   cnt := 0; bufpos := file.bufptr;
  905.   buflen := file.buflen;
  906.   IF slen > 2 * LEN(file.buffer^) THEN
  907.     IF NOT (EmptyBuf (file)) THEN
  908.       file.status := writeerr; RETURN FALSE
  909.     END;
  910.     bufpos := 0;
  911.     buflen := 0;
  912.     WHILE cnt < slen DO
  913.       len := d.Write(file.handle,from[cnt], slen-cnt);
  914.       IF len<0 THEN file.status := writeerr; RETURN FALSE END;
  915.       INC (cnt, len);
  916.       INC (file.pos, len);
  917.       IF (file.pos > file.size) THEN file.size := file.pos END;
  918.       file.bpos := file.pos;
  919.     END;
  920.   ELSE
  921.     WHILE cnt<slen DO
  922.       IF (bufpos=LEN(file.buffer^)) THEN
  923.         file.buflen := buflen;
  924.         IF NOT EmptyBuf(file) THEN
  925.           file.status := writeerr; RETURN FALSE
  926.         END;
  927.         bufpos := 0;
  928.         buflen := 0;
  929.       END;
  930.       len := slen - cnt;
  931.       IF len > LEN(file.buffer^) - bufpos THEN
  932.         len := LEN(file.buffer^) - bufpos;
  933.       END;
  934.       e.CopyMem (from[cnt], file.buffer[bufpos], len);
  935.       INC(cnt, len); INC(bufpos, len);
  936.       IF (bufpos > buflen) THEN buflen := bufpos; END;
  937.       INC (file.pos, len);
  938.       IF (file.pos > file.size) THEN file.size := file.pos END;
  939.       file.bufchg := TRUE;
  940.     END;
  941.   END;
  942.   file.bufptr := bufpos;
  943.   file.buflen := buflen;
  944.   file.status := ok;
  945.  
  946.   IF ~eos AND ~WriteChar (file, 0AX) THEN RETURN FALSE; END;
  947.  
  948.   RETURN TRUE;
  949. END WriteString;
  950.  
  951. (*-------------------------------------------------------------------------*)
  952. PROCEDURE Size * (VAR file: File): LONGINT;
  953.  
  954. (* Determine the size of a file.
  955. **
  956. ** Arguments:
  957. **   file: the file to query.
  958. **
  959. ** Result:
  960. **   The file's size in bytes.
  961. *)
  962.  
  963. BEGIN
  964.   RETURN file.size;
  965. END Size;
  966.  
  967. (*-------------------------------------------------------------------------*)
  968. PROCEDURE Position * (VAR file: File): LONGINT;
  969.  
  970. (* Determine the current position in a file.
  971. **
  972. ** Arguments:
  973. **   file: the file to query.
  974. **
  975. ** Result:
  976. **   The position within the file in bytes.
  977. *)
  978.  
  979. BEGIN
  980.   RETURN file.pos;
  981. END Position;
  982.  
  983. (*-------------------------------------------------------------------------*)
  984. PROCEDURE move * {"FSystem.Move"}(VAR file: File; to: LONGINT);
  985. PROCEDURE Move * (VAR file: File; to: LONGINT): BOOLEAN;
  986.  
  987. (* Seek within a file.
  988. **
  989. ** Arguments:
  990. **   file: the file to seek in.
  991. **   to  : the new position to seek, counted in bytes from the files start.
  992. **
  993. ** Result:
  994. **   TRUE on success, else FALSE with file.status denoting the error.
  995. *)
  996.  
  997. VAR l: LONGINT;
  998.  
  999. BEGIN
  1000.   IF (to >= file.bpos) & (to < file.bpos + file.buflen) THEN
  1001.     l := to - file.bpos;
  1002.     file.pos := to;
  1003.     INC (file.bufptr, l);
  1004.     file.status := ok;
  1005.     RETURN TRUE;
  1006.   END;
  1007.  
  1008.   IF NOT EmptyBuf(file)       THEN                        RETURN FALSE END;
  1009.   IF (to>file.size) OR (to<0) THEN file.status := toofar; RETURN FALSE END;
  1010.   IF d.Seek(file.handle,to,d.beginning)=0 THEN END;
  1011.   file.pos  := to;
  1012.   file.bpos := to;
  1013.   file.status := ok;
  1014.   RETURN TRUE;
  1015. END Move;
  1016.  
  1017. (*-------------------------------------------------------------------------*)
  1018. PROCEDURE forward * {"FSystem."}(VAR file: File; to: LONGINT);
  1019. PROCEDURE Forward * (VAR file: File; to: LONGINT): BOOLEAN;
  1020.  
  1021. (* Seek forward within a file.
  1022. **
  1023. ** Arguments:
  1024. **   file: the file to seek in.
  1025. **   to  : the number of bytes to skip forward.
  1026. **
  1027. ** Result:
  1028. **   TRUE on success, else FALSE with file.status denoting the error.
  1029. *)
  1030.  
  1031. BEGIN
  1032.   RETURN Move(file,file.pos+to);
  1033. END Forward;
  1034.  
  1035. (*-------------------------------------------------------------------------*)
  1036. PROCEDURE backward * {"FSystem.Backward"}(VAR file: File; to: LONGINT);
  1037. PROCEDURE Backward * (VAR file: File; to: LONGINT): BOOLEAN;
  1038.  
  1039. (* Seek backward within a file.
  1040. **
  1041. ** Arguments:
  1042. **   file: the file to seek in.
  1043. **   to  : the number of bytes to skip backward.
  1044. **
  1045. ** Result:
  1046. **   TRUE on success, else FALSE with file.status denoting the error.
  1047. *)
  1048.  
  1049. BEGIN
  1050.   RETURN Move(file,file.pos-to);
  1051. END Backward;
  1052.  
  1053. (*-------------------------------------------------------------------------*)
  1054. PROCEDURE delete * {"FSystem.Delete"}(VAR file: File);
  1055. PROCEDURE Delete * (VAR file: File): BOOLEAN;
  1056.  
  1057. (* Delete a file.
  1058. **
  1059. ** Arguments:
  1060. **   file: the file to delete.
  1061. **
  1062. ** Result:
  1063. **   TRUE on success, else FALSE with file.status denoting the error.
  1064. **
  1065. ** The file is closed, then deleted.
  1066. *)
  1067.  
  1068. VAR
  1069.   name : POINTER TO ARRAY OF CHAR;
  1070.   rc : BOOLEAN;
  1071. BEGIN
  1072.   IF file.name = NIL THEN
  1073.     file.status := cantopen;
  1074.     RETURN FALSE;
  1075.   END;
  1076.   NEW(name, LEN(file.name^));
  1077.   COPY(file.name^, name^);
  1078.   close(file);
  1079.   rc := d.DeleteFile(name^);
  1080.   (* $IFNOT GarbageCollector *)
  1081.     DISPOSE(name);
  1082.   (* $END *)
  1083.   RETURN rc;
  1084. END Delete;
  1085.  
  1086.  
  1087. (*-------------------------------------------------------------------------*)
  1088. (* $CopyArrays- *)
  1089. PROCEDURE Exists * (name: ARRAY OF CHAR): BOOLEAN;
  1090.  
  1091. (* Check if a named file exists.
  1092. **
  1093. ** Arguments:
  1094. **   name: the filename to check.
  1095. **
  1096. ** Result:
  1097. **   TRUE if the file exists, else FALSE.
  1098. **
  1099. ** BUGS:
  1100. **   Doesn't handle multi-assigns.
  1101. *)
  1102.  
  1103. VAR
  1104.   lock: d.FileLockPtr;
  1105.  
  1106. BEGIN
  1107.   lock := sd.Lock(name,d.sharedLock);
  1108.   IF lock#NIL THEN
  1109.     sd.UnLock(lock); RETURN TRUE
  1110.   ELSE
  1111.     RETURN FALSE;
  1112.   END;
  1113. END Exists;
  1114.  
  1115. (*=========================================================================*)
  1116.  
  1117. END FSystem.
  1118.  
  1119. (***************************************************************************)
  1120.  
  1121.