TapeStreams:
CEDAR
DEFINITIONS =
BEGIN
AccessOptions: TYPE ~ {read, write};
FillBlockOptions: TYPE ~ {truncate, zeroes, blanks};
ConversionList: TYPE = LIST OF REF ConversionRecord ← NIL;
ConversionRecord:
TYPE ~
RECORD
[ name: ATOM ← NIL, proc: Conversion ← NIL, clientData: REF ANY ← NIL ];
Conversion:
TYPE =
PROC [ base:
IO.
STREAM , clientData:
REF
ANY ←
NIL]
RETURNS [ s:
IO.
STREAM ];
.. takes a IO.STREAM and produces a layered IO.STREAM on it. Used for format conversion.
Error: PUBLIC ERROR [error: ErrorDesc];
ErrorDesc: TYPE ~ RECORD [group: ErrorGroup, explanation: Rope.ROPE] ← [ok, NIL];
ErrorGroup: TYPE = {
ok, -- initial group for a new TapeStreams.ErrorDesc
bug, -- error on the part of this software, TapeOps, or the TapeServer
environment, -- something's wrong in the environment; human intervention required
user -- illegal operation, probably due to user action
};
StreamOpen:
PROC [tape: TapeOps.TapeHandle, access: AccessOptions ← read,
blocking:
NAT ← 80, padding: FillBlockOptions ← blanks,
conversions: ConversionList ←
NIL, tapeRecordProc: TapeRecordProc ←
NIL,
clientData:
REF
ANY ←
NIL, allowSoftErrors:
BOOL ←
TRUE]
RETURNS[stream:
IO.
STREAM];
StreamOpen takes a "tape" returned from TapeOps.Open and creates an IO.STREAM on it at the place the tape was positioned to prior to the call to StreamOpen. "access" is used to tell whether the stream is to be written or read. The "blocking" parameter is used to indicate the physical block size to be written onto the tape during writes. The "padding" tells what to do if fewer than blocksize bytes are to be written. The default is to fill this space with blanks. Other options include zero fill, and truncation. Note that if the record is truncated, there may be only 1 byte written, which makes our tape server and most other tape sites, very unhappy. The "conversions" parameter allows a general way for format conversions to be specified by means of layered IO.STREAM's. The first conversion is done just on top of the real tape stream. The "tapeRecordProc" entry allow the user to provide a procedure call whenever a tape block is written onto, or read from, the tape.
Write Streams: are streams that support the following procedures from IO.mesa.
PutChar: PROC [self: STREAM, char: CHAR]
This causes the char to be put into the internal buffer and written onto the tape when the buffer is filled to the blocking size as specified in the StreamOpen.
PutBlock: PROC [self: STREAM, block: REF READONLY TEXT, startIndex: NAT ← 0, count: NAT ← NAT.LAST]
This procedure writes bytes from "block" starting at startIndex for count bytes. These bytes are moved into the tape buffer and the tape buffer is written onto the tape when the buffer is filled to the blocking size as specified in the StreamOpen.
Flush: PROC [self: STREAM]
This causes whatever bytes are in the tape buffer to be padded out to a full tape block and written onto the tape. Flush is called by Close and should not be called by the client before calling Close.
Close: PROC [self: STREAM, abort: BOOL ← FALSE]
This proc performs a Flush and then writes two tapemarks onto the tape and backs over one. In general, the end of tape volume is two tapemarks in consesion. If abort is true the tape is unloaded from the drive.
Read Streams: are streams that support the following procedures from IO.mesa.
GetChar: PROC [self: STREAM] RETURNS [CHAR]
This procedure reads 1 byte from the tape stream raising IO.EndOfStream if a tapemark has been read from the tape.
GetBlock: PROC [self: STREAM, block: REF TEXT,
startIndex: NAT ← 0, count: NAT ← NAT.LAST]
RETURNS [nBytesRead: NAT]
This procedure reads bytes into "block" using the indexes as shown in IO.mesa. Returns 0 bytes if end of file is reached.
EndOf: PROC [self: STREAM] RETURNS [BOOL]
Returns true if the last tape block read was a tapemark.
Close: PROC [self: STREAM, abort: BOOL ← FALSE]
This proc closes the stream. If the stream is not at the end of file mark on the tape the tape is NOT moved to that point. If abort is true the tape is unloaded from the drive.
Procedure to call on a record read or write of block by the lower level software
TapeRecordProc: TYPE ~ PROC [clientData: REF ANY ← NIL];
Procedure available as the $FlushBlock property of an output stream
FlushBlockProc: TYPE ~ PROC [ self: IO.STREAM, padChar: CHAR ← '\000, bytesRequired: INT ← LAST[INT], truncate: BOOL ← FALSE ];
Private Types for use by Conversion procedures
TapeStreamState: PRIVATE TYPE = REF TapeStreamStateRec ← NIL;
TapeStreamStateRec:
PRIVATE
TYPE =
RECORD
[
index: INT, -- stream index
buffer: REF TEXT,
lastBufferIndexPlusOne: INT,
tapeHandle: TapeOps.TapeHandle,
blockSize: INT ← 80,
writing: BOOL ← FALSE,
clientData: REF ANY ← NIL,
tapeRecordProc: TapeRecordProc ← NIL,
access: AccessOptions ← read,
eof: BOOL ← FALSE,
firstRecordRead: BOOL ← FALSE,
padding: FillBlockOptions ← blanks,
status: TapeOps.TapeStatus,
allowSoftErrors: BOOL ← TRUE
];
END. -- of TapeStreams