;;; -*- Mode:gate; Fonts:(HL12 HL12I HL12B CPTFONTB HL12BI HL12B HL12I ) -*- =Node: 4IO Streams* =Text: 3I/O STREAMS* An 1I/O stream*, or just 1stream*, is a source and/or sink of characters or bytes. A set of 1operations* is available with every stream; operations include things like ``output a character'' and ``input a character''. The way to perform an operation on a stream is the same for all streams, although what happens inside the stream is very different depending on what kind of a stream it is. So all a program has to know is how to deal with streams using the standard, generic operations. A programmer creating a new kind of stream only needs to implement the appropriate standard operations. A stream is a message-receiving object. This means that it is something that you can apply to arguments. The first argument is a keyword symbol which is the name of the operation you wish to perform. The rest of the arguments depend on what operation you are doing. Message-passing and generic operations are explained in the flavor chapter (4(FLAVOR-0)Objects, Message Passing, and Flavors*). Some streams can only do input, some can only do output, and some can do both. Some operations are only supported by some streams. Also, there are some operations that the stream may not support by itself, but which work anyway, albeit slowly, because the 1stream default handler* can handle them. All streams support the operation 2:which-operations*, which returns a list of the names of all of the operations that are supported ``natively'' by the stream. (2:which-operations* itself is not in the list.) All input streams support all the standard input operations, and all output streams support all the standard output operations. All bidirectional streams support both. 3streamp* 1object* According to Common Lisp, this returns 2t* if 1object* is a stream. In the Lisp machine, a stream is any object which can be called as a function with certain calling conventions. It is theoretically impossible to test for this. However, 2streamp* does return 2t* for any of the usual types of streams, and 2nil* for any Common Lisp datum which is not a stream. =Node: 4Standard Streams* =Text: 3STANDARD STREAMS* There are several variables whose values are streams used by many functions in the Lisp system. These variables and their uses are listed here. By convention, variables that are expected to hold a stream capable of input have names ending with 2-input*, and similarly for output. Those expected to hold a bidirectional stream have names ending with 2-io*. The names with asterisks are synonyms introduced for the sake of Common Lisp. 3*standard-input** 1Variable* 3standard-input* 1Variable* In the normal Lisp top-level loop, input is read from 2*standard-input** (that is, whatever stream is the value of 2*standard-input**). Many input functions, including 2tyi* and 2read*, take a stream argument that defaults to 2*standard-input**. 3*standard-output** 1Variable* 3standard-output* 1Variable* In the normal Lisp top-level loop, output is sent to 2*standard-output** (that is, whatever stream is the value of 2*standard-output**). Many output functions, including 2tyo* and 2print*, take a stream argument that defaults to 2*standard-output**. 3*error-output** 1Variable* 3error-output* 1Variable* The value of 2*error-output** is a stream on which noninteractive error or warning messages should be printed. Normally this is the same as 2*standard-output**, but 2*standard-output** might be bound to a file and 2*error-output** left going to the terminal. 3*debug-io** 1Variable* 3debug-io* 1Variable* The value of 2*debug-io** is used for all input and output by the error handler. Normally this is a synonym for 2*terminal-io**. The value may be 2nil*, which is regarded as equivalent to a synonym for 2*terminal-io**. This feature is provided because users often set 2*debug-io** by hand, and it is much easier to set it back to 2nil* afterward than to figure out the proper synonym stream pointing to 2*terminal-io**. 3*query-io** 1Variable* 3query-io* 1Variable* The value of 2*query-io** is a stream that should be used when asking questions of the user. The question should be output to this stream, and the answer read from it. The reason for this is that when the normal input to a program may be coming from a file, questions such as ``Do you really want to delete all of the files in your directory??'' should be sent directly to the user, and the answer should come from the user, not from the data file. 2*query-io** is used by 2fquery* and related functions; see 4(QUERY-1)Querying the User*. 3*terminal-io** 1Variable* 3terminal-io* 1Variable* The value of 2*terminal-io** is the stream that the program should use to talk to the user's console. In an interactive program, it is the window from which the program is being run; I/O on this stream reads from the keyboard and displays on the screen. However, in a background process that has no window, 2*terminal-io** defaults to a stream that does not ever expect to be used. If it is used, perhaps by an error printout, it turns into a background window and requests the user's attention. 3*trace-output** 1Variable* 3trace-output* 1Variable* The value of 2*trace-output** is the stream on which the 2trace* function prints its output. 2*standard-input**, 2*standard-output**, 2*error-output**, 2*debug-io**, 2*trace-output**, and 2*query-io** are initially bound to synonym streams that pass all operations on to the stream that is the value of 2*terminal-io**. Thus any operations performed on those streams go to the keyboard and screen. Most user programs should not change the value of 2*terminal-io**. A program which wants (for example) to divert output to a file should do so by binding the value of 2*standard-output**; that way queries on 2*query-io**, debugging on 2*debug-io** and error messages sent to 2*error-output** can still get to the user by going through 2*terminal-io**, which is usually what is desired. =Node: 4Standard Input Stream Operations* =Text: 3STANDARD INPUT STREAM OPERATIONS :tyi* &optional 1eof* 1Operation on streams* The stream inputs one character and returns it. For example, if the next character to be read in by the stream is a `C', then the form 3(send s :tyi)* returns the value of 2#/C* (that is, 103 octal). Note that the 2:tyi* operation does not echo the character in any fashion; it just does the input. The 2tyi* function (see 4(IOSYSTEM-1)Maclisp Compatibility Input Functions*) does echoing when reading from the terminal. The optional 1eof* argument to the 2:tyi* operation tells the stream what to do if it gets to the end of the file. If the argument is not provided or is 2nil*, the stream returns 2nil* at the end of file. Otherwise it signals a 2sys:end-of-file* error. Note that this is 1not* the same as the eof-option argument to 2read*, 2tyi*, and related functions. The 2:tyi* operation on a binary input stream returns a non-negative number, not necessarily to be interpreted as a character. For some streams (such as windows), not all the input data are numbers. Some are lists, called 1blips*. The 2:tyi* operation returns only numbers. If the next available input is not a number, it is discarded, and so on until a number is reached (or end of file is reached). 3:any-tyi* &optional 1eof* 1Operation on streams* Like 2:tyi* but returns any kind of datum. Non-numbers are not discarded as they would be by 2:tyi*. This distinction only makes a difference on streams which can provide input which is not composed of numbers; currently, only windows can do that. 3:tyipeek* &optional 1eof* 1Operation on streams* Peeks at the next character or byte from the stream without discarding it. The next 2:tyi* or 2:tyipeek* operation will get the same character. 1eof* is the same as in the 2:tyi* operation: if 2nil*, end of file returns 2nil*; otherwise, it signals a 2sys:end-of-file* error. 3:untyi* 1char* 1Operation on streams* Unreads the character or byte 1char*; that is to say, puts it back into the input stream so that the next 2:tyi* operation will read it again. For example, 3(send s :untyi 120)* 3(send s :tyi) ==> 120* This operation is used by 2read*, and any stream that supports 2:tyi* must support 2:untyi* as well. You are only allowed to 2:untyi* one character before doing a 2:tyi*, and the character you 2:untyi* must be the last character read from the stream. That is, 2:untyi* can only be used to back up one character, not to stuff arbitrary data into the stream. You also can't 2:untyi* after you have peeked ahead with 2:tyipeek* since that does one 2:untyi* itself. Some streams implement 2:untyi* by saving the character, while others implement it by backing up the pointer to a buffer. 3:string-in* 1eof-option* 1string* &optional 1(start* 301)** 1end* 1Operation on streams* Reads characters from the stream and stores them into the array 1string*. Many streams can implement this far more efficiently that repeated 2:tyi*'s. 1start* and 1end*, if supplied, delimit the portion of 1string* to be stored into. If 1eof-option* is non-2nil* then a 2sys:end-of-file* error is signaled if end of file is reached on the stream before the string has been filled. If 1eof-option* is 2nil*, any number of characters before end of file is acceptable, even no characters. If 1string* has an array-leader, the fill pointer is adjusted to 1start* plus the number of characters stored into 1string*. Two values are returned: the index of the next position in 1string* to be filled, and a flag that is non-2nil* if end of file was reached before 1string* was filled. Most callers do not need to look at either of these values. 1string* may be any kind of array, not necessarily a string; this is useful when reading from a binary input stream. 3:line-in* &optional 1leader* 1Operation on streams* The stream should input one line from the input source, and return it as a string with the carriage return character stripped off. Contrary to what you might assume from its name, this operation is not much like the 2readline* function. Many streams have a string that is used as a buffer for lines. If this string itself were returned, there would be problems caused if the caller of the stream attempted to save the string away somewhere, because the contents of the string would change when the next line was read in. In order to solve this problem, the string must be copied. On the other hand, some streams don't reuse the string, and it would be wasteful to copy it on every 2:line-in* operation. This problem is solved by using the 1leader* argument to 2:line-in*. If 1leader* is 2nil* (the default), the stream does not bother to copy the string and the caller should not rely on the contents of that string after the next operation on the stream. If 1leader* is 2t*, the stream does make a copy. If 1leader* is a fixnum then the stream makes a copy with an array leader 1leader* elements long. (This is used by the editor, which represents lines of buffers as strings with additional information in their array-leaders, to eliminate an extra copy operation.) If the stream reaches end of file while reading in characters, it returns the characters it has read in as a string and returns a second value of 2t*. The caller of the stream should therefore arrange to receive the second value, and check it to see whether the string returned was a whole line or just the trailing characters after the last carriage return in the input source. This operation should be implemented by all input streams whose data are characters. 3:string-line-in* 1eof-option* 1string* &optional 1(start* 301)** 1end* 1Operation on streams* Reads characters, storing them in 1string*, until 1string* is full or a 2Return* character is read. If input stops due to a Return, the Return itself is not put in the buffer. Thus, this operation is nearly the same as 2:string-in*, except that 2:string-in* always keeps going until the buffer is full or until end of file. 1start* and 1end*, if supplied, delimit the portion of 1string* to be stored into. If 1eof-option* is non-2nil* then a 2sys:end-of-file* error is signaled if end of file is reached on the stream before the string has been filled. If 1eof-option* is 2nil*, any number of characters before end of file is acceptable, even no characters. If 1string* has an array-leader, the fill pointer is adjusted to 1start* plus the number of characters stored into 1string*. 1string* may be any kind of array, not necessarily a string; this is useful when reading from a binary input stream. Three values are returned: 2(1)* The index in 1string* at which input stopped. This is the first index not stored in. 2(2)* 2t* if input stopped due to end of file. 2(3)* 2t* if the line is incomplete; that is, if a 2Return* character did not terminate it. 3:read-until-eof* 1Operation on streams* Discards all data from the stream until it is at end of file, or does anything else with the same result. 3:close* &optional 1ignore* 1Operation on streams* Releases resources associated with the stream, when it is not going to be used any more. On some kinds of streams, this may do nothing. On Chaosnet streams, it closes the Chaosnet connection, and on file streams, it closes the input file on the file server. The argument is accepted for compatibility with 2:close* on output streams. =Node: 4Standard Output Stream Operations* =Text: 3STANDARD OUTPUT STREAM OPERATIONS :tyo* 1char* 1Operation on streams* The stream outputs the character 1char*. For example, if 2s* is bound to a stream, then the form 3(send s :tyo #/B)* outputs a 2B* to the stream. For binary output streams, the argument is a non-negative number rather than specifically a character. 3:fresh-line* 1Operation on streams* Tells the stream that it should position itself at the beginning of a new line. If the stream is already at the beginning of a fresh line it should do nothing; otherwise it should output a carriage return. If the stream cannot tell whether it is at the beginning of a line, it should always output a carriage return. 3:string-out* 1(string* 301)** &optional 1start* 1end* 1Operation on streams* Outputs the characters of 1string* successively to 1stream*. This operation is provided for two reasons; first, it saves the writing of a loop which is used very often, and second, many streams can perform this operation much more efficiently than the equivalent sequence of 2:tyo* operations. If 1start* and 1end* are not supplied, the whole string is output. Otherwise a substring is output; 1start* is the index of the first character to be output (defaulting to 20*), and 1end* is one greater than the index of the last character to be output (defaulting to the length of the string). Callers need not pass these arguments, but all streams that handle 2:string-out* must check for them and interpret them appropriately. 3:line-out* 1string* &optional 1(start* 301)** 1end* 1Operation on streams* Outputs the characters of 1string* successively to 1stream*, then outputs a 2Return* character. 1start* and 1end* optionally specify a substring, as with 2:string-out*. If the stream doesn't support 2:line-out* itself, the default handler implements it by means of 2:tyo*. This operation should be implemented by all output streams whose data are characters. 3:close* &optional 1mode* 1Operation on streams* Closes the stream to make the output final if this is necessary. The stream becomes 1closed* and no further output operations should be performed on it. However, it is all right to 2:close* a closed stream. On many file server hosts, a file being written is not accessible to be read until the output stream is closed. This operation does nothing on streams for which it is not meaningful. The 1mode* argument is normally not supplied. If it is 2:abort*, we are abnormally exiting from the use of this stream. If the stream is outputting to a file, and has not been closed already, the stream's newly-created file is deleted; it will be as if it was never opened in the first place. Any previously existing file with the same name remains undisturbed. 3:eof* 1Operation on streams* Indicates the end of data on an output stream. This is different from 2:close* because some devices allow multiple data files to be transmitted without closing. 2:close* implies 2:eof* when the stream is an output stream and the close mode is not 2:abort*. This operation does nothing on streams for which it is not meaningful. =Node: 4Asking Streams What They Can Do* =Text: 3ASKING STREAMS WHAT THEY CAN DO* All streams are supposed to support certain operations which enable a program using the stream to ask which operations are available. 3:which-operations* 1Operation on streams* Returns a list of operations handled natively by the stream. Certain operations not in the list may work anyway, but slowly, so it is just as well if any programs that work with or without them choose not to use them. 2:which-operations* itself need not be in the list. 3:operation-handled-p* 1operation* 1Operation on streams* Returns 2t* if 1operation* is handled natively by the stream: if 1operation* is a member of the 2:which-operations* list, or is 2:which-operations*. 3:send-if-handles* 1operation* &rest 1arguments* 1Operation on streams* Performs the operation 1operation*, with the specified 1arguments*, only if the stream can handle it. If 1operation* is handled, this is the same as sending an 1operation* message directly, but if 1operation* is not handled, using 2:send-if-handles* avoids any error. If 1operation* is handled, 2:send-if-handles* returns whatever values the execution of the 1operation* returns. If 1operation* is not handled, 2:send-if-handles* returns 2nil*. 3:direction* 1Operation on streams* Returns 2:input*, 2:output*, or 2:bidirectional* for a bidirectional stream. There are a few kinds of streams, which cannot do either input or output, for which the 2:direction* operation returns 2nil*. For example, 2open* with the 2:direction* keyword specified as 2nil* returns a stream-like object which cannot do input or output but can handle certain file inquiry operations such as 2:truename* and 2:creation-date*. 3:characters* 1Operation on streams* Returns 2t* if the data input or output on the stream represent characters, or 2nil* if they are just numbers (as for a stream reading a non-text file). 3:element-type* 1Operation on streams* Returns a type specified describing in principle the data input or output on the stream. Refer to the function 2stream-element-type*, below, which works using this operation. These functions for inquiring about streams are defined by Common Lisp. 3input-stream-p* 1stream* 2t* if 1stream* handles input operations (at least, if it handles 2:tyi*). 3output-stream-p* 1stream* 2t* if 1stream* handles output operations (at least, if it handles 2:tyo*). 3stream-element-type* 1stream* Returns a type specifier which describes, conceptually, the kind of data input from or output to 1stream*. The value is always a subtype of 2integer* (for a binary stream) or a subtype of 2character* (for a character stream). If it is a subtype of 2integer*, a Common Lisp program should use 2read-byte* (4(IOSYSTEM-1)Character-Level Input Functions*) or 2write-byte* (4(IOSYSTEM-1)Output Functions*) for I/O. If it is a subtype of 2character*, 2read-char* (4(IOSYSTEM-1)Character-Level Input Functions*) or 2write-char* (4(IOSYSTEM-1)Output Functions*) should be used. The value returned is not intended to be rigidly accurate. It describes the typical or characteristic sort of data transferred by the stream, but the stream may on occasion deal with data that do not fit the type; also, not all objects of the type may be possible as input or even make sense as output. For example, windows describe their element type as 2character* even though they may offer blips, which are lists, as input on occasion. In addition, streams which say they provide characters really return integers if the 2:tyi* operation is used rather than the standard Common Lisp function 2read-char*. =Node: 4Operations for Interactive Streams* =Text: 3OPERATIONS FOR INTERACTIVE STREAMS* The operations 2:listen*, 2:tyi-no-hang*, 2:rubout-handler* and 2:beep* are intended for interactive streams, which communicate with the user. 2:listen* and 2:tyi-no-hang* are supported in a trivial fashion by other streams, for compatibility. 3:listen* 1Operation on streams* On an interactive device, the 2:listen* operation returns non-2nil* if there are any input characters immediately available, or 2nil* if there is no immediately available input. On a non-interactive device, the operation always returns non-2nil* except at end of file. The main purpose of 2:listen* is to test whether the user has hit a key, perhaps trying to stop a program in progress. 3:tyi-no-hang* &optional 1eof* 1Operation on streams* Just like 2:tyi* except that it returns 2nil* rather than waiting if it would be necessary to wait in order to get the character. This lets the caller check efficiently for input being available and get the input if there is any. 2:tyi-no-hang* is different from 2:listen* because it reads a character. Streams for which the question of whether input is available is not meaningful treat this operation just like 2:tyi*. So do Chaosnet file streams. Although in fact reading a character from a file stream may involve a delay, these delays are 1supposed* to be insignificant, so we pretend they do not exist. 3:any-tyi-no-hang* &optional 1eof* 1Operation on streams* Like 2:tyi-no-hang* but does not filter and discard input which is not numbers. It is therefore possible to see blips in the input stream. The distinction matters only for input from windows. 3:rubout-handler* 1options* 1function* &rest 1args* 1Operation on streams* This is supported by interactive bidirectional streams, such as windows on the terminal, and is described in its own section below (see 4(IOSYSTEM-4)Rubout Handling*). 3:beep* &optional 1type* 1Operation on streams* This is supported by interactive streams. It attracts the attention of the user by making an audible beep and/or flashing the screen. 1beep-type* is a keyword selecting among several different beeping noises; see 2beep* (4(IOSYSTEM-1)Output Functions*) for a list of them. =Node: 4Cursor Positioning Stream Operations* =Text: 3CURSOR POSITIONING STREAM OPERATIONS :read-cursorpos* &optional 1(units* 3:pixel1)** 1Operation on streams* This operation is supported by all windows and some other streams. It returns two values, the current 1x* and 1y* coordinates of the cursor. It takes one optional argument, which is a symbol indicating in what units 1x* and 1y* should be; the symbols 2:pixel* and 2:character* are understood. 2:pixel* means that the coordinates are measured in display pixels (bits), while 2:character* means that the coordinates are measured in characters horizontally and lines vertically. This operation and 2:increment-cursorpos* are used by the 2format* 2~T* request (see 4(IOSYSTEM-3)The Format Function*), which is why 2~T* doesn't work on all streams. Any stream that supports this operation should support 2:increment-cursorpos* as well. Some streams return a meaningful value for the horizontal position but always return zero for the vertical position. This is sufficient for 2~T* to work. 3:increment-cursorpos* 1x-increment* 1y-increment* &optional 1(units* 3:pixel1)Operation on streams** Moves the stream's cursor left or down according to the specified increments, as if by outputting an appropriate number of space or return characters. 1x* and 1y* are like the values of 2:read-cursorpos* and 1units* is the same as the 1units* argument to 2:read-cursorpos*. Any stream which supports this operation should support 2:read-cursorpos* as well, but it need not support 2:set-cursorpos*. Moving the cursor with 2:increment-cursorpos* differs from moving it to the same place with 2:set-cursorpos* in that this operation is thought of as doing output and 2:set-cursorpos* is not. For example, moving a window's cursor down with 2:increment-cursorpos* when it is near the bottom to begin with will wrap around, possibly doing a 2**MORE***. 2:set-cursorpos*, by comparison, cannot move the cursor ``down'' if it is at the bottom of the window; it can move the cursor explicitly to the top of the window, but then no 2**MORE*** will happen. Some streams, such as those created by 2with-output-to-string*, cannot implement arbitrary cursor motion, but do implement this operation. 3:set-cursorpos* 1x* 1y* &optional 1(units* 3:pixel1)** 1Operation on streams* This operation is supported by the same streams that support 2:read-cursorpos*. It sets the position of the cursor. 1x* and 1y* are like the values of 2:read-cursorpos* and 1units* is the same as the 1units* argument to 2:read-cursorpos*. 3:clear-screen* 1Operation on streams* Erases the screen area on which this stream displays. Non-window streams don't support this operation. There are many other special-purpose stream operations for graphics. They are not documented here, but in the window-system documentation. No claim that the above operations are the most useful subset should be implied. =Node: 4Operations for Efficient Pretty-Printing* =Text: 3OPERATIONS FOR EFFICIENT PRETTY-PRINTING* 2grindef* runs much more efficiently on streams that implement the 2:untyo-mark* and 2:untyo* operations. 3:untyo-mark* 1Operation on streams* This is used by the grinder (see 4(READPRINT-3)Pretty-Printing Output Functions*) if the output stream supports it. It takes no arguments. The stream should return some object that indicates how far output has gotten up to in the stream. 3:untyo* 1mark* 1Operation on streams* This is used by the grinder (see 4(READPRINT-3)Pretty-Printing Output Functions*) in conjunction with 2:untyo-mark*. It takes one argument, which is something returned by the 2:untyo-mark* operation of the stream. The stream should back up output to the point at which the object was returned. =Node: 4Random Access File Operations* =Text: 3RANDOM ACCESS FILE OPERATIONS* The following operations are implemented only by streams to random-access devices, principally files. 3:read-pointer* 1Operation on streams* Returns the current position within the file, in characters (bytes in fixnum mode). For text files on ASCII file servers, this is the number of Lisp Machine characters, not ASCII characters. The numbers are different because of character-set translation. 3:set-pointer* 1new-pointer* 1Operation on streams* Sets the reading position within the file to 1new-pointer* (bytes in fixnum mode). For text files on ASCII file servers, this does not do anything reasonable unless 1new-pointer* is 0, because of character-set translation. Some file systems support this operation for input streams only. 3:rewind* 1Operation on streams* This operation is obsolete. It is the same as 2:set-pointer* with argument zero. =Node: 4Buffered Stream Operations* =Text: 3BUFFERED STREAM OPERATIONS :clear-input* 1Operation on streams* Discards any buffered input the stream may have. It does nothing on streams for which it is not meaningful. 3:clear-output* 1Operation on streams* Discards any buffered output the stream may have. It does nothing on streams for which it is not meaningful. 3:force-output* 1Operation on streams* This is for output streams to buffered asynchronous devices, such as the Chaosnet. 2:force-output* causes any buffered output to be sent to the device. It does not wait for it to complete; use 2:finish* for that. If a stream supports 2:force-output*, then 2:tyo*, 2:string-out*, and 2:line-out* may have no visible effect until a 2:force-output* is done. This operation does nothing on streams for which it is not meaningful. 3:finish* 1Operation on streams* This is for output streams to buffered asynchronous devices, such as the Chaosnet. 2:finish* does a 2:force-output*, then waits until the currently pending I/O operation has been completed. This operation does nothing on streams for which it is not meaningful. The following operations are implemented only by buffered input streams. They allow increased efficiency by making the stream's internal buffer available to the user. 3:read-input-buffer* &optional 1eof* 1Operation on streams* Returns three values: a buffer array, the index in that array of the next input byte, and the index in that array just past the last available input byte. These values are similar to the 1string*, 1start*, 1end* arguments taken by many functions and stream operations. If the end of the file has been reached and no input bytes are available, this operation returns 2nil* or signals an error, based on the 1eof* argument, just like the 2:tyi* operation. After reading as many bytes from the array as you care to, you must use the 2:advance-input-buffer* operation. 3:get-input-buffer* &optional 1eof* 1Operation on streams* This is an obsolete operation similar to 2:read-input-buffer*. The only difference is that the third value is the number of significant elements in the buffer-array, rather than a final index. If found in programs, it should be replaced with 2:read-input-buffer*. 3:advance-input-buffer* &optional 1new-pointer* 1Operation on streams* If 1new-pointer* is non-2nil*, it is the index in the buffer array of the next byte to be read. If 1new-pointer* is 2nil*, the entire buffer has been used up. =Node: 4Obtaining Streams to Use* =Text: 3OBTAINING STREAMS TO USE* Windows are one important class of streams. Each window can be used as a stream. Output is displayed on the window and input comes from the keyboard. A window is created using 2make-instance* on a window flavor. Simple programs use windows implicitly through 2*terminal-io** and the other standard stream variables. Also important are 1file streams*, which are produced by the function 2open* (see 4(FILEACCESS-1)Opening and Closing File Streams*). These read or write the contents of a file. 1Chaosnet streams* are made from Chaosnet connections. Data output to the stream goes out over the network; data coming in over the network is available as input from the stream. File streams that deal with Chaosnet file servers are very similar to Chaosnet streams, but Chaosnet streams can be used for many purposes other than file access. 1String streams* read or write the contents of a string. They are made by 2with-output-to-string* or 2with-input-from-string* (see 4(IOSYSTEM-2)String I/O Streams*), or by 2make-string-input-stream* or 2make-string-output-stream*, below. 1Editor buffer streams* read or write the contents of an editor buffer. The 1null stream* may be passed to a program that asks for a stream as an argument. It returns immediate end of file if used for input and throws away any output. The null stream is the symbol 2si:null-stream*. This is to say, you do not call that function to get a stream or use the symbol's value as the stream; 1the symbol itself* is the object that is the stream. The 1cold-load stream* is able to do I/O to the keyboard and screen without using the window system. It is what is used by the error handler, if you type 2Terminal Call*, to handle a background error that the window system cannot deal with. It is called the cold-load stream because it is what is used during system bootstrapping, before the window system has been loaded. 3si:null-stream* 1operation* &rest 1arguments* This function is the null stream. Like any stream, it supports various operations. Output operations are ignored and input operations report end of file immediately, with no data. Usage example: 3(let ((*standard-output* 'si:null-stream))* 3 (function-whose-output-I-dont-want)) si:cold-load-stream* 1Constant* The one and only cold-load stream. Usage example: 3(let ((*query-io* si:cold-load-stream))* 3 (yes-or-no-p "Clear all window system locks? ")) with-open-stream* 1(variable* 1expression)* 1body...* 1Macro* 1body* is executed with 1variable* bound to the value of 1expression*, which ought to be a stream. On exit, whether normal or by throwing, a 2:close* message with argument 2:abort* is sent to the stream. This is a generalization of 2with-open-file*, which is equivalent to using 2with-open-stream* with a call to 2open* as the 1expression*. 3with-open-stream-case* 1(variable* 1expression)* 1clauses...* 1Macro* Like 2with-open-stream* as far as opening and closing the stream are concerned, but instead of a simple body, it has clauses like those of a 2condition-case* that say what to do if 1expression* does or does not get an error. See 2with-open-file-case*, 4(FILEACCESS-1)Opening and Closing File Streams*. 3make-synonym-stream* 1symbol-or-locative* 3make-syn-stream* 1symbol-or-locative* Creates and returns a 1synonym* stream (`syn' for short). Any operations sent to this stream are redirected to the stream that is the value of the argument (if it is a symbol) or the contents of it (if it is a locative). A synonym stream is actually an uninterned symbol whose function defnition is forwarded to the function cell of the argument or to the contents of the argument as appropriate. If the argument is a symbol, the synonym stream's print-name is 1symbol2-syn-stream**; otherwise the name is just 2syn-stream*. Once a synonym stream is made for a symbol, it is recorded, and the same one is handed out again if there is another request for it. The two names for this function are synonyms too. 3make-concatenated-stream* &rest 1streams* Returns an input stream which will read its input from the first of 1streams* until that reaches its eof, then read input from the second of 1streams*, and so on until the last of 1streams* has reached end of file. 3make-two-way-stream* 1input-stream* 1output-stream* Returns a bidirectional stream which passes input operations to 1input-stream* and passes output operations to 1output-stream*. This works by attempting to recognize all standard input operations; anything not recognized is passed to 1output-stream*. 3make-echo-stream* 1input-stream* 1output-stream* Like 2make-two-way-stream* except that each input character read via 1input-stream* is output to 1output-stream* before it is returned to the caller. 3make-broadcast-stream* &rest 1streams* Returns a stream that only works in the output direction. Any output sent to this stream is forwarded to all of the streams given. The 2:which-operations* is the intersection of the 2:which-operations* of all of the streams. The value(s) returned by a stream operation are the values returned by the last stream in 1streams*. 3zwei:interval-stream* 1interval-or-from-bp* &optional 1to-bp* 1in-order-p* 1hack-fonts* Returns a bidirectional stream that reads or writes all or part of an editor buffer. Note that editor buffer streams can also be obtained from 2open* by using a pathname whose host is 2ED*, 2ED-BUFFER* or 2ED-FILE* (see 4(FILENAMES-3)Editor Buffer Pathnames*). The first three arguments specify the buffer or portion to be read or written. Either the first argument is an 1interval* (a buffer is one kind of interval), and all the text of that interval is read or written, or the first two arguments are two buffer pointers delimiting the range to be read or written. The third argument is used only in the latter case; if non-2nil*, it tells the function to assume that the second buffer pointer comes later in the buffer than the first and not to take the time to verify the assumption. The stream has only one pointer inside it, used for both input and output. As you do input, the pointer advances through the text. When you do output, it is inserted in the buffer at the place where the pointer has reached. The pointer starts at the beginning of the specified range. 1hack-fonts* tells what to do about fonts. Its possible values are 2t* The character 2* is recognized as special when you output to the stream; sequences such as 22* are interpreted as font-changes. They do not get inserted into the buffer; instead, they change the font in which following output will be inserted. On input, font change sequences are included to indicate faithfully what was in the buffer. 2:tyo* You are expected to read and write 16-bit characters containing font numbers. 2nil* All output is inserted in font zero and font information is discarded in the input you receive. This is the best mode to use if you are reading or otherwise parsing the contents of an editor buffer. 3sys:with-help-stream* 1(stream* 1options...)* 1body...* 1Macro* Executes the 1body* with the variable 1stream* bound to a suitable stream for printing a large help message. If 2*standard-output** is a window, then 1stream* is also a window; a temporary window which fills the screen. Otherwise, 1stream* is just the same as 2*standard-output**. The purpose of this is to spare the user the need to read a large help printout in a small window, or have his data overwritten by it permanently. This is the mechanism used if you type the 2Control-Help* key while in the rubout handler. 1options* is a list of alternating keywords and values. .kitem :label The value (which is evaluated) is used as the label of the temporary window, if one is used. 2:width* The value, which is not evaluated, is a symbol. While 1body* is executed, this symbol is bound to the width, in characters, available for the message. 2:height* The value is a symbol, like the value after 2:width*, and it is bound to the height in lines of the area available for the help message. 2:superior* The value, which is evaluated, specifies the original stream to use in deciding where to print the help message. The default is 2*standard-output**. =Node: 4String IO Streams* =Text: 3STRING I/O STREAMS* The functions and special forms in this section allow you to create I/O streams that input from or output to the contents of a string. 3make-string-input-stream* 1string* &optional 1(start* 301)** 1end* Returns a stream which can be used to read the contents of 1string* (or the portion of it from index 1start* to index 1end*) as input. End of file occurs on reading past position 1end* or the end of string. 3make-string-output-stream* &optional 1string* Returns an output stream which will accumulate all output in a string. If 1string* is non-2nil*, output is added to it with 2string-nconc* (4(CHARSTR-2)Basic String Operations*). Otherwise, a new string is created and used to hold the output. 3get-output-stream-string* 1string-output-stream* Returns the string of output accumulated so far by a stream which was made by 2make-string-output-stream*. The accumulated output is cleared out, so it will not be obtained again if 2get-output-stream-string* is called another time on the same stream. 3with-input-from-string* 1(var* 1string* &key 1start* 1end* 1index)* 1body...* 1Macro* The form 3(with-input-from-string (1var* 1string*)* 3 1body*)* evaluates the forms in 1body* with the variable 1var* bound to a stream which reads characters from the string which is the value of the form 1string*. The value of the construct is the value of the last form in its body. If the 1start* and 1end* arguments are specified, they should be forms. They are evaluated at run time to produce the indices starting and ending the portion of 1string* to be read. If the 1index* argument is specified, it should be something 2setf* can store in. When 1body* is finished, the index in the string at which reading stopped is stored there. This is the index of the first character not read. If the entire string was read, it is the length of the string. The value of 1index* is not updated until 2with-input-from-string* is exited, so you can't use its value within the body to see how far the reading has gotten. Example: 3(with-input-from-string* 3 (foo "This is a test." :start (+ 2 2) :end 8 :index bar)* 3 (readline))* returns 2" is "* and sets 2bar* to eight. An older calling sequence which used positional rather than keyword arguments is still accepted: 3(with-input-from-string (1var* 1string* 1index* 1end*)* 3 1body*)* The functions 2read-from-string* and 2cli:read-from-string* are convenient special cases of what 2with-input-from-string* can do. See 4(READPRINT-3)Non-Stream Parsing Functions*. 3with-output-to-string* 1(var* 1[string* 1[index]])* 1body...* 1Macro* This special form provides a variety of ways to send output to a string through an I/O stream. 3(with-output-to-string (1var*)* 3 1body*)* evaluates the forms in 1body* with 1var* bound to a stream which saves the characters output to it in a string. The value of the special form is the string. 3(with-output-to-string (1var* 1string*)* 3 1body*)* appends its output to the string which is the value of the form 1string*. (This is like the 2string-nconc* function; see 4(CHARSTR-2)Basic String Operations*.) The value returned is the value of the last form in the body, rather than the string. Multiple values are not returned. 1string* must have a fill pointer. If 1string* is too small to contain all the output, 2adjust-array-size* is used to make it bigger. 3(with-output-to-string (1var* 1string* 1index*)* 3 1body*)* is similar to the above except that 1index* is a variable or 2setf*-able reference which contains the index of the next character to be stored into. It must be initialized before the 2with-output-to-string* and it is updated upon normal exit. The value of 1index* is not updated until 2with-output-to-string* returns, so you can't use its value within the body to see how far the writing has gotten. The presence of 1index* means that 1string* is not required to have a fill-pointer; if there is one, it is updated on exit. Another way of doing output to a string is to use the 2format* facility (see 4(IOSYSTEM-3)The Format Function*). =Node: 4Implementing Streams* =Text: 3IMPLEMENTING STREAMS* There are two ways to implement a stream: using 2defun* or using flavors. Using flavors is best when you can take advantage of the predefined stream mixins, including those which perform buffering, or when you wish to define several similar kinds of streams that can inherit methods from each other. 2defun* (or 2defselect*, which is a minor variation of the technique) may have an advantage if you are dividing operations into broad groups and handling them by passing them off to one or more other streams. In this case, the automatic operation decoding provided by flavors may get in the way. A number of streams in the system are implemented using 2defun* or 2defselect* for historical reasons. It isn't yet clear whether there is any reason not to convert most of them to use flavors. If you use 2defun*, you can use the 1stream default handler* to implement some of the standard operations for you in a default manner. If you use flavors, there are predefined mixins to do this for you. A few streams are individual objects, one of a kind. For example, there is only one null stream, and no need for more, since two null streams would behave identically. But most streams are elements of a general class. For example, there can be many file streams for different files, even though all behave the same way. There can also be multiple streams reading from different points in the same file. If you implement a class of streams with 2defun*, then the actual streams must be closures of the function you define, made with 2closure*. If you use flavors to implement the streams, having a class of similar streams comes naturally: each instance of the flavor is a stream, and the instance variables distinguish one stream of the class from another. =Node: 4Implementing Streams with Flavors* =Text: 3IMPLEMENTING STREAMS WITH FLAVORS* To define a stream using flavors, define a flavor which incorporates the appropriate predefined stream flavor, and then redefine those operations which are peculiar to your own type of stream. Flavors for defining unbuffered streams: 3si:stream* 1Flavor* This flavor provides default definitions for a few standard operations such as 2:direction* and 2:characters*. Usually you do not have to mention this explicitly; instead you use the higher level flavors below, which are built on this one. 3si:input-stream* 1Flavor* This flavor provides default definitions of all the mandatory input operations except 2:tyi* and 2:untyi*, in terms of those two. You can make a simple non-character input stream by defining a flavor incorporating this one and giving it methods for 2:tyi* and 2:untyi*. 3si:output-stream* 1Flavor* This flavor provides default definitions of all the mandatory output operations except 2:tyo*, in terms of 2:tyo*. All you need to do to define a simple unbuffered non-character output stream is to define a flavor incorporating this one and give it a method for the 2:tyo* operation. 3si:bidirectional-stream* 1Flavor* This is a combination of 2si:input-stream* and 2si:output-stream*. It defines 2:direction* to return 2:bidirectional*. To define a simple unbuffered non-character bidirectional stream, build on this flavor and define 2:tyi*, 2:untyi* and 2:tyo*. The unbuffered streams implement operations such as 2:string-out* and 2:string-in* by repeated use of 2:tyo* or 2:tyi*. For greater efficiency, if the stream's data is available in blocks, it is better to define a buffered stream. You start with the predefined buffered stream flavors, which define 2:tyi* or 2:tyo* themselves and manage the buffers for you. You must provide other operations that the system uses to obtain the next input buffer or to write or discard an output buffer. Flavors for defining buffered streams: 3si:buffered-input-stream* 1Flavor* This flavor is the basis for a non-character buffered input stream. It defines 2:tyi* as well as all the other standard input operations, but you must define the two operations 2:next-input-buffer* and 2:discard-input-buffer*, which the buffer management routines use. 3:next-input-buffer* 1Operation on 2si:buffered-input-stream** In a buffered input stream, this operation is used as a subroutine of the standard input operations, such as 2:tyi*, to get the next bufferful of input data. It should return three values: an array containing the data, a starting index in the array, and an ending index. For example, in a Chaosnet stream, this operation would get the next packet of input data and return pointers delimiting the actual data in the packet. 3:discard-input-buffer* 1buffer-array* 1Operation on 2si:buffered-input-stream** In a buffered input stream, this operation is used as a subroutine of the standard input operations such as 2:tyi*. It says that the buffer management routines have used or thrown away all the input in a buffer, and the buffer is no longer needed. In a Chaosnet stream, this operation would return the packet buffer to the pool of free packets. 3si:buffered-output-stream* 1Flavor* This flavor is the basis for a non-character buffered output stream. It defines 2:tyo* as well as all the other standard output operations, but you must define the operations 2:new-output-buffer*, 2:send-output-buffer* and 2:discard-output-buffer*, which the buffer management routines use. 3:new-output-buffer* 1Operation on 2si:buffered-output-stream** In a buffered output stream, this operation is used as a subroutine of the standard output operations, such as 2:tyo*, to get an empty buffer for storing more output data. How the buffer is obtained depends on the kind of stream, but in any case this operation should return an array (the buffer), a starting index, and an ending index. The two indices delimit the part of the array that is to be used as a buffer. For example, a Chaosnet stream would get a packet from the free pool and return indices delimiting the part of the packet array which can hold data bytes. 3:send-output-buffer* 1buffer-array* 1ending-index* 1Operation on 2si:buffered-output-stream** In a buffered output stream, this operation is used as a subroutine of the standard output operations, such as 2:tyo*, to send the data in a buffer that has been completely or partially filled. 1ending-index* is the first index in the buffer that has not actually been stored. This may not be the same as the ending index that was returned by the 2:new-output-buffer* operation that was used to obtain this buffer; if a 2:force-output* is being handled, 1ending-index* indicates how much of the buffer is currently full. The method for this operation should process the buffer's data and, if necessary, return the buffer to a free pool. 3:discard-output-buffer* 1buffer-array* 1Operation on 2si:buffered-output-stream** In a buffered output stream, this operation is used as a subroutine of the standard output operations, such as 2:clear-output*, to free an output buffer and say that the data in it should be ignored. It should simply return 1buffer-array* to a free pool, if appropriate. Some buffered output streams simply have one buffer array which they use over and over. For such streams, 2:new-output-buffer* can simply return that particular array each time; 2:send-output-buffer* and 2:discard-output-buffer* do not have to do anything about returning the buffer to a free pool. In fact, 2:discard-output-buffer* can probably do nothing. 3si:buffered-stream* 1Flavor* This is a combination of 2si:buffered-input-stream* and 2si:buffered-output-stream*, used to make a buffered bidirectional stream. The input and output buffering are completely independent of each other. You must define all five of the low level operations: 2:new-output-buffer*, 2:send-output-buffer* and 2:discard-output-buffer* for output, and 2:next-input-buffer* and 2:discard-input-buffer* for input. The data in most streams are characters. Character streams should support either 2:line-in* or 2:line-out* in addition to the other standard operations. 3si:unbuffered-line-input-stream* 1Flavor* This flavor is the basis for unbuffered character input streams. You need only define 2:tyi* and 2:untyi*. 3si:line-output-stream-mixin* 1Flavor* To make an unbuffered character output stream, mix this flavor into the one you define, together with 2si:output-stream*. In addition, you must define 2:tyo*, as for unbuffered non-character streams. 3si:buffered-input-character-stream* 1Flavor* This is used just like 2si:buffered-input-stream*, but it also provides the 2:line-in* operation and makes 2:characters* return 2t*. 3si:buffered-output-character-stream* 1Flavor* This is used just like 2si:buffered-output-stream*, but it also provides the 2:line-out* operation and makes 2:characters* return 2t*. 3si:buffered-character-stream* 1Flavor* This is used just like 2si:buffered-stream*, but it also provides the 2:line-in* and 2:line-out* operations and makes 2:characters* return 2t*. To make an unbuffered random-access stream, you need only define the 2:read-pointer* and 2:set-pointer* operations as appropriate. Since you provide the 2:tyi* or 2:tyo* handler yourself, the system cannot help you. In a buffered random-access stream, the random access operations must interact with the buffer management. The system provides for this. 3si:input-pointer-remembering-mixin* 1Flavor* Incorporate this into a buffered input stream to support random access. This flavor defines the 2:read-pointer* and 2:set-pointer* operations. If you wish 2:set-pointer* to work, you must provide a definition for the 2:set-buffer-pointer* operation. You need not do so if you wish to support only 2:read-pointer*. 3:set-buffer-pointer* 1new-pointer* 1Operation on 2si:input-pointer-remembering-mixin** You must define this operation if you use 2si:input-pointer-remembering-mixin* and want the 2:set-pointer* operation to work. This operation should arrange for the next 2:next-input-buffer* operation to provide a bufferful of data that includes the specified character or byte position somewhere inside it. The value returned should be the file pointer corresponding to the first character or byte of that next bufferful. 3si:output-pointer-remembering-mixin* 1Flavor* Incorporate this into a buffered output stream to support random access. This mixin defines the 2:read-pointer* and 2:set-pointer* operations. If you wish 2:set-pointer* to work, you must provide definitions for the 2:set-buffer-pointer* and 2:get-old-data* operations. You need not do so if you wish to support only 2:read-pointer*. 3:set-buffer-pointer* 1new-pointer* 1Operation on 2si:output-pointer-remembering-mixin** This is the same as in 2si:input-pointer-remembering-mixin*. 3:get-old-data* 1buffer-array* 1lower-output-limitOperation on 2si:output-pointer-remembering-mixin** The buffer management routines perform this operation when you do a 2:set-pointer* that is outside the range of pointers that fit in the current output buffer. They first send the old buffer, then do 2:set-buffer-pointer* as described above to say where in the file the next output buffer should come, then do 2:new-output-buffer* to get the new buffer. Then the 2:get-old-data* operation is performed. It should fill current buffer (1buffer-array*) with the 1old* contents of the file at the corresponding addresses, so that when the buffer is eventually written, any bytes skipped over by random access will retain their old values. The instance variable 2si:stream-output-lower-limit* is the starting index in the buffer of the part that is supposed to be used for output. 2si:stream-output-limit* is the ending index. The instance variable 2si:output-pointer-base* is the file pointer corresponding to the starting index in the buffer. 3si:file-stream-mixin* 1Flavor* Incorporate this mixin together with 2si:stream* to make a 1file probe stream*, which cannot do input or output but records the answers to an enquiry about a file. You should specify the init option 2:pathname* when you instantiate the flavor. You must provide definitions for the 2:plist* and 2:truename* operations; in terms of them, this mixin defines the operations 2:get*, 2:creation-date*, and 2:info*. 3si:input-file-stream-mixin* 1Flavor* Incorporate this mixin into input streams that are used to read files. You should specify the file's pathname with the 2:pathname* init option when you instantiate the flavor. In addition to the services and requirements of 2si:file-stream-mixin*, this mixin takes care of mentioning the file in the who-line. It also includes 2si:input-pointer-remembering-mixin* so that the 2:read-pointer* operation, at least, will be available. 3si:output-file-stream-mixin* 1Flavor* This is the analogue of 2si:input-file-stream-mixin* for output streams. =Node: 4Implementing Streams Without Flavors* =Text: 3IMPLEMENTING STREAMS WITHOUT FLAVORS* You do not need to use flavors to implement a stream. Any object that can be used as a function, and decodes its first argument appropriately as an operation name, can serve as a stream. Although in practice using flavors is as easy as any other way, it is educational to see how to define streams ``from scratch''. We could begin to define a simple output stream, which accepts characters and conses them onto a list, as follows: 3(defvar the-list nil)* 3(defun list-output-stream (op &optional arg1 &rest rest)* 3 (ecase op* 3 (:tyo* 3 (setq the-list (cons arg1 the-list)))* 3 (:which-operations '(:tyo))))* This is an output stream, and so it supports the 2:tyo* operation. All streams must support 2:which-operations*. The lambda-list for a stream defined with a 2defun* must always have one required parameter (1op*), one optional parameter (1arg1*), and a rest parameter (1rest*). This definition is not satisfactory, however. It handles 2:tyo* properly, but it does not handle 2:string-out*, 2:direction*, 2:send-if-handles*, and other standard operations. The function 2stream-default-handler* exists to spare us the trouble of defining all those operations from scratch in simple streams like this. By adding one additional clause, we let the default handler take care of all other operations, if it can. 3(defun list-output-stream (op &optional arg1 &rest rest)* 3 (selectq op* 3 (:tyo* 3 (setq the-list (cons arg1 the-list)))* 3 (:which-operations '(:tyo))* 3 (otherwise* 3 (stream-default-handler #'list-output-stream* 3 op arg1 rest))))* If the operation is not one that the stream understands (e.g. 2:string-out*), it calls 2stream-default-handler*. Note how the rest argument is passed to it. This is why the argument list must look the way it does. 2stream-default-handler* can be thought of as a restricted analogue of flavor inheritance. If we want to have only one stream of this sort, the symbol 2list-output-stream* can be used as the stream. The data output to it will appear in the global value of 2the-list*. One more step is required, though: 3(defprop list-output-stream t si:io-stream-p)* This tells certain functions including 2read* to treat the symbol 2list-output-stream* as a stream rather than as an end of file option. If we wish to be able to create any number of list output streams, each accumulating its own list, we must use closures: 3(defvar the-stream nil* 3 "Inside a list output stream, holds the stream itself.")* 3(defvar the-list nil* 3 "Inside a list output stream,* 3holds the list of characters being accumulated.")* 3(defun list-output-stream (op &optional arg1 &rest rest)* 3 (selectq op* 3 (:tyo* 3 (push arg1 the-list)))* 3(:withdrawal (prog1 the-list (setq the-list nil)))* 3 (:which-operations '(:tyo :withdrawal))* 3 (otherwise* 3 (stream-default-handler the-stream* 3 op arg1 rest))))* 3(defun make-list-output-stream ()* 3 (let ((the-stream the-list))* 3 (setq the-stream* 3 (closure '(the-stream the-list)* 3 'list-output-stream))))* We have added a new operation 2:withdrawal* that can be used to find out what data has been accumulated by a stream. This is necessary because we can no longer simply look at or set the global value of 2the-list*; that is not the same as the value closed into the stream. In addition, we have a new variable 2the-stream* which allows the function 2list-output-stream* to know which stream it is serving at any time. This variable is passed to 2stream-default-handler* so that when it simulates 2:string-out* by means of 2:tyo*, it can do the 2:tyo*'s to the same stream that the 2:string-out* was done to. The same stream could be defined with 2defselect* instead of 2defun*. It actually makes only a small difference. The 2defun* for 2list-output-stream* could be replaced with this code: 3(defselect (list-output-stream list-output-d-h)* 3 (:tyo (arg1)* 3 (push arg1 the-list))* 3 (:withdrawal ()* 3 (prog1 the-list (setq the-list nil))))* 3(defun list-output-d-h (op &optional arg1 &rest rest)* 3 (stream-default-handler the-stream op arg1 rest))* 2defselect* takes care of decoding the operations, provides a definition for 2:which-operations*, and allows you to write a separate lambda list for each operation. By comparison, the same stream defined using flavors looks like this: 3(defflavor list-output-stream ((the-list nil))* 3 (si:line-output-stream-mixin si:output-stream))* 3(defmethod (list-output-stream :tyo) (character)* 3 (push character the-list))* 3(defmethod (list-outut-stream :withdrawal) ()* 3 (prog1 the-list (setq the-list nil)))* 3(defun make-list-output-stream ()* 3 (make-instance 'list-output-stream))* Here is a simple input stream, which generates successive characters of a list. 3(defvar the-list)* 3;Put your input list here* 3(defvar the-stream)* 3(defvar untyied-char nil)* 3(defun list-input-stream (op &optional arg1 &rest rest)* 3 (selectq op* 3 (:tyi* 3 (cond ((not (null untyied-char))* 3 (prog1 untyied-char (setq untyied-char nil)))* 3 ((null the-list)* 3 (and arg1 (error arg1)))* 3 (t (pop the-list))))* 3 (:untyi* 3 (setq untyied-char arg1))* 3 (:which-operations '(:tyi :untyi))* 3 (otherwise* 3 (stream-default-handler the-stream* 3 op arg1 rest))))* 3(defun make-list-input-stream (the-list)* 3 (let (the-stream untyied-char)* 3 (setq the-stream* 3 (closure '(the-list the-stream untyied-char)* 3 'list-input-stream))))* The important things to note are that 2:untyi* must be supported, and that the stream must check for having reached the end of the information and do the right thing with the argument to the 2:tyi* operation. 3stream-default-handler* 1stream* 1op* 1arg1* 1rest* Tries to handle the 1op* operation on 1stream*, given arguments of 1arg1* and the elements of 1rest*. The exact action taken for each of the defined operations is explained with the documentation on that operation, above.