This is a bug fix to the library module FIO to allow Close to return a
boolean indicating success. Most probably in the future there should be
a GetStatus procedure provided and the FileStatus enumeration inside
FIO.mod should also be exported.
gcc/m2/ChangeLog:
PR modula2/123151
* gm2-libs/FIO.def (IsError): New procedure function.
(IsActive): Rewrite the comment.
(Exists): Ditto.
(OpenToRead): Ditto.
(OpenToWrite): Ditto.
(Close): Add an optional BOOLEAN return result.
* gm2-libs/FIO.mod (Close): Reimplement with an
optional BOOLEAN return result.
gcc/testsuite/ChangeLog:
PR modula2/123151
* gm2/pimlib/base/run/pass/FIO.mod: Reimplement.
Copy from gm2-libs/FIO.mod since FIO.def api has changed.
* gm2/pimlib/run/pass/testclose.mod: New test.
Signed-off-by: Gaius Mulley <[email protected]>
---
gcc/m2/gm2-libs/FIO.def | 30 ++-
gcc/m2/gm2-libs/FIO.mod | 53 +++--
.../gm2/pimlib/base/run/pass/FIO.mod | 181 ++++++++++--------
.../gm2/pimlib/run/pass/testclose.mod | 41 ++++
4 files changed, 202 insertions(+), 103 deletions(-)
create mode 100644 gcc/testsuite/gm2/pimlib/run/pass/testclose.mod
diff --git a/gcc/m2/gm2-libs/FIO.def b/gcc/m2/gm2-libs/FIO.def
index 276536143fe..8f502dec3d4 100644
--- a/gcc/m2/gm2-libs/FIO.def
+++ b/gcc/m2/gm2-libs/FIO.def
@@ -35,7 +35,7 @@ EXPORT QUALIFIED (* types *)
File,
(* procedures *)
OpenToRead, OpenToWrite, OpenForRandom, Close,
- EOF, EOLN, WasEOLN, IsNoError, Exists, IsActive,
+ EOF, EOLN, WasEOLN, IsError, IsNoError, Exists, IsActive,
exists, openToRead, openToWrite, openForRandom,
SetPositionFromBeginning,
SetPositionFromEnd,
@@ -55,35 +55,42 @@ EXPORT QUALIFIED (* types *)
TYPE
File = CARDINAL ;
-(* the following variables are initialized to their UNIX equivalents *)
+(* The following variables are initialized to their UNIX equivalents. *)
VAR
StdIn, StdOut, StdErr: File ;
(*
- IsNoError - returns a TRUE if no error has occured on file, f.
+ IsNoError - returns TRUE if no error has occured on file f.
*)
PROCEDURE IsNoError (f: File) : BOOLEAN ;
(*
- IsActive - returns TRUE if the file, f, is still active.
+ IsError - returns TRUE if an error has occured on file f.
+*)
+
+PROCEDURE IsError (f: File) : BOOLEAN ;
+
+
+(*
+ IsActive - returns TRUE if the file f is still active.
*)
PROCEDURE IsActive (f: File) : BOOLEAN ;
(*
- Exists - returns TRUE if a file named, fname exists for reading.
+ Exists - returns TRUE if a file fname exists for reading.
*)
PROCEDURE Exists (fname: ARRAY OF CHAR) : BOOLEAN ;
(*
- OpenToRead - attempts to open a file, fname, for reading and
+ OpenToRead - attempts to open a file fname for reading and
it returns this file.
The success of this operation can be checked by
calling IsNoError.
@@ -93,7 +100,7 @@ PROCEDURE OpenToRead (fname: ARRAY OF CHAR) : File ;
(*
- OpenToWrite - attempts to open a file, fname, for write and
+ OpenToWrite - attempts to open a file fname for write and
it returns this file.
The success of this operation can be checked by
calling IsNoError.
@@ -103,7 +110,7 @@ PROCEDURE OpenToWrite (fname: ARRAY OF CHAR) : File ;
(*
- OpenForRandom - attempts to open a file, fname, for random access
+ OpenForRandom - attempts to open a file fname for random access
read or write and it returns this file.
The success of this operation can be checked by
calling IsNoError.
@@ -124,9 +131,14 @@ PROCEDURE OpenForRandom (fname: ARRAY OF CHAR;
Close - close a file which has been previously opened using:
OpenToRead, OpenToWrite, OpenForRandom.
It is correct to close a file which has an error status.
+ Close has an optional return value:
+ TRUE signifies that the close was successful and all
+ state associated with f is deallocated.
+ FALSE signifies that the close was unsuccessful and no
+ state associated with f has been deallocated.
*)
-PROCEDURE Close (f: File) ;
+PROCEDURE Close (f: File) : [BOOLEAN] ;
(* the following functions are functionally equivalent to the above
diff --git a/gcc/m2/gm2-libs/FIO.mod b/gcc/m2/gm2-libs/FIO.mod
index 8fa43e4b1c6..dbe84a60cf8 100644
--- a/gcc/m2/gm2-libs/FIO.mod
+++ b/gcc/m2/gm2-libs/FIO.mod
@@ -194,7 +194,7 @@ END GetNextFreeDescriptor ;
(*
- IsNoError - returns a TRUE if no error has occured on file, f.
+ IsNoError - returns a TRUE if no error has occured on file f.
*)
PROCEDURE IsNoError (f: File) : BOOLEAN ;
@@ -211,6 +211,24 @@ BEGIN
END IsNoError ;
+(*
+ IsError - returns a TRUE if an error has occured on file f.
+*)
+
+PROCEDURE IsError (f: File) : BOOLEAN ;
+VAR
+ fd: FileDescriptor ;
+BEGIN
+ IF f=Error
+ THEN
+ RETURN( FALSE )
+ ELSE
+ fd := GetIndice (FileInfo, f) ;
+ RETURN( (fd#NIL) AND ((fd^.state#successful) AND (fd^.state#endoffile)
AND (fd^.state#endofline)) )
+ END
+END IsError ;
+
+
(*
IsActive - returns TRUE if the file, f, is still active.
*)
@@ -474,28 +492,32 @@ END OpenForRandom ;
Close - close a file which has been previously opened using:
OpenToRead, OpenToWrite, OpenForRandom.
It is correct to close a file which has an error status.
+ Close has an optional return value:
+ TRUE signifies that the close was successful and all
+ state associated with f is deallocated.
+ FALSE signifies that the close was unsuccessful and no
+ state associated with f has been deallocated.
*)
-PROCEDURE Close (f: File) ;
+PROCEDURE Close (f: File) : [BOOLEAN] ;
VAR
fd: FileDescriptor ;
BEGIN
- IF f#Error
+ IF f # Error
THEN
- fd := GetIndice(FileInfo, f) ;
- (*
- we allow users to close files which have an error status
- *)
- IF fd#NIL
+ fd := GetIndice (FileInfo, f) ;
+ (* We allow users to close files which have an error status. *)
+ IF fd # NIL
THEN
- FlushBuffer(f) ;
+ FlushBuffer (f) ;
WITH fd^ DO
- IF unixfd>=0
+ IF unixfd >= 0
THEN
- IF close(unixfd)#0
+ IF close (unixfd) # 0
THEN
- FormatError1('failed to close file (%s)\n', name.address) ;
- state := failed (* --fixme-- too late to notify user
(unless we return a BOOLEAN) *)
+ FormatError1 ('failed to close file (%s)\n', name.address) ;
+ state := failed ;
+ RETURN FALSE
END
END ;
IF name.address#NIL
@@ -516,7 +538,10 @@ BEGIN
END ;
DISPOSE(fd) ;
PutIndice(FileInfo, f, NIL)
- END
+ END ;
+ RETURN TRUE
+ ELSE
+ RETURN FALSE
END
END Close ;
diff --git a/gcc/testsuite/gm2/pimlib/base/run/pass/FIO.mod
b/gcc/testsuite/gm2/pimlib/base/run/pass/FIO.mod
index 94a183301c3..dbe84a60cf8 100644
--- a/gcc/testsuite/gm2/pimlib/base/run/pass/FIO.mod
+++ b/gcc/testsuite/gm2/pimlib/base/run/pass/FIO.mod
@@ -1,6 +1,6 @@
(* FIO.mod provides a simple buffered file input/output library.
-Copyright (C) 2001-2023 Free Software Foundation, Inc.
+Copyright (C) 2001-2025 Free Software Foundation, Inc.
Contributed by Gaius Mulley <[email protected]>.
This file is part of GNU Modula-2.
@@ -36,23 +36,21 @@ IMPLEMENTATION MODULE FIO ;
provides a simple buffered file input/output library.
*)
-FROM SYSTEM IMPORT ADR, TSIZE, SIZE, WORD ;
+FROM SYSTEM IMPORT ADR, TSIZE, WORD, COFF_T ;
FROM ASCII IMPORT nl, nul, tab ;
FROM StrLib IMPORT StrLen, StrConCat, StrCopy ;
FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
FROM NumberIO IMPORT CardToStr ;
-FROM libc IMPORT exit, open, creat, read, write, close, lseek, strncpy, memcpy
;
FROM Indexing IMPORT Index, InitIndex, InBounds, HighIndice, PutIndice,
GetIndice ;
FROM M2RTS IMPORT InstallTerminationProcedure ;
+FROM libc IMPORT exit, open, creat, read, write, close, lseek, strncpy, memcpy
;
+FROM wrapc IMPORT SeekSet, SeekEnd, ReadOnly, WriteOnly ;
+
CONST
- SEEK_SET = 0 ; (* relative from beginning of the file *)
- SEEK_END = 2 ; (* relative to the end of the file *)
- UNIXREADONLY = 0 ;
- UNIXWRITEONLY = 1 ;
- CreatePermissions = 666B;
MaxBufferLength = 1024*16 ;
MaxErrorString = 1024* 8 ;
+ CreatePermissions = 666B;
TYPE
FileUsage = (unused, openedforread, openedforwrite,
openedforrandom) ;
@@ -196,7 +194,7 @@ END GetNextFreeDescriptor ;
(*
- IsNoError - returns a TRUE if no error has occured on file, f.
+ IsNoError - returns a TRUE if no error has occured on file f.
*)
PROCEDURE IsNoError (f: File) : BOOLEAN ;
@@ -213,6 +211,24 @@ BEGIN
END IsNoError ;
+(*
+ IsError - returns a TRUE if an error has occured on file f.
+*)
+
+PROCEDURE IsError (f: File) : BOOLEAN ;
+VAR
+ fd: FileDescriptor ;
+BEGIN
+ IF f=Error
+ THEN
+ RETURN( FALSE )
+ ELSE
+ fd := GetIndice (FileInfo, f) ;
+ RETURN( (fd#NIL) AND ((fd^.state#successful) AND (fd^.state#endoffile)
AND (fd^.state#endofline)) )
+ END
+END IsError ;
+
+
(*
IsActive - returns TRUE if the file, f, is still active.
*)
@@ -428,10 +444,10 @@ BEGIN
THEN
unixfd := creat(name.address, CreatePermissions)
ELSE
- unixfd := open(name.address, UNIXWRITEONLY, 0)
+ unixfd := open(name.address, INTEGER (WriteOnly ()), 0)
END
ELSE
- unixfd := open(name.address, UNIXREADONLY, 0)
+ unixfd := open(name.address, INTEGER (ReadOnly ()), 0)
END ;
IF unixfd<0
THEN
@@ -476,28 +492,32 @@ END OpenForRandom ;
Close - close a file which has been previously opened using:
OpenToRead, OpenToWrite, OpenForRandom.
It is correct to close a file which has an error status.
+ Close has an optional return value:
+ TRUE signifies that the close was successful and all
+ state associated with f is deallocated.
+ FALSE signifies that the close was unsuccessful and no
+ state associated with f has been deallocated.
*)
-PROCEDURE Close (f: File) ;
+PROCEDURE Close (f: File) : [BOOLEAN] ;
VAR
fd: FileDescriptor ;
BEGIN
- IF f#Error
+ IF f # Error
THEN
- fd := GetIndice(FileInfo, f) ;
- (*
- we allow users to close files which have an error status
- *)
- IF fd#NIL
+ fd := GetIndice (FileInfo, f) ;
+ (* We allow users to close files which have an error status. *)
+ IF fd # NIL
THEN
- FlushBuffer(f) ;
+ FlushBuffer (f) ;
WITH fd^ DO
- IF unixfd>=0
+ IF unixfd >= 0
THEN
- IF close(unixfd)#0
+ IF close (unixfd) # 0
THEN
- FormatError1('failed to close file (%s)\n', name.address) ;
- state := failed (* --fixme-- too late to notify user
(unless we return a BOOLEAN) *)
+ FormatError1 ('failed to close file (%s)\n', name.address) ;
+ state := failed ;
+ RETURN FALSE
END
END ;
IF name.address#NIL
@@ -518,7 +538,10 @@ BEGIN
END ;
DISPOSE(fd) ;
PutIndice(FileInfo, f, NIL)
- END
+ END ;
+ RETURN TRUE
+ ELSE
+ RETURN FALSE
END
END Close ;
@@ -664,10 +687,9 @@ END ReadNBytes ;
Useful when performing small reads.
*)
-PROCEDURE BufferedRead (f: File; nBytes: CARDINAL; a: ADDRESS) : INTEGER ;
+PROCEDURE BufferedRead (f: File; nBytes: CARDINAL; dest: ADDRESS) : INTEGER ;
VAR
- t : ADDRESS ;
- result: INTEGER ;
+ src : ADDRESS ;
total,
n : INTEGER ;
p : POINTER TO BYTE ;
@@ -675,52 +697,52 @@ VAR
BEGIN
IF f#Error
THEN
- fd := GetIndice(FileInfo, f) ;
+ fd := GetIndice (FileInfo, f) ;
total := 0 ; (* how many bytes have we read *)
IF fd#NIL
THEN
WITH fd^ DO
(* extract from the buffer first *)
- IF buffer#NIL
+ IF buffer # NIL
THEN
WITH buffer^ DO
- WHILE nBytes>0 DO
- IF (left>0) AND valid
+ WHILE nBytes > 0 DO
+ IF (left > 0) AND valid
THEN
- IF nBytes=1
+ IF nBytes = 1
THEN
(* too expensive to call memcpy for 1 character *)
- p := a ;
+ p := dest ;
p^ := contents^[position] ;
- DEC(left) ; (* remove consumed byte
*)
- INC(position) ; (* move onwards n byte
*)
- INC(total) ;
+ DEC (left) ; (* remove consumed byte
*)
+ INC (position) ; (* move onwards n byte
*)
+ INC (total) ;
RETURN( total )
ELSE
- n := Min(left, nBytes) ;
- t := address ;
- INC(t, position) ;
- p := memcpy(a, t, n) ;
- DEC(left, n) ; (* remove consumed bytes
*)
- INC(position, n) ; (* move onwards n bytes
*)
+ n := Min (left, nBytes) ;
+ src := address ;
+ INC (src, position) ;
+ p := memcpy (dest, src, n) ;
+ DEC (left, n) ; (* remove consumed bytes
*)
+ INC (position, n) ; (* move onwards n bytes
*)
(* move onwards ready for
direct reads *)
- INC(a, n) ;
- DEC(nBytes, n) ; (* reduce the amount for future
direct *)
+ INC (dest, n) ;
+ DEC (nBytes, n) ; (* reduce the amount for
future direct *)
(* read
*)
- INC(total, n)
+ INC (total, n)
END
ELSE
(* refill buffer *)
- n := read(unixfd, address, size) ;
- IF n>=0
+ n := read (unixfd, address, size) ;
+ IF n >= 0
THEN
valid := TRUE ;
position := 0 ;
left := n ;
filled := n ;
bufstart := abspos ;
- INC(abspos, n) ;
- IF n=0
+ INC (abspos, n) ;
+ IF n = 0
THEN
(* eof reached *)
state := endoffile ;
@@ -1084,7 +1106,7 @@ END UnReadChar ;
(*
- ReadAny - reads HIGH(a) bytes into, a. All input
+ ReadAny - reads HIGH (a) + 1 bytes into, a. All input
is fully buffered, unlike ReadNBytes and thus is more
suited to small reads.
*)
@@ -1092,9 +1114,9 @@ END UnReadChar ;
PROCEDURE ReadAny (f: File; VAR a: ARRAY OF BYTE) ;
BEGIN
CheckAccess(f, openedforread, FALSE) ;
- IF BufferedRead (f, HIGH (a), ADR (a)) = VAL (INTEGER, HIGH (a))
+ IF BufferedRead (f, HIGH (a) + 1, ADR (a)) = VAL (INTEGER, HIGH (a) + 1)
THEN
- SetEndOfLine(f, a[HIGH(a)])
+ SetEndOfLine (f, a[HIGH(a)])
END
END ReadAny ;
@@ -1233,52 +1255,51 @@ END WriteNBytes ;
Useful when performing small writes.
*)
-PROCEDURE BufferedWrite (f: File; nBytes: CARDINAL; a: ADDRESS) : INTEGER ;
+PROCEDURE BufferedWrite (f: File; nBytes: CARDINAL; src: ADDRESS) : INTEGER ;
VAR
- t : ADDRESS ;
- result: INTEGER ;
+ dest : ADDRESS ;
total,
n : INTEGER ;
p : POINTER TO BYTE ;
fd : FileDescriptor ;
BEGIN
- IF f#Error
+ IF f # Error
THEN
- fd := GetIndice(FileInfo, f) ;
+ fd := GetIndice (FileInfo, f) ;
IF fd#NIL
THEN
total := 0 ; (* how many bytes have we read *)
WITH fd^ DO
- IF buffer#NIL
+ IF buffer # NIL
THEN
WITH buffer^ DO
- WHILE nBytes>0 DO
+ WHILE nBytes > 0 DO
(* place into the buffer first *)
- IF left>0
+ IF left > 0
THEN
- IF nBytes=1
+ IF nBytes = 1
THEN
(* too expensive to call memcpy for 1 character *)
- p := a ;
+ p := src ;
contents^[position] := p^ ;
- DEC(left) ; (* reduce space
*)
- INC(position) ; (* move onwards n byte
*)
- INC(total) ;
+ DEC (left) ; (* reduce space
*)
+ INC (position) ; (* move onwards n byte
*)
+ INC (total) ;
RETURN( total )
ELSE
- n := Min(left, nBytes) ;
- t := address ;
- INC(t, position) ;
- p := memcpy(a, t, CARDINAL(n)) ;
- DEC(left, n) ; (* remove consumed bytes
*)
- INC(position, n) ; (* move onwards n bytes
*)
- (* move ready for further
writes *)
- INC(a, n) ;
- DEC(nBytes, n) ; (* reduce the amount for future
writes *)
- INC(total, n)
+ n := Min (left, nBytes) ;
+ dest := address ;
+ INC (dest, position) ;
+ p := memcpy (dest, src, CARDINAL (n)) ;
+ DEC (left, n) ; (* remove consumed bytes
*)
+ INC (position, n) ; (* move onwards n bytes
*)
+ (* move ready for further
writes *)
+ INC (src, n) ;
+ DEC (nBytes, n) ; (* reduce the amount for
future writes *)
+ INC (total, n)
END
ELSE
- FlushBuffer(f) ;
+ FlushBuffer (f) ;
IF (state#successful) AND (state#endofline)
THEN
nBytes := 0
@@ -1331,7 +1352,7 @@ END FlushBuffer ;
(*
- WriteAny - writes HIGH(a) bytes onto, file, f. All output
+ WriteAny - writes HIGH (a) + 1 bytes onto, file, f. All output
is fully buffered, unlike WriteNBytes and thus is more
suited to small writes.
*)
@@ -1339,7 +1360,7 @@ END FlushBuffer ;
PROCEDURE WriteAny (f: File; VAR a: ARRAY OF BYTE) ;
BEGIN
CheckAccess (f, openedforwrite, TRUE) ;
- IF BufferedWrite (f, HIGH (a), ADR (a)) = VAL (INTEGER, HIGH (a))
+ IF BufferedWrite (f, HIGH (a) + 1, ADR (a)) = VAL (INTEGER, HIGH (a) + 1)
THEN
END
END WriteAny ;
@@ -1450,7 +1471,7 @@ BEGIN
filled := 0
END
END ;
- offset := lseek(unixfd, pos, SEEK_SET) ;
+ offset := lseek (unixfd, VAL (COFF_T, pos), SeekSet ()) ;
IF (offset>=0) AND (pos=offset)
THEN
abspos := pos
@@ -1499,7 +1520,7 @@ BEGIN
filled := 0
END
END ;
- offset := lseek(unixfd, pos, SEEK_END) ;
+ offset := lseek (unixfd, VAL (COFF_T, pos), SeekEnd ()) ;
IF offset>=0
THEN
abspos := offset ;
diff --git a/gcc/testsuite/gm2/pimlib/run/pass/testclose.mod
b/gcc/testsuite/gm2/pimlib/run/pass/testclose.mod
new file mode 100644
index 00000000000..35794e5fbc2
--- /dev/null
+++ b/gcc/testsuite/gm2/pimlib/run/pass/testclose.mod
@@ -0,0 +1,41 @@
+MODULE testclose ;
+
+IMPORT FIO ;
+IMPORT libc ;
+
+
+(*
+ assert -
+*)
+
+PROCEDURE assert (condition: BOOLEAN; line: CARDINAL) ;
+BEGIN
+ IF NOT condition
+ THEN
+ libc.printf ("%s:%d:assert failed\n", __FILE__, line) ;
+ libc.exit (1)
+ END
+END assert ;
+
+
+(*
+ Init -
+*)
+
+PROCEDURE Init ;
+VAR
+ f: FIO.File ;
+BEGIN
+ f := FIO.OpenToWrite ('testclose.txt') ;
+ assert (FIO.IsNoError (f), __LINE__) ;
+ FIO.WriteString (f, 'hello') ;
+ assert (FIO.IsNoError (f), __LINE__) ;
+ FIO.WriteLine (f) ;
+ assert (FIO.IsNoError (f), __LINE__) ;
+ assert (FIO.Close (f), __LINE__)
+END Init ;
+
+
+BEGIN
+ Init
+END testclose.
--
2.47.3