(* Copyright (C) 1989, Digital Equipment Corporation *) (* All rights reserved. *) (* See the file COPYRIGHT for a full description. *) (* Last modified on Sat Feb 29 08:19:34 PST 1992 by kalsow *) (* modified on Mon Dec 24 01:09:54 1990 by muller *) (* A Wr.T (or ``writer'') is a character output stream. The basic operation on a writer is PutChar, which extends a writer's character sequence by one character. Some writers (called ``seekable writers'') also allow overwriting in the middle of the sequence. For example, writers to random access files are seekable, but writers to terminals and sequential files are not. Writers can be (and usually are) buffered. This means that operations on the writer don't immediately affect the underlying target of the writer, but are saved up and performed later. For example, a writer to a disk file is not likely to update the disk after each character. Abstractly, a writer wr consists of: len(wr) a non-negative integer c(wr) a character sequence of length len(wr) cur(wr) an integer in the range [0..len(wr) target(wr) a character sequence closed(wr) a boolean seekable(wr) a boolean buffered(wr) a boolean These values are generally not directly represented in the data fields of a writer object, but in principle they determine the state of the writer. The sequence c(wr) is zero-based: c(wr)[i] is valid for i from 0 through len(wr)-1. The value of cur(wr) is the index of the character in c(wr) that will be replaced or appended by the next call to PutChar. If wr is not seekable, then cur(wr) is always equal to len(wr), since in this case all writing happens at the end. The difference between c(wr) and target(wr) reflects the buffering: if wr is not buffered, then target(wr) is updated to equal c(wr) after every operation; ifwr is buffered, then updates to target(wr) can be delayed. For example, in a writer to a file, target(wr) is the actual sequence of characters on the disk; in a writer to a terminal, target(wr) is the sequence of characters that have actually been transmitted (this sequence may not exist in any data structure, but it still exists in the eye of God). Every writer is a monitor; that is, it contains an internal lock that is acquired and held for each operation in this interface, so that concurrent operations will appear atomic. For faster, unmonitored access, see the UnsafeWr interface. Since there are many classes of writers, there are many ways that a writer can break---for example, the network can go down, the disk can fill up, etc. All problems of this sort are reported by raising the exception Failure. Each writer class should specify what failures it can raise and how they are encoded in the argument to Wr.Failure (which has type REFANY). Illegal operations (for example, writing to a closed writer) cause checked runtime errors by raising an internal exception. Many operations on a writer can wait indefinitely. For example, PutChar can wait if the user has suspended output to his terminal. These waits can be alertable, so each procedure that might wait includes Thread.Alerted in its raises clause. The rest of this section is a listing of the Wr interface, together with comments specifying the effect of each procedure. It is convenient to define the action PutC(wr, ch), which outputs the character ch to the writer wr: PutC(wr, ch) = IF closed(wr) THEN END; IF cur(wr) = len(wr) THEN "Extend c(wr) by one character, incrementing len(wr)" END; c(wr)[cur(wr)] := ch; INC(cur(wr)); "Possibly Flush wr" where "Possibly Flush wr" specifies a non-deterministic choice between assigning target(wr):=c(wr) and doing nothing. PutC is only used in specifying the interface; it is not a real procedure. *) INTERFACE Wr; FROM Thread IMPORT Alerted; TYPE T <: ROOT; EXCEPTION Failure(REFANY); PROCEDURE PutChar(wr: T; ch: CHAR) RAISES {Failure, Alerted}; (* Output ch to wr. More precisely, this is equivalent to: PutC(wr, ch); IF NOT buffered(wr) THEN Flush(wr) END *) PROCEDURE PutText(wr: T; t: TEXT) RAISES {Failure, Alerted}; (* Output t to wr. More precisely, this is equivalent to: FOR i := 0 TO Text.Length(t) - 1 DO PutC(wr, Text.GetChar(t, i)) END; IF NOT buffered(wr) THEN Flush(wr) END except that, like all operations in this interface, it is atomic with respect to other operations in the interface. *) PROCEDURE PutString(wr: T; READONLY a: ARRAY OF CHAR) RAISES {Failure,Alerted}; (* Output a to wr. More precisely, this is equivalent to: FOR i := FIRST(a) TO LAST(a) DO PutC(wr, a[i]) END; IF NOT buffered(wr) THEN Flush(wr) END except that it is atomic. *) PROCEDURE Seek(wr: T; n: CARDINAL) RAISES {Failure, Alerted}; (* Set the current position of wr to n. This is a no-op if wr is closed. More precisely, this is equivalent to: IF wr.closed OR NOT seekable(wr) THEN END; cur(wr) := MIN(n, len(wr)); "Possibly Flush wr" *) PROCEDURE Flush(wr: T) RAISES {Failure, Alerted}; (* Perform all buffered operations. That is, set target(wr) := c(wr). It is a checked runtime error if wr is closed. *) PROCEDURE Close(wr: T) RAISES {Failure, Alerted}; (* Flush wr, release any resources associated with wr (such as buffer space, file write locks, etc.) and finally set closed(wr) := true. The resources released depend on the class of the writer; consult the specification for the procedure that creates readers of wr's class. This leaves closed(wr) equal to TRUE even if it raises an exception, and is a no-op if wr is closed. *) PROCEDURE Length(wr: T): CARDINAL RAISES {Failure, Alerted}; (* This is equivalent to: IF closed(wr) THEN END; RETURN len(wr) *) PROCEDURE Index(wr: T): CARDINAL RAISES {}; (* This is equivalent to: IF closed(wr) THEN END; RETURN cur(wr) *) PROCEDURE Seekable(wr: T): BOOLEAN RAISES {}; PROCEDURE Closed(wr: T): BOOLEAN RAISES {}; PROCEDURE Buffered(wr: T): BOOLEAN RAISES {}; (* These procedures return seekable(wr), closed(wr), and buffered(wr), respectively. *) END Wr.