<?xml version="1.0" encoding="UTF-8"?>

<chapter id="gray"><title>Gray streams</title>

<section id="gray-overview"><title>Overview</title>
<para>This interface permits the definition of new classes of streams,
and programming their behavior by defining methods for the elementary
stream operations.
It is based on the proposal &gray-streams; of David N. Gray to X3J13
and is supported by most &cl; implementations currently in use.</para>

<para>All symbols defined by this interface, starting with the prefix
 <literal>FUNDAMENTAL-</literal> or <literal>STREAM-</literal>,
 are exported from the package &gray-pac;
 and &re-export;ed from &ext-pac;.</para>
</section>

<section id="gray-classes"><title>Defined classes</title>
<variablelist><title>Defined classes</title>
 <varlistentry id="fu-st"><term>&fu-st;</term>
  <listitem><simpara>This is a superclass of all user-defined streams.
   It is a subclass of &stream-t; and of &standard-object-t;.
   Its metaclass is &standard-class;.</simpara></listitem></varlistentry>
 <varlistentry id="fu-st-in"><term>&fu-st-in;</term>
  <listitem><simpara>This is a superclass of all user-defined &in-s;s.
   It is a subclass of &fu-st;.  The built-in function &input-stream-p;
   returns true on instances of this class.  This means that when you
   define a new stream class capable of doing input, you have to make it
   a subclass of &fu-st-in;.</simpara></listitem></varlistentry>
 <varlistentry id="fu-st-out"><term>&fu-st-out;</term>
  <listitem><simpara>This is a superclass of all user-defined &out-s;s.
   It is a subclass of &fu-st;.  The built-in function &output-stream-p;
   returns true on instances of this class.  This means that when you
   define a new stream class capable of doing output, you have to make
   it a subclass of &fu-st-out;.</simpara></listitem></varlistentry>
 <varlistentry id="fu-st-char"><term>&fu-st-char;</term>
  <listitem><simpara>This is a superclass of all user-defined streams
   whose &stream-element-type; is &character-t;.  It is a subclass of
   &fu-st;.  It defines a method on &stream-element-type; that returns
   &character-t;.</simpara></listitem></varlistentry>
 <varlistentry id="fu-st-bin"><term>&fu-st-bin;</term>
  <listitem><simpara>This is a superclass of all user-defined streams
   whose &stream-element-type; is a subtype of &integer-t;.  It is a
   subclass of &fu-st;.  When you define a subclass of &fu-st-bin;,
   you have to provide a method on &stream-element-type;.
  </simpara></listitem></varlistentry>
 <varlistentry>
    <term><classname>GRAY:FUNDAMENTAL-CHARACTER-INPUT-STREAM</classname></term>
  <listitem><simpara>This is a convenience class inheriting from both
   &fu-st-char; and &fu-st-in;.</simpara></listitem></varlistentry>
 <varlistentry>
   <term><classname>GRAY:FUNDAMENTAL-CHARACTER-OUTPUT-STREAM</classname></term>
  <listitem><simpara>This is a convenience class inheriting from both
   &fu-st-char; and &fu-st-out;.</simpara></listitem></varlistentry>
 <varlistentry>
    <term><classname>GRAY:FUNDAMENTAL-BINARY-INPUT-STREAM</classname></term>
  <listitem><simpara>This is a convenience class inheriting from both
   &fu-st-bin; and &fu-st-in;.</simpara></listitem></varlistentry>
 <varlistentry>
    <term><classname>GRAY:FUNDAMENTAL-BINARY-OUTPUT-STREAM</classname></term>
  <listitem><simpara>This is a convenience class inheriting from both
   &fu-st-bin; and &fu-st-out;.</simpara></listitem></varlistentry>
</variablelist></section>

<section id="gray-gf-general"><title>General generic functions</title>
<variablelist><title>General generic functions defined on streams</title>
 <varlistentry><term><code>(&stream-element-type; &stream-r;)</code></term>
  <listitem><simpara>Returns the stream's element type, normally a
   subtype of &character-t; or &integer-t;.</simpara>
  <simpara>The method for &fu-st-char; returns &character-t;.
  </simpara></listitem></varlistentry>
 <varlistentry><term><code>((&setf; &stream-element-type;)
    <replaceable>new-element-type</replaceable> &stream-r;)</code></term>
  <listitem><simpara>Changes the stream's element type.</simpara>
   <simpara>The default method &signal;s an &error-t;.</simpara>
   <simpara>This function is a &clisp; extension (see <xref
    linkend="stream-eltype"/>).</simpara>
   </listitem></varlistentry>
 <varlistentry><term><code>(&close; &stream-r; &key-amp;
    &abort-k;)</code></term>
  <listitem><simpara>Closes the stream and flushes any associated buffers.
   </simpara><simpara>When you define a primary method on this
    function, do not forget to &call-next-method;.
  </simpara></listitem></varlistentry>
 <varlistentry><term><code>(&open-stream-p; &stream-r;)</code></term>
  <listitem><simpara>Returns true before the stream has been closed, and
    &nil; after the stream has been closed.</simpara>
   <simpara>You do not need to add methods to this function.</simpara>
   </listitem></varlistentry>
 <varlistentry id="st-position"><term><code>(&st-position;
    &stream-r; <replaceable>position</replaceable>)</code></term>
  <listitem><simpara>Just like &file-position;, but &nil;
    <replaceable>position</replaceable> means inquire.</simpara>
   &need-defmethod;</listitem></varlistentry>
 <varlistentry id="st-rd-seq"><term><code>(GRAY:STREAM-READ-SEQUENCE
   &sequence-r; &stream-r; &key-amp; &start-k; &end-k;)</code></term>
  <listitem><simpara>Used by &read-sequence;. <emphasis>Deprecated</emphasis>.
    Define &st-rd-cs; or &st-rd-bs; and call &rd-ch-seq;/&rd-by-seq; instead.
   </simpara><simpara>The default method calls &st-rd-cs; or &st-rd-bs;.
 </simpara></listitem></varlistentry>
 <varlistentry id="st-wr-seq"><term><code>(GRAY:STREAM-WRITE-SEQUENCE
   &sequence-r; &stream-r; &key-amp; &start-k; &end-k;)</code></term>
  <listitem><simpara>Used by &write-sequence;. <emphasis>Deprecated</emphasis>.
    Define &st-wr-cs; or &st-wr-bs; and call &wr-ch-seq;/&wr-by-seq; instead.
   </simpara><simpara>The default method calls &st-wr-cs; or &st-wr-bs;.
 </simpara></listitem></varlistentry>
</variablelist></section>

<section id="gray-gf-char-in">
 <title>Generic functions for character input</title>
<variablelist><title>Generic functions for character input</title>
 <varlistentry id="st-rc"><term><code>(&st-rc; &stream-r;)</code></term>
  <listitem><simpara>If a character was pushed back using &st-uc;,
   returns and consumes it.  Otherwise returns and consumes the next
   character from the stream. Returns &eof-k; if the &eos; is reached.
   </simpara>&need-defmethod;</listitem></varlistentry>
 <varlistentry id="st-uc"><term><code>(&st-uc; &stream-r; &ch-r;)</code></term>
  <listitem><simpara>Pushes &ch-r;, which must be the last character
   read from the &stream-r;, back onto the front of the &stream-r;.
   </simpara>&need-defmethod;</listitem></varlistentry>
 <varlistentry id="st-rcnh"><term><code>(&st-rcnh; &stream-r;)</code></term>
  <listitem><simpara>Returns a character or &eof-k;, like &st-rc;, if
   that would return immediately.  If &st-rc;'s value is not available
   immediately, returns &nil; instead of waiting.</simpara><simpara>
   The default method simply calls &st-rc;; this is sufficient for streams
   whose &st-rc; method never blocks.</simpara></listitem></varlistentry>
 <varlistentry><term><code>(GRAY:STREAM-PEEK-CHAR &stream-r;)</code></term>
  <listitem><simpara>If a character was pushed back using &st-uc;,
   returns it.  Otherwise returns the next character from the stream,
   avoiding any side effects &st-rc; would do.  Returns &eof-k; if the
   &eos; is reached.</simpara>
  <simpara>The default method calls &st-rc; and &st-uc;; this is
   sufficient for streams whose &st-rc; method has no
   side-effects.</simpara></listitem></varlistentry>
 <varlistentry><term><code>(GRAY:STREAM-LISTEN &stream-r;)</code></term>
  <listitem><simpara>If a character was pushed back using &st-uc;,
   returns it.  Otherwise returns the next character from the stream, if
   already available.  If no character is available immediately, or if
   &eos; is reached, returns &nil;.</simpara>
  <simpara>The default method calls &st-rcnh; and &st-uc;; this is
   sufficient for streams whose &st-rc; method has no
   side-effects.</simpara></listitem></varlistentry>
 <varlistentry><term><code>(GRAY:STREAM-READ-CHAR-WILL-HANG-P
    &stream-r;)</code></term>
  <listitem><simpara>Returns &nil; if &st-rc; will return immediately.
   Otherwise it returns true.</simpara>
  <simpara>The default method calls &st-rcnh; and &st-uc;; this is
   sufficient for streams whose &st-rc; method has no side-effects.</simpara>
  <simpara>This function is a &clisp; extension (see &rcwhp;).</simpara>
  </listitem></varlistentry>
 <varlistentry id="st-rd-cs"><term><code>(&st-rd-cs;
   &stream-r; &sequence-r; &optional-amp; [&start-r; [&end-r;]])</code></term>
  <listitem><simpara>Fills the subsequence of &sequence-r; specified by
   &start-k; and &end-k; with characters consecutively read from &stream-r;.
   Returns the index of the first element of &sequence-r; that was not
   updated (&areq; &end-r;, or &lst; &end-r; if the stream reached its end).
   </simpara>
  <simpara>&sequence-r; is an &array-t; of &character-t;s, i.e. a &string-t;.
   &start-r; is a nonnegative &integer-t; and defaults to &zero;.
   &end-r; is a nonnegative &integer-t; or &nil; and defaults to &nil;,
   which stands for <code>(&length; &sequence-r;)</code>.
  </simpara><simpara>The default method repeatedly calls &st-rc;; this
   is always sufficient if speed does not matter.</simpara>
  <simpara>This function is a &clisp; extension (see
   &rd-ch-seq;).</simpara></listitem></varlistentry>
 <varlistentry><term><code>(GRAY:STREAM-READ-LINE &stream-r;)</code></term>
  <listitem><simpara>Reads a line of characters, and return two values:
   the line (a &string-t;, without the terminating &nl-s; character),
   and a &boolean-t; value which is true if the line was terminated by
   &eos; instead of &nl-s;.</simpara>
  <simpara>The default method repeatedly calls &st-rc;; this
   is always sufficient.</simpara></listitem></varlistentry>
 <varlistentry><term><code>(GRAY:STREAM-CLEAR-INPUT &stream-r;)</code></term>
  <listitem><simpara>Clears all pending interactive input from the
   &stream-r;, and returns true if some pending input was removed.</simpara>
  <simpara>The default method does nothing and returns &nil;; this is
   sufficient for non-interactive streams.</simpara></listitem></varlistentry>
</variablelist></section>

<section id="gray-gf-char-out">
 <title>Generic functions for character output</title>
<variablelist><title>Generic functions for character output</title>
 <varlistentry id="st-wc"><term><code>(&st-wc; &stream-r; &ch-r;)</code></term>
  <listitem><simpara>Writes &ch-r;.
   </simpara>&need-defmethod;</listitem></varlistentry>
 <varlistentry id="st-lc"><term><code>(&st-lc; &stream-r;)</code></term>
  <listitem><simpara>Returns the column number where the next character
   would be written (&zero; stands for the first column),
    or &nil; if that is not meaningful for this stream.
   </simpara>&need-defmethod;</listitem></varlistentry>
 <varlistentry><term><code>(GRAY:STREAM-START-LINE-P &stream-r;)</code></term>
  <listitem><simpara>Returns true if the next character would be
   written at the start of a new line.</simpara>
  <simpara>The default method calls &st-lc; and compares its result with
   0; this is sufficient for streams whose &st-lc; never returns &nil;.
  </simpara></listitem></varlistentry>
 <varlistentry id="st-wr-cs"><term><code>(&st-wr-cs;
    &stream-r; &sequence-r; &optional-amp; [&start-r; [&end-r;]])</code></term>
  <listitem><simpara>Outputs the subsequence of &sequence-r; specified
   by &start-k; and &end-k; to &stream-r;.</simpara>
  <simpara>&sequence-r; is an &array-t; of &character-t;s, i.e. a &string-t;.
   &start-r; is a nonnegative &integer-t; and defaults to 0.
   &end-r; is a nonnegative integer or &nil; and defaults to &nil;,
   which stands for <code>(&length; &sequence-r;)</code>.
  </simpara><simpara>The default method repeatedly calls &st-wc;; this
   is always sufficient if speed does not matter.
  </simpara><simpara>This function is a &clisp; extension
   (see &wr-ch-seq;).</simpara></listitem></varlistentry>
 <varlistentry><term><code>(GRAY:STREAM-WRITE-STRING
    &stream-r; &string-r; &optional-amp; [&start-r; [&end-r;]])</code></term>
  <listitem><simpara>Outputs the subsequence of &string-r; specified by
   &start-k; and &end-k; to &stream-r;.  Returns &string-r;.</simpara>
   <simpara>&string-r; is a string.  &start-r; is a nonnegative integer
   and default to 0.  &end-r; is a nonnegative integer or &nil; and
   defaults to &nil;, which stands for <code>(&length; &string-r;)</code>.
   </simpara><simpara>The default method calls &st-wr-cs;;
   this is always sufficient.</simpara></listitem></varlistentry>
 <varlistentry><term><code>(GRAY:STREAM-TERPRI &stream-r;)</code></term>
  <listitem><simpara>Outputs a &nl-s; character.</simpara>
  <simpara>The default method calls &st-wc;; this is always
   sufficient.</simpara></listitem></varlistentry>
 <varlistentry><term><code>(GRAY:STREAM-FRESH-LINE &stream-r;)</code></term>
  <listitem><simpara>Possibly outputs a &nl-s; character, so as to ensure
   that the next character would be written at the start of a new line.
   Returns true if it did output a &nl-s; character.</simpara>
  <simpara>The default method calls
   <function>GRAY:STREAM-START-LINE-P</function> and then
   <function>GRAY:STREAM-TERPRI</function> if necessary; this is always
   sufficient.</simpara></listitem></varlistentry>
 <varlistentry><term><code>(GRAY:STREAM-FINISH-OUTPUT &stream-r;)</code></term>
  <listitem><simpara>Ensures that any buffered output has reached its
   destination, and then returns.</simpara>
  <simpara>The default method does nothing.</simpara></listitem></varlistentry>
 <varlistentry><term><code>(GRAY:STREAM-FORCE-OUTPUT &stream-r;)</code></term>
  <listitem><simpara>Brings any buffered output on its way towards its
   destination, and returns without waiting until it has reached its
   destination.</simpara>
  <simpara>The default method does nothing.</simpara></listitem></varlistentry>
 <varlistentry><term><code>(GRAY:STREAM-CLEAR-OUTPUT &stream-r;)</code></term>
  <listitem><simpara>Attempts to discard any buffered output which has
   not yet reached its destination.</simpara>
  <simpara>The default method does nothing.</simpara></listitem></varlistentry>
 <varlistentry><term><code>(GRAY:STREAM-ADVANCE-TO-COLUMN
    &stream-r; &col-r;)</code></term>
  <listitem><simpara>Ensures that the next character will be written at
    least at &col-r;.</simpara>
  <simpara>The default method outputs an appropriate amount of space
   characters; this is sufficient for non-proportional output.</simpara>
 </listitem></varlistentry>
</variablelist></section>

<section id="gray-gf-byte-in">
 <title>Generic functions for binary input</title>
<variablelist><title>Generic functions for binary input</title>
 <varlistentry id="st-rb"><term><code>(&st-rb; &stream-r;)</code></term>
  <listitem><simpara>Returns and consumes the next integer from the
   stream. Returns &eof-k; if the &eos; is reached.</simpara>
   &need-defmethod;</listitem></varlistentry>
 <varlistentry id="st-rbla"><term><code>(&st-rbla; &stream-r;)</code></term>
  <listitem><simpara>To be called only if &stream-r;'s
   &stream-element-type; is &ubyte-8; or &sbyte-8;.
   Returns &t; if &st-rb; would return immediately with an
   &integer-t; result.  Returns &eof-k; if the &eos; is already
   known to be reached.  If &st-rb;'s value is not available
   immediately, returns &nil; instead of waiting.</simpara>
   &need-defmethod;<simpara>This function is a &clisp; extension (see
   &rbla;).</simpara></listitem></varlistentry>
 <varlistentry id="st-rbwhp"><term><code>(&st-rbwhp; &stream-r;)</code></term>
  <listitem><simpara>To be called only if &stream-r;'s
   &stream-element-type; is &ubyte-8; or &sbyte-8;.
   Returns &nil; if &st-rb; will return immediately.
   Otherwise it returns true.</simpara>
   <simpara>The default method calls &st-rbla;; this is always sufficient.
   </simpara><simpara>This function is a &clisp; extension (see &rbwhp;).
 </simpara></listitem></varlistentry>
 <varlistentry id="st-rbnh"><term><code>(&st-rbnh; &stream-r;)</code></term>
  <listitem><simpara>To be called only if &stream-r;'s
   &stream-element-type; is &ubyte-8; or &sbyte-8;.
   Returns an &integer-t; or &eof-k;, like &st-rb;, if that would
   return immediately.  If &st-rb;'s value is not available immediately,
   returns &nil; instead of waiting.</simpara>
   <simpara>The default method calls &st-rb; if &st-rbla; returns true;
    this is always sufficient.</simpara>
   <simpara>This function is a &clisp; extension (see &rbnh;).
 </simpara></listitem></varlistentry>
 <varlistentry id="st-rd-bs"><term><code>(&st-rd-bs;
    &stream-r; &sequence-r; &optional-amp;
    [&start-r; [&end-r; [&no-hang; [&interactive-r;]]]])</code></term>
  <listitem><simpara>Fills the subsequence of &sequence-r; specified by
    &start-k; and &end-k; with integers consecutively read from &stream-r;.
    Returns the index of the first element of &sequence-r; that was not
    updated (&areq; &end-r;, or &lst; &end-r; if the stream reached its end).
   </simpara><simpara>&sequence-r; is an &array-t; of &integer-t;s.
    &start-r; is a nonnegative &integer-t; and defaults to 0.
    &end-r; is a nonnegative &integer-t; or &nil; and defaults to &nil;,
    which stands for <code>(&length; &sequence-r;)</code>.
    If &no-hang; is true, the function should avoid blocking and instead fill
    only as many elements as are immediately available. If &no-hang; is false
    and &interactive-r; is true, the function can block for reading the first
    byte but should avoid blocking for any further bytes.</simpara>
   <simpara>The default method repeatedly calls &st-rb;; this
    is always sufficient if speed does not matter.</simpara>
   <simpara>This function is a &clisp; extension (see
    &rd-by-seq;).</simpara></listitem></varlistentry>
</variablelist></section>

<section id="gray-gf-byte-out">
 <title>Generic functions for binary output</title>
<variablelist><title>Generic functions for binary output</title>
 <varlistentry><term><code>(GRAY:STREAM-WRITE-BYTE
    &stream-r; &int-r;)</code></term>
  <listitem><simpara>Writes &int-r;.</simpara>
   &need-defmethod;</listitem></varlistentry>
 <varlistentry id="st-wr-bs"><term><code>(&st-wr-bs;
    &stream-r; &sequence-r; &optional-amp;
    [&start-r; [&end-r; [&no-hang; [&interactive-r;]]]])</code></term>
  <listitem><simpara>Outputs the subsequence of &sequence-r; specified
    by &start-k; and &end-k; to &stream-r;</simpara>
   <simpara>&sequence-r; is an &array-t; of &integer-t;s.
    &start-r; is a nonnegative &integer-t; and defaults to 0.
    &end-r; is a nonnegative &integer-t; or &nil; and defaults to &nil;,
    which stands for <code>(&length; &sequence-r;)</code>.
    If &no-hang; is true, the function should avoid blocking and instead output
    only as many elements as it can immediately proceed. If &no-hang; is false
    and &interactive-r; is true, the function can block for writing the first
    byte but should avoid blocking for any further bytes.</simpara>
   <simpara>The default method repeatedly calls
    <function>GRAY:STREAM-WRITE-BYTE</function>; this is always
    sufficient if speed does not matter.</simpara>
   <simpara>This function is a &clisp; extension (see
    &wr-by-seq;).</simpara></listitem></varlistentry>
</variablelist></section>

<section id="fill-stream"><title>Class &fill-stream;</title>
<para>As an example of the use of &gray-pac; &stream-t;s, &clisp;
 offers an additional class, &fill-stream;.  An instance of this class
 is a <quote>formatting</quote> &stream-t;, which makes the final
 output to the underlying stream look neat: indented and filled.
 An instance of &fill-stream; is created like this:
<programlisting language="lisp">
(&make-instance; '&fill-stream; :stream &stream-r;
               [:text-indent symbol-or-number]
               [:sexp-indent symbol-or-number-or-function])
</programlisting>where<variablelist>
 <varlistentry><term>&stream-r;</term>
  <listitem><simpara>is the target &stream-t; where the output actually
    goes.</simpara></listitem></varlistentry>
 <varlistentry><term><replaceable>symbol-or-number</replaceable></term>
  <listitem><simpara>is the variable whose value is the &integer-t; text
    indentation or the indentation itself (defaults to 0).
 </simpara></listitem></varlistentry>
 <varlistentry><term><replaceable>symbol-or-number-or-function</replaceable></term>
  <listitem><para>When &format; writes an S-expression to a
    &fill-stream; using &format-s;, and the expression's printed
    representation does not fit on the current line, it is printed on
    separate lines, ignoring the prescribed text indentation and
    preserving spacing.  When this argument is non-&nil;, the
    S-expression &is-e; indented by:<variablelist>
     <varlistentry><term>&t;</term>
      <listitem><simpara>the text indentation above;
     </simpara></listitem></varlistentry>
     <varlistentry><term>&symbol-t;</term>
      <listitem><simpara>&symbol-value; is the indentation;
     </simpara></listitem></varlistentry>
     <varlistentry><term>&integer-t;</term>
      <listitem><simpara>the indentation itself;
     </simpara></listitem></varlistentry>
     <varlistentry><term>&function-t;</term>
      <listitem><simpara>called with one argument, the text indentation,
        and the value is used as S-expression indentation;
        thus &identity; is equivalent to &t; above.
    </simpara></listitem></varlistentry></variablelist>
    Defaults to &fill-indent-sexp;, whose initial value is &pl1;.
</para></listitem></varlistentry></variablelist></para>

<para></para>

<warning><para>Note that, due to buffering, one must call &force-output;
 when done with the &fill-stream; (and before changing the indent variable).
 The former is done automatically by the macro
<code>(with-fill-stream (fill target-stream ...) ...)</code>.</para></warning>

<example id="ex-fill-stream"><title>Example of &fill-stream; usage</title>
 <programlisting language="lisp">
(defvar *my-indent-level*)
(with-output-to-string (out)
  (let ((*print-right-margin* 20)
        (*print-pretty* t)
        (*my-indent-level* 2))
    (with-fill-stream (fill out :text-indent '*my-indent-level*)
      (format fill "~%this is some long sentence which will      be broken at spaces")
      (force-output fill)
      (let ((*my-indent-level* 5))
        (format fill "~%and    properly indented to the level specified by the ~S argument which can be a ~S or an ~S - cool!"
                :TEXT-INDENT 'symbol 'integer)
        (force-output fill))
      (format fill "~%Don't forget  to call ~S on it, and/or use ~S   Pretty formatting of the  S-expressions    printed with ~~S is  preserved: ~S"
              'force-output 'with-fill-stream '(defun foo (x y z) (if x (+ y z) (* y z)))))))
<computeroutput>"
  this is some long
  sentence which
  will be broken at
  spaces
     and properly
     indented to
     the level
     specified by
     the
     :TEXT-INDENT
     argument which
     can be a
     SYMBOL or an
     INTEGER -
     cool!
  Don't forget to
  call FORCE-OUTPUT
  on it, and/or use
  WITH-FILL-STREAM
  Pretty formatting
  of the
  S-expressions
  printed with ~S
  is preserved:
   (DEFUN FOO
    (X Y Z)
    (IF X (+ Y Z)
     (* Y Z)))
"</computeroutput></programlisting>
</example>
</section>

</chapter>
