1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
|
(* $Id: PosixFileDescr.Mod,v 1.10 1999/10/31 13:58:05 ooc-devel Exp $ *)
MODULE PosixFileDescr [FOREIGN "C"; LINK FILE "PosixFileDescr.c" END];
(* Generalized access to POSIX-style file descriptors.
Copyright (C) 1997-1999 Michael van Acken
This module is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This module 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with OOC. If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*)
<* Warnings := FALSE; ConformantMode := FALSE *>
(*
Warning:
This module is not part of the "official" OOC suit of library modules. It
provides an abstract class containing the features that are used by all channel
implementations based on Unix-style file descriptors. It will not be available
for all implementations of OOC. Usage of of this module should be restricted
to derived modules like StdChannels or Files. It should never be used directly
by a programmer.
*)
IMPORT
SYSTEM, C, Time, CharClass, Ch := Channel, Msg, LongStrings;
TYPE
Result* = Ch.Result;
CONST (* NOTE: refer to module Channel for the meaning of the various codes *)
noLength* = Ch.noLength;
noPosition* = Ch.noPosition;
(* the following values may appear in the `res' (or `res.code')
field of `Channel', `Reader', or `Writer': *)
done* = Ch.done;
invalidChannel* = Ch.invalidChannel;
writeError* = Ch.writeError;
noRoom* = Ch.noRoom;
(* symbolic values for error codes in `Reader.res' resp. `Writer.res': *)
outOfRange* = Ch.outOfRange;
readAfterEnd* = Ch.readAfterEnd;
channelClosed* = Ch.channelClosed;
readError* = Ch.readError;
invalidFormat* = Ch.invalidFormat;
(* symbolic values for error code in `Ch.res': *)
noReadAccess* = Ch.noReadAccess;
noWriteAccess* = Ch.noWriteAccess;
closeError* = Ch.closeError;
noModTime* = Ch.noModTime;
noTmpName* = Ch.noTmpName;
freeErrorCode* = Ch.freeErrorCode;
CONST (* values for field `buffering parameter `mode' of procedure `Init': *)
readOnly* = 0;
writeOnly* = 1;
readWrite* = 2;
CONST (* standard file descriptor ids *)
stdinFileno* = 0;
stdoutFileno* = 1;
stderrFileno* = 2;
CONST (* accepted values for parameter `buffering' in `ChannelDesc: *)
noBuffer* = 0;
lineBuffer* = 1; (* only applicable to terminals *)
blockBuffer* = 2;
TYPE
Channel* = POINTER TO ChannelDesc;
Reader* = POINTER TO ReaderDesc;
Writer* = POINTER TO WriterDesc;
TYPE
ChannelDesc* = RECORD
(Ch.ChannelDesc)
fd-: C.int;
(* file descriptor; set with the Init procedure *)
pos: C.int;
(* current reading/writing position of the channel; this may differ from
a reader or writer position if more than one of them are attached *)
positionable: BOOLEAN;
(* TRUE iff Length and SetPos are available for the attached riders *)
append: BOOLEAN;
(* TRUE iff writers will always append to the file *)
dirty: BOOLEAN;
(* TRUE iff buffer needs to be written back; this also serves to
distinguish between the buffers access mode: TRUE means write, FALSE
is read *)
buffering: SHORTINT;
(* mode of buffering (none, line, block); for reading line buffering is
only applicable for canonical terminal input *)
buf: POINTER TO ARRAY [NO_LENGTH_INFO, NO_DESCRIPTOR] OF CHAR;
sizeBuffer: LONGINT;
(* buffer of length sizeBuffer; sizeBuffer=0 is equivalent to buf=NIL *)
bufStart,
bufEnd: LONGINT;
(* describe the interval for which the buffer holds valid data from the
file; the interval contains the character at bufStart, but excludes
the one at bufEnd, i.e., it is [bufStart..bufEnd[; bufStart<=bufEnd
always holds, bufStart=bufEnd means an empty buffer; a dirty buffer
is never emtpy *)
reader: Reader;
(* holds the single reader if the channel isn't positionable *)
writer: Writer;
(* holds the single writer if the channel isn't positionable *)
END;
ReaderDesc* = RECORD
(Ch.ReaderDesc)
pos: C.int;
END;
WriterDesc* = RECORD
(Ch.WriterDesc)
pos: C.int;
END;
TYPE
FileDescriptor = C.int;
VAR
(* these variables have nothing to do with file descriptors; since nobody
should use this module anyway I stuck them here; they are used by the
module ProgramArgs *)
argc- ["_program_argc"]: C.int;
argv- ["_program_argv"]: C.charPtr2d;
TYPE
ErrorContext = POINTER TO ErrorContextDesc;
ErrorContextDesc* = RECORD
(* this record is exported, so that extensions of Channel can access the
error descriptions by extending `ErrorContextDesc' *)
(Ch.ErrorContextDesc)
END;
PROCEDURE (context: ErrorContext) GetTemplate* (msg: Msg.Msg; VAR templ: Msg.LString);
PROCEDURE InitReader* (r: Reader; ch: Channel);
PROCEDURE InitWriter* (w: Writer; ch: Channel);
(* Reader methods
------------------------------------------------------------------------ *)
PROCEDURE (r: Reader) Pos*(): LONGINT;
(* Returns the current reading position associated with the reader `r' in
channel `r.base', i.e. the index of the first byte that is read by the
next call to ReadByte resp. ReadBytes. This procedure will return
`noPosition' if the reader has no concept of a reading position (e.g. if it
corresponds to input from keyboard), otherwise the result is not negative.*)
PROCEDURE (r: Reader) Available*(): LONGINT;
(* Returns the number of bytes available for the next reading operation. For
a file this is the length of the channel `r.base' minus the current reading
position, for an sequential channel (or a channel designed to handle slow
transfer rates) this is the number of bytes that can be accessed without
additional waiting. The result is -1 if the channel has been closed.
Note that the number of bytes returned is always a lower approximation of
the number that could be read at once; for some channels or systems it might
be as low as 1 even if tons of bytes are waiting to be processed. *)
PROCEDURE (r: Reader) SetPos* (newPos: LONGINT);
(* Sets the reading position to `newPos'. A negative value of `newPos' or
calling this procedure for a reader that doesn't allow positioning will set
`r.res' to `outOfRange'. A value larger than the channel's length is legal,
but the following read operation will most likely fail with an
`readAfterEnd' error unless the channel has grown beyond this position in
the meantime.
Calls to this procedure while `r.res # done' will be ignored, in particular
a call with `r.res = readAfterEnd' error will not reset `res' to `done'. *)
PROCEDURE (r: Reader) ReadByte* (VAR x: SYSTEM.BYTE);
(* Reads a single byte from the channel `r.base' at the reading position
associated with `r' and places it in `x'. The reading position is moved
forward by one byte on success, otherwise `r.res' is changed to indicate
the error cause. Calling this procedure with the reader `r' placed at the
end (or beyond the end) of the channel will set `r.res' to `readAfterEnd'.
`r.bytesRead' will be 1 on success and 0 on failure.
Calls to this procedure while `r.res # done' will be ignored. *)
PROCEDURE (r: Reader) ReadBytes* (VAR x: ARRAY OF SYSTEM.BYTE;
start, n: LONGINT);
(* Reads `n' bytes from the channel `r.base' at the reading position associated
with `r' and places them in `x', starting at index `start'. The
reading position is moved forward by `n' bytes on success, otherwise
`r.res' is changed to indicate the error cause. Calling this procedure with
the reader `r' placed less than `n' bytes before the end of the channel will
will set `r.res' to `readAfterEnd'. `r.bytesRead' will hold the number of
bytes that were actually read (being equal to `n' on success).
Calls to this procedure while `r.res # done' will be ignored.
pre: (n >= 0) & (0 <= start) & (start+n <= LEN (x)) *)
(* Writer methods
------------------------------------------------------------------------ *)
PROCEDURE (w: Writer) Pos*(): LONGINT;
(* Returns the current writing position associated with the writer `w' in
channel `w.base', i.e. the index of the first byte that is written by the
next call to WriteByte resp. WriteBytes. This procedure will return
`noPosition' if the writer has no concept of a writing position (e.g. if it
corresponds to output to terminal), otherwise the result is not negative. *)
PROCEDURE (w: Writer) SetPos* (newPos: LONGINT);
(* Sets the writing position to `newPos'. A negative value of `newPos' or
calling this procedure for a writer that doesn't allow positioning will set
`w.res' to `outOfRange'. A value larger than the channel's length is legal,
the following write operation will fill the gap between the end of the
channel and this position with zero bytes.
Calls to this procedure while `w.res # done' will be ignored. *)
PROCEDURE (w: Writer) WriteByte* (x: SYSTEM.BYTE);
(* Writes a single byte `x' to the channel `w.base' at the writing position
associated with `w'. The writing position is moved forward by one byte on
success, otherwise `w.res' is changed to indicate the error cause.
`w.bytesWritten' will be 1 on success and 0 on failure.
Calls to this procedure while `w.res # done' will be ignored. *)
PROCEDURE (w: Writer) WriteBytes* (VAR x: ARRAY OF SYSTEM.BYTE; start, n: LONGINT);
(* Writes `n' bytes from `x', starting at position `start', to the channel
`w.base' at the writing position associated with `w'. The writing position
is moved forward by `n' bytes on success, otherwise `w.res' is changed to
indicate the error cause. `w.bytesWritten' will hold the number of bytes
that were actually written (being equal to `n' on success).
Calls to this procedure while `w.res # done' will be ignored.
pre: (n >= 0) & (0 <= start) & (start+n <= LEN (x)) *)
(* Channel methods
------------------------------------------------------------------------ *)
PROCEDURE (ch: Channel) Length*(): LONGINT;
(* Result is the number of bytes of data that this channel refers to. If `ch'
represents a file, then this value is the file's size. If `ch' has no fixed
length (e.g. because it's interactive), the result is `noLength'. *)
PROCEDURE (ch: Channel) GetModTime* (VAR mtime: Time.TimeStamp);
(* Retrieves the modification time of the data accessed by the given channel.
If no such information is avaiblable, `ch.res' is set to `noModTime',
otherwise to `done'. *)
PROCEDURE (ch: Channel) NewReader*(): Reader;
(* Attaches a new reader to the channel `ch'. It is placed at the very start
of the channel, and its `res' field is initialized to `done'. `ch.res' is
set to `done' on success and the new reader is returned. Otherwise result
is NIL and `ch.res' is changed to indicate the error cause.
Note that always the same reader is returned if the channel does not support
multiple reading positions. *)
PROCEDURE (ch: Channel) NewWriter*(): Writer;
(* Attaches a new writer to the channel `ch'. It is placed at the very start
of the channel, and its `res' field is initialized to `done'. `ch.res' is
set to `done' on success and the new writer is returned. Otherwise result
is NIL and `ch.res' is changed to indicate the error cause.
Note that always the same reader is returned if the channel does not support
multiple writing positions. *)
PROCEDURE (ch: Channel) Flush*;
(* Flushes all buffers related to this channel. Any pending write operations
are passed to the underlying OS and all buffers are marked as invalid. The
next read operation will get its data directly from the channel instead of
the buffer. If a writing error occurs during flushing, the field `ch.res'
will be changed to `writeError', otherwise it's assigned `done'. Note that
you have to check the channel's `res' flag after an explicit flush yourself,
since none of the attached writers will notice any write error in this
case. *)
PROCEDURE (ch: Channel) Close*;
(* Flushes all buffers associated with `ch', closes the channel, and frees all
system resources allocated to it. This invalidates all riders attached to
`ch', they can't be used further. On success, i.e. if all read and write
operations (including flush) completed successfully, `ch.res' is set to
`done'. An opened channel can only be closed once, successive calls of
`Close' are undefined. *)
PROCEDURE Init* (ch: Channel; fd: FileDescriptor; mode: SHORTINT);
(* Attach channel `ch' to file descriptor `fd'. `mode' specifies whether the
descriptor should be treated as read only, write only, or read/write.
It's a bad idea to pass a duplicated file descriptor to `fd', all kinds
of unexpected things might happen. *)
PROCEDURE Truncate* (w: Writer; newLength: LONGINT);
(* Causes the file associated with `w' to have the specified length. If the
file was previously larger than `newLength', the extra data is lost. If it
was previously shorter, bytes between the old and new lengths are read as
zeros. The writer's position is not modified.
Note: On systems that do not support shortening files directly it is
implemented as a partial file copy.
This procedure should always be called through Files.Writer.Truncate. *)
END PosixFileDescr.
|