1 (* RndFile.mod implement the ISO RndFile specification.
3 Copyright (C) 2008-2024 Free Software Foundation, Inc.
4 Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
6 This file is part of GNU Modula-2.
8 GNU Modula-2 is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3, or (at your option)
13 GNU Modula-2 is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 <http://www.gnu.org/licenses/>. *)
27 IMPLEMENTATION MODULE RndFile
;
30 FROM RTgen
IMPORT ChanDev
, DeviceType
,
31 InitChanDev
, doLook
, doSkip
, doSkipLook
, doWriteLn
,
32 doReadText
, doWriteText
, doReadLocs
, doWriteLocs
,
35 FROM RTfio
IMPORT doreadchar
, dounreadchar
, dogeterrno
, dorbytes
,
36 dowbytes
, dowriteln
, iseof
, iseoln
, iserror
;
38 FROM IOLink
IMPORT DeviceId
, DeviceTablePtr
, IsDevice
, MakeChan
, UnMakeChan
,
39 DeviceTablePtrValue
, RAISEdevException
, AllocateDeviceId
,
42 FROM Storage
IMPORT ALLOCATE
, DEALLOCATE
;
43 FROM RTgenif
IMPORT GenDevIF
, InitGenDevIF
;
44 FROM FIO
IMPORT File
;
45 FROM libc
IMPORT memcpy
;
46 FROM errno
IMPORT geterrno
;
47 FROM IOConsts
IMPORT ReadResults
;
48 FROM ChanConsts
IMPORT readFlag
, writeFlag
;
50 FROM EXCEPTIONS
IMPORT ExceptionNumber
, RAISE
,
51 AllocateSource
, ExceptionSource
, IsCurrentSource
,
52 IsExceptionalExecution
;
54 IMPORT FIO
, RTio
, errno
, ErrnoCategory
;
60 rndfileException
: ExceptionSource
;
63 PROCEDURE look (d
: DeviceTablePtr
;
64 VAR ch
: CHAR; VAR r
: ReadResults
) ;
71 PROCEDURE skip (d
: DeviceTablePtr
) ;
77 PROCEDURE skiplook (d
: DeviceTablePtr
;
78 VAR ch
: CHAR; VAR r
: ReadResults
) ;
81 doSkipLook(dev
, d
, ch
, r
)
85 PROCEDURE lnwrite (d
: DeviceTablePtr
) ;
92 PROCEDURE textread (d
: DeviceTablePtr
;
95 VAR charsRead
: CARDINAL) ;
98 doReadText(dev
, d
, to
, maxChars
, charsRead
)
102 PROCEDURE textwrite (d
: DeviceTablePtr
;
103 from
: SYSTEM.ADDRESS
;
104 charsToWrite
: CARDINAL);
107 doWriteText(dev
, d
, from
, charsToWrite
)
111 PROCEDURE rawread (d
: DeviceTablePtr
;
114 VAR locsRead
: CARDINAL) ;
117 doReadLocs(dev
, d
, to
, maxLocs
, locsRead
)
121 PROCEDURE rawwrite (d
: DeviceTablePtr
;
122 from
: SYSTEM.ADDRESS
;
123 locsToWrite
: CARDINAL) ;
126 doWriteLocs(dev
, d
, from
, locsToWrite
)
130 PROCEDURE getname (d
: DeviceTablePtr
;
131 VAR a
: ARRAY OF CHAR) ;
133 FIO.
GetFileName(RTio.
GetFile(d^.cid
), a
)
137 PROCEDURE flush (d
: DeviceTablePtr
) ;
139 FIO.
FlushBuffer(RTio.
GetFile(d^.cid
))
144 checkOpenErrno - assigns, e, and, res, depending upon file result of opening,
148 PROCEDURE checkOpenErrno (file
: FIO.File
; VAR e
: INTEGER; VAR res
: OpenResults
) ;
150 IF FIO.
IsNoError(file
)
154 e
:= errno.
geterrno()
156 res
:= ErrnoCategory.
GetOpenResults(e
)
161 checkRW - ensures that the file attached to, p, has been opened, towrite.
164 PROCEDURE checkRW (towrite
: BOOLEAN; p
: DeviceTablePtr
) ;
166 pb
: POINTER TO BOOLEAN ;
169 name
: SYSTEM.ADDRESS
;
171 contents
: SYSTEM.ADDRESS
;
178 fp
:= CurrentPos(cid
) ;
179 file
:= RTio.
GetFile(RTio.
ChanId(cid
)) ;
180 name
:= FIO.
getFileName(file
) ;
181 size
:= FIO.
getFileNameLength(file
) ;
182 ALLOCATE(contents
, size
+1) ;
183 contents
:= memcpy(contents
, name
, size
) ;
185 file
:= FIO.
openForRandom(contents
, size
, towrite
, FALSE) ;
186 RTio.
SetFile(cid
, file
) ;
188 DEALLOCATE(contents
, size
+1)
195 newCid - returns a ChanId which represents the opened file, name.
196 res is set appropriately on return.
199 PROCEDURE newCid (fname
: ARRAY OF CHAR;
201 VAR res
: OpenResults
;
202 toWrite
, newfile
: BOOLEAN;
203 whichreset
: ResetProc
) : ChanId
;
209 pb
: POINTER TO BOOLEAN ;
211 file
:= FIO.
OpenForRandom(fname
, toWrite
, newfile
) ;
212 checkOpenErrno(file
, e
, res
) ;
214 IF FIO.
IsNoError(file
)
219 RTio.
SetFile(c
, file
) ;
220 p
:= DeviceTablePtrValue(c
, did
) ;
227 doSkipLook
:= skiplook
;
228 doLnWrite
:= lnwrite
;
229 doTextRead
:= textread
;
230 doTextWrite
:= textwrite
;
231 doRawRead
:= rawread
;
232 doRawWrite
:= rawwrite
;
233 doGetName
:= getname
;
234 doReset
:= whichreset
;
240 RETURN( IOChan.
InvalidChan() )
249 PROCEDURE handlefree (d
: DeviceTablePtr
) ;
252 pb
: POINTER TO BOOLEAN ;
257 f
:= RTio.
GetFile(RTio.
ChanId(cid
)) ;
270 PROCEDURE resetRandom (d
: DeviceTablePtr
) ;
275 (* --fixme --, finish this *)
277 RAISEdevException(cid
, did
, IOChan.wrongDevice
,
278 'RndFile.' + __FUNCTION__
+
279 ': channel is not a random file')
285 PROCEDURE OpenOld (VAR cid
: ChanId
; name
: ARRAY OF CHAR; flags
: FlagSet
;
286 VAR res
: OpenResults
);
287 (* Attempts to obtain and open a channel connected to a stored random
288 access file of the given name.
289 The old flag is implied; without the write flag, read is implied;
290 without the text flag, raw is implied.
291 If successful, assigns to cid the identity of the opened channel,
292 assigns the value opened to res, and sets the read/write position
293 to the start of the file.
294 If a channel cannot be opened as required, the value of res indicates
295 the reason, and cid identifies the invalid channel.
298 INCL(flags
, ChanConsts.oldFlag
) ;
299 IF NOT (ChanConsts.writeFlag
IN flags
)
301 INCL(flags
, ChanConsts.readFlag
)
303 IF NOT (ChanConsts.textFlag
IN flags
)
305 INCL(flags
, ChanConsts.rawFlag
)
307 cid
:= newCid(name
, flags
, res
, FALSE, FALSE, resetRandom
)
311 PROCEDURE OpenClean (VAR cid
: ChanId
; name
: ARRAY OF CHAR; flags
: FlagSet
;
312 VAR res
: OpenResults
);
313 (* Attempts to obtain and open a channel connected to a stored random
314 access file of the given name.
315 The write flag is implied; without the text flag, raw is implied.
316 If successful, assigns to cid the identity of the opened channel,
317 assigns the value opened to res, and truncates the file to zero length.
318 If a channel cannot be opened as required, the value of res indicates
319 the reason, and cid identifies the invalid channel.
322 INCL(flags
, ChanConsts.writeFlag
) ;
323 IF NOT (ChanConsts.textFlag
IN flags
)
325 INCL(flags
, ChanConsts.rawFlag
)
327 cid
:= newCid(name
, flags
, res
, TRUE, TRUE, resetRandom
)
331 PROCEDURE IsRndFile (cid
: ChanId
): BOOLEAN;
332 (* Tests if the channel identified by cid is open to a random access file. *)
334 RETURN( (cid #
NIL) AND (IOChan.
InvalidChan() # cid
) AND
335 (IsDevice(cid
, did
)) AND
336 ((ChanConsts.readFlag
IN IOChan.
CurrentFlags(cid
)) OR
337 (ChanConsts.writeFlag
IN IOChan.
CurrentFlags(cid
))) )
341 PROCEDURE IsRndFileException (): BOOLEAN;
342 (* Returns TRUE if the current coroutine is in the exceptional execution
343 state because of the raising of a RndFile exception; otherwise returns
347 RETURN( IsCurrentSource (rndfileException
) )
348 END IsRndFileException
;
352 PROCEDURE StartPos (cid
: ChanId
): FilePos
;
353 (* If the channel identified by cid is not open to a random access file,
354 the exception wrongDevice is raised; otherwise returns the position of
355 the start of the file.
362 d
:= DeviceTablePtrValue(cid
, did
) ;
365 RAISEdevException(cid
, did
, IOChan.wrongDevice
,
366 'RndFile.' + __FUNCTION__
+
367 ': channel is not a random file')
372 PROCEDURE CurrentPos (cid
: ChanId
): FilePos
;
373 (* If the channel identified by cid is not open to a random access file,
374 the exception wrongDevice is raised; otherwise returns the position
375 of the current read/write position.
382 d
:= DeviceTablePtrValue(cid
, did
) ;
384 RETURN( FIO.
FindPosition(RTio.
GetFile(cid
)) )
387 RAISEdevException(cid
, did
, IOChan.wrongDevice
,
388 'RndFile.' + __FUNCTION__
+
389 ': channel is not a random file')
394 PROCEDURE EndPos (cid
: ChanId
): FilePos
;
395 (* If the channel identified by cid is not open to a random access file,
396 the exception wrongDevice is raised; otherwise returns the first
397 position after which there have been no writes.
406 d
:= DeviceTablePtrValue(cid
, did
) ;
407 old
:= CurrentPos(cid
) ;
409 old
:= CurrentPos(cid
) ;
410 FIO.
SetPositionFromEnd(RTio.
GetFile(cid
), 0) ;
412 end
:= CurrentPos(cid
) ;
413 FIO.
SetPositionFromBeginning(RTio.
GetFile(cid
), old
) ;
417 RAISEdevException(cid
, did
, IOChan.wrongDevice
,
418 'RndFile.' + __FUNCTION__
+
419 ': channel is not a random file')
424 PROCEDURE NewPos (cid
: ChanId
; chunks
: INTEGER; chunkSize
: CARDINAL;
425 from
: FilePos
): FilePos
;
426 (* If the channel identified by cid is not open to a random access file,
427 the exception wrongDevice is raised; otherwise returns the position
428 (chunks * chunkSize) relative to the position given by from, or
429 raises the exception posRange if the required position cannot be
430 represented as a value of type FilePos.
437 d
:= DeviceTablePtrValue(cid
, did
) ;
439 RETURN( from
+VAL(FilePos
, chunks
*VAL(INTEGER, chunkSize
))-
443 RAISEdevException(cid
, did
, IOChan.wrongDevice
,
444 'RndFile.' + __FUNCTION__
+
445 ': channel is not a random file')
450 PROCEDURE SetPos (cid
: ChanId
; pos
: FilePos
);
451 (* If the channel identified by cid is not open to a random access file,
452 the exception wrongDevice is raised; otherwise sets the read/write
453 position to the value given by pos.
460 d
:= DeviceTablePtrValue(cid
, did
) ;
462 FIO.
SetPositionFromBeginning(RTio.
GetFile(cid
), pos
) ;
466 RAISEdevException(cid
, did
, IOChan.wrongDevice
,
467 'RndFile.' + __FUNCTION__
+
468 ': channel is not a random file')
473 PROCEDURE Close (VAR cid
: ChanId
);
474 (* If the channel identified by cid is not open to a random access file,
475 the exception wrongDevice is raised; otherwise closes the channel,
476 and assigns the value identifying the invalid channel to cid.
481 UnMakeChan(did
, cid
) ;
482 cid
:= IOChan.
InvalidChan()
484 RAISEdevException(cid
, did
, IOChan.wrongDevice
,
485 'RndFile.' + __FUNCTION__
+
486 ': channel is not a random file')
499 AllocateDeviceId(did
) ;
500 gen
:= InitGenDevIF(did
, doreadchar
, dounreadchar
,
501 dogeterrno
, dorbytes
, dowbytes
,
503 iseof
, iseoln
, iserror
) ;
504 dev
:= InitChanDev(streamfile
, did
, gen
) ;
505 AllocateSource (rndfileException
)