;;; gray-streams.lisp
;;;
;;; Copyright (C) 2004-2007 Peter Graves, Andras Simon
;;; $Id$
;;;
;;; This program is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU General Public License
;;; as published by the Free Software Foundation; either version 2
;;; of the License, or (at your option) any later version.
;;;
;;; This program is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this program; if not, write to the Free Software
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
;;;
;;; As a special exception, the copyright holders of this library give you
;;; permission to link this library with independent modules to produce an
;;; executable, regardless of the license terms of these independent
;;; modules, and to copy and distribute the resulting executable under
;;; terms of your choice, provided that you also meet, for each linked
;;; independent module, the terms and conditions of the license of that
;;; module.  An independent module is a module which is not derived from
;;; or based on this library.  If you modify this library, you may extend
;;; this exception to your version of the library, but you are not
;;; obligated to do so.  If you do not wish to do so, delete this
;;; exception statement from your version.

;;; Adapted from:
;;;; Gray Streams Implementation for Corman Lisp - Version 1.3
;;;;
;;;; Copyright (C) 2000 Christopher Double. All Rights Reserved.
;;;;
;;;; License
;;;; =======
;;;; This software is provided 'as-is', without any express or implied
;;;; warranty. In no event will the author be held liable for any damages
;;;; arising from the use of this software.
;;;;
;;;; Permission is granted to anyone to use this software for any purpose,
;;;; including commercial applications, and to alter it and redistribute
;;;; it freely, subject to the following restrictions:
;;;;
;;;; 1. The origin of this software must not be misrepresented; you must
;;;;    not claim that you wrote the original software. If you use this
;;;;    software in a product, an acknowledgment in the product documentation
;;;;    would be appreciated but is not required.
;;;;
;;;; 2. Altered source versions must be plainly marked as such, and must
;;;;    not be misrepresented as being the original software.
;;;;
;;;; 3. This notice may not be removed or altered from any source
;;;;    distribution.
;;;;
;;;; Notes
;;;; =====
;;;;
;;;; NB: The ABCL implementation has been extensively reworked since these
;;;; notes were included.  Please see the ABCL revision history via
;;;; the interface at
;;;;
;;;; http://trac.common-lisp.net/armedbear/browser/trunk/abcl/src/org/armedbear/lisp/gray-streams.lisp 
;;;;
;;;; for a more relevant history vis a vis the ABCL implementation.
;;;;
;;;; A simple implementation of Gray streams for Corman Lisp 1.42.
;;;; Gray streams are 'clos' based streams as described at:
;;;;
;;;; ftp://parcftp.xerox.com/pub/cl/cleanup/mail/stream-definition-by-user.mail
;;;;
;;;; 20110319 
;;;;   The xerox.com ftp URI doesn't resolve.  Instead see Kent Pitman's
;;;;   archival copy at
;;;;
;;;;     http://www.nhplace.com/kent/CL/Issues/stream-definition-by-user.html
;;;;
;;;; Some differences exist between this implementation and the
;;;; specification above. See notes below for details.
;;;;
;;;; More recent versions of this software may be available at:
;;;;   http://www.double.co.nz/cl
;;;;
;;;; Comments, suggestions and bug reports to the author,
;;;; Christopher Double, at: chris@double.co.nz
;;;;
;;;; 03/03/2001 - 1.0
;;;;              Initial release.
;;;;
;;;; 20/08/2001 - 1.1
;;;;              Small modifications by Frederic Bastenaire (fba@free.fr)
;;;;              (lines flagged by  ;; # fb 1.01)
;;;;              - Make it work with the READ function by
;;;;                defining %read-char, %read-char-with-error
;;;;               and input-character-stream-p
;;;;              - Add nickname GS to package "GRAY-STREAMS" for ease of use
;;;;              - added missing '*' to *old-write-byte* in gray-write-byte
;;;;
;;;; 03/01/2002 - 1.2
;;;;              Fixed bug with GRAY-WRITE-LINE and GRAY-WRITE-STRING
;;;;              that appeared in Corman Lisp 2.0 due to changes to
;;;;              WRITE-LINE and WRITE-STRING.
;;;;
;;;; 04/01/2002 - 1.3
;;;;              Added support for STREAM-READ-SEQUENCE and STREAM-WRITE-SEQUENCE.
;;;;              Fixed STREAM-WRITE-STRING bug.
;;;;
;;;; Notes
;;;; =====
;;;;
;;;;
;;;; Much of the implementation of the Gray streams below is from the
;;;; document referenced earlier.
;;;;
(require "PPRINT")

(defpackage "GRAY-STREAMS"
  (:use
   "COMMON-LISP")
  (:nicknames "GS") ;; # fb 1.01
  (:export
   "FUNDAMENTAL-STREAM"
   "FUNDAMENTAL-OUTPUT-STREAM"
   "FUNDAMENTAL-INPUT-STREAM"
   "FUNDAMENTAL-CHARACTER-STREAM"
   "FUNDAMENTAL-BINARY-STREAM"
   "STREAM-READ-BYTE"
   "STREAM-WRITE-BYTE"
   "FUNDAMENTAL-CHARACTER-INPUT-STREAM"
   "STREAM-READ-CHAR"
   "STREAM-UNREAD-CHAR"
   "STREAM-READ-CHAR-NO-HANG"
   "STREAM-PEEK-CHAR"
   "STREAM-LISTEN"
   "STREAM-READ-LINE"
   "STREAM-CLEAR-INPUT"
   "FUNDAMENTAL-CHARACTER-OUTPUT-STREAM"
   "STREAM-WRITE-CHAR"
   "STREAM-LINE-COLUMN"
   "STREAM-LINE-LENGTH"
   "STREAM-START-LINE-P"
   "STREAM-WRITE-STRING"
   "STREAM-TERPRI"
   "STREAM-FRESH-LINE"
   "STREAM-FINISH-OUTPUT"
   "STREAM-FORCE-OUTPUT"
   "STREAM-CLEAR-OUTPUT"
   "STREAM-ADVANCE-TO-COLUMN"
   "STREAM-READ-SEQUENCE"
   "STREAM-WRITE-SEQUENCE"
   "STREAM-FILE-POSITION"
   "STREAM-FILE-LENGTH"
   "STREAM-FILE-STRING-LENGTH"
   "STREAM-ELEMENT-TYPE"
   "FUNDAMENTAL-BINARY-INPUT-STREAM"
   "FUNDAMENTAL-BINARY-OUTPUT-STREAM"))

(in-package :gray-streams)

(defvar *ansi-read-char* #'read-char)
(defvar *ansi-peek-char* #'peek-char)
(defvar *ansi-unread-char* #'unread-char)
(defvar *ansi-listen* #'listen)
(defvar *ansi-read-line* #'read-line)
(defvar *ansi-read-char-no-hang* #'read-char-no-hang)
(defvar *ansi-write-char* #'write-char)
(defvar *ansi-fresh-line* #'fresh-line)
(defvar *ansi-terpri* #'terpri)
(defvar *ansi-write-string* #'write-string)
(defvar *ansi-write-line* #'write-line)
(defvar *sys-%force-output* #'sys::%force-output)
(defvar *sys-%finish-output* #'sys::%finish-output)
(defvar *sys-%clear-output* #'sys::%clear-output)
(defvar *sys-%output-object* #'sys::%output-object)
(defvar *ansi-clear-input* #'clear-input)
(defvar *ansi-read-byte* #'read-byte)
(defvar *ansi-write-byte* #'write-byte)
(defvar *ansi-stream-element-type* #'cl::stream-element-type)
(defvar *ansi-stream-external-format* #'cl::stream-external-format)
(defvar *ansi-close* #'cl::close)
(defvar *ansi-input-character-stream-p*
  #'(lambda (s) (and (input-stream-p s) (eql (stream-element-type s) 'character))))
(defvar *ansi-input-stream-p* #'cl::input-stream-p)
(defvar *ansi-output-stream-p* #'cl::output-stream-p)
(defvar *ansi-interactive-stream-p* #'cl::interactive-stream-p)
(defvar *ansi-open-stream-p* #'cl::open-stream-p)
(defvar *ansi-streamp* #'cl::streamp)
(defvar *ansi-read-sequence* #'cl::read-sequence)
(defvar *ansi-write-sequence* #'cl::write-sequence)
(defvar *ansi-make-two-way-stream* #'cl:make-two-way-stream)
(defvar *ansi-two-way-stream-input-stream* #'cl:two-way-stream-input-stream)
(defvar *ansi-two-way-stream-output-stream* #'cl:two-way-stream-output-stream)
(defvar *ansi-file-position* #'cl:file-position)
(defvar *ansi-file-length* #'cl:file-length)
(defvar *ansi-file-string-length* #'cl:file-string-length)
(defvar *ansi-pathname* #'cl:pathname)
(defvar *ansi-truename* #'cl:truename)

(defun ansi-streamp (stream)
  (typep stream '(or sys::system-stream xp::xp-structure)))

(defclass fundamental-stream (standard-object stream)
  ((open-p :initform t
           :accessor stream-open-p))
  (:documentation "The base class of all Gray streams"))

(defgeneric gray-close (stream &key abort))
(defgeneric gray-open-stream-p (stream))
(defgeneric gray-streamp (stream))
(defgeneric gray-input-stream-p (stream))
(defgeneric gray-input-character-stream-p (stream)) ;; # fb 1.01
(defgeneric gray-output-stream-p (stream))
(defgeneric gray-interactive-stream-p (stream))
(defgeneric gray-stream-element-type (stream))
(defgeneric (setf gray-stream-element-type) (new-value stream))
(defgeneric gray-stream-external-format (stream))
(defgeneric (setf gray-stream-external-format) (new-value stream))
(defgeneric gray-pathname (pathspec))
(defgeneric gray-truename (filespec))

(defun assert-stream (stream)
  (if (gray-streamp stream)
      t
      (error 'type-error :datum stream :expected-type 'stream)))

(defun bug-or-error (stream fun)
  (assert-stream stream)
  (error "The stream ~S has no suitable method for ~S." stream fun))

(defmethod gray-close ((stream fundamental-stream) &key abort)
  (declare (ignore abort))
  (setf (stream-open-p stream) nil)
  t)

(defmethod gray-open-stream-p ((stream fundamental-stream))
  (stream-open-p stream))

(defmethod gray-streamp ((s fundamental-stream))
  s)

(defmethod gray-stream-external-format ((s fundamental-stream))
  :default)

(defmethod gray-interactive-stream-p (stream)
  (declare (ignore stream))
  nil)

(defclass fundamental-input-stream (fundamental-stream) ())

(defmethod gray-input-character-stream-p (s)  ;; # fb 1.01
  (and (gray-input-stream-p s)
       (eq (gray-stream-element-type s) 'character)))

(defmethod gray-input-stream-p ((s fundamental-input-stream))
  (declare (ignore s))
  t)

(defclass fundamental-output-stream (fundamental-stream) ())

(defmethod gray-input-stream-p ((s fundamental-output-stream))
  (typep s 'fundamental-input-stream))

(defmethod gray-output-stream-p ((s fundamental-output-stream))
  (declare (ignore s))
  t)

(defmethod gray-output-stream-p ((s fundamental-input-stream))
  (typep s 'fundamental-output-stream))

(defclass fundamental-character-stream (fundamental-stream) ())

(defmethod gray-stream-element-type ((s fundamental-character-stream))
  (declare (ignore s))
  'character)

(defclass fundamental-binary-stream (fundamental-stream) ())

(defgeneric stream-read-byte (stream))
(defgeneric stream-write-byte (stream integer))

(defclass fundamental-character-input-stream
  (fundamental-input-stream fundamental-character-stream) ())

(defgeneric stream-read-char (stream))
(defgeneric stream-unread-char (stream character))
(defgeneric stream-read-char-no-hang (stream))
(defgeneric stream-peek-char (stream))
(defgeneric stream-listen (stream))
(defgeneric stream-read-line (stream))
(defgeneric stream-clear-input (stream))

(defmethod stream-read-char-no-hang ((stream fundamental-character-input-stream))
  (stream-read-char stream))

(defmethod stream-peek-char ((stream fundamental-character-input-stream))
  (let ((character (stream-read-char stream)))
    (unless (eq character :eof)
      (stream-unread-char stream character))
    character))

(defmethod stream-listen ((stream  fundamental-character-input-stream))
  (let ((char (stream-read-char-no-hang stream)))
    (and (not (null char))
         (not (eq char :eof))
         (progn
           (stream-unread-char stream char)
           t))))

(defmethod stream-read-line ((stream  fundamental-character-input-stream))
  (let ((line (make-array 64
                          :element-type 'character
                          :fill-pointer 0
                          :adjustable t)))
    (loop
      (let ((character (stream-read-char stream)))
        (if (eq character :eof)
            (return (values line t))
            (if (eql character #\Newline)
                (return (values line nil))
                (vector-push-extend character line)))))))

(defmethod stream-clear-input (stream)
  (declare (ignore stream))
  nil)

(defclass fundamental-character-output-stream
  (fundamental-output-stream fundamental-character-stream) ())

(defgeneric stream-write-char (stream character))
(defgeneric stream-line-column (stream))
(defgeneric stream-start-line-p (stream))
(defgeneric stream-line-length (stream))
(defgeneric stream-write-string (stream string &optional start end))
(defgeneric stream-terpri (stream))
(defmethod stream-terpri (stream)
  (stream-write-char stream #\Newline)
  nil)

(defgeneric stream-fresh-line (stream))
(defgeneric stream-finish-output (stream))
(defgeneric stream-force-output (stream))
(defgeneric stream-clear-output (stream))
(defgeneric stream-advance-to-column (stream column))
(defgeneric stream-read-sequence (stream sequence &optional start end))
(defgeneric stream-write-sequence (stream sequence &optional start end))

(defmethod stream-force-output (stream)
  (declare (ignore stream))
  nil)

(defmethod stream-finish-output (stream)
  (declare (ignore stream))
  nil)

(defmethod stream-clear-output (stream)
  (declare (ignore stream))
  nil)

(defmethod stream-start-line-p ((stream fundamental-character-output-stream))
  (equal (stream-line-column stream) 0))

(defmethod stream-write-string ((stream fundamental-character-output-stream)
                                string
                                &optional (start 0) end)
  (let ((end (or end (length string))))
    (do ((i start (1+ i)))
        ((>= i end) string)
      (stream-write-char stream (char string i)))))

(defmethod stream-fresh-line ((stream fundamental-character-output-stream))
  (if (stream-start-line-p stream)
      nil
      (progn
        (stream-terpri stream)
        t)))

(defmethod stream-advance-to-column ((stream fundamental-character-output-stream)
                                     column)
  (let ((current (stream-line-column stream)))
    (unless (null current)
      (dotimes (i (- current column) t)
        (stream-write-char stream #\Space)))))

(defun basic-read-sequence (stream sequence start end
                            expected-element-type read-fun)
  (let ((element-type (stream-element-type stream)))
    (if (subtypep element-type expected-element-type)
        (dotimes (count (- end start)
                  ;; If (< end start), skip the dotimes body but
                  ;; return start
                  (max start end))
          (let ((el (funcall read-fun stream)))
            (when (eq el :eof)
              (return (+ count start)))
            (setf (elt sequence (+ count start)) el)))
        (error "Cannot READ-SEQUENCE on stream of :ELEMENT-TYPE ~A"
               element-type))))

(defun basic-write-sequence (stream sequence start end
                             expected-element-type write-fun)
  (let ((element-type (stream-element-type stream)))
    (if (subtypep element-type expected-element-type)
        ;; Avoid LOOP because it isn't loaded yet
        (do ((n start (+ n 1)))
            ((= n end))
          (funcall write-fun stream (elt sequence n)))
        (error "Cannot WRITE-SEQUENCE on stream of :ELEMENT-TYPE ~A"
               element-type)))
  (stream-force-output stream)
  sequence)

(defmethod stream-read-sequence ((stream  fundamental-character-input-stream)
                                 sequence &optional (start 0) end)
  (basic-read-sequence stream sequence start (or end (length sequence))
                       'character #'stream-read-char))

(defmethod stream-write-sequence ((stream fundamental-character-output-stream)
                                  sequence &optional (start 0) end)
  (basic-write-sequence stream sequence start (or end (length sequence))
                        'character #'stream-write-char))

(defclass fundamental-binary-input-stream
  (fundamental-input-stream fundamental-binary-stream) ())

(defclass fundamental-binary-output-stream
  (fundamental-output-stream fundamental-binary-stream) ())

(defmethod stream-read-sequence ((stream fundamental-binary-input-stream)
                                 sequence &optional (start 0) end)
  (basic-read-sequence stream sequence start (or end (length sequence))
                       'signed-byte #'stream-read-byte))

(defmethod stream-write-sequence ((stream fundamental-binary-output-stream)
                                  sequence &optional (start 0) end)
  (basic-write-sequence stream sequence start (or end (length sequence))
                        'signed-byte #'stream-write-byte))

(defmethod stream-line-length (stream)
  (declare (ignore stream))
  nil)

(defmethod stream-line-length ((stream xp::xp-structure))
  (xp::line-length stream))

(defun decode-read-arg (arg)
  (cond ((null arg) *standard-input*)
        ((eq arg t) *terminal-io*)
        (t arg)))

(defun decode-print-arg (arg)
  (cond ((null arg) *standard-output*)
        ((eq arg t) *terminal-io*)
        (t arg)))

(defun report-eof (stream eof-errorp eof-value)
  (if eof-errorp
      (error 'end-of-file :stream stream)
      eof-value))

(defun check-for-eof (value stream eof-errorp eof-value)
  (if (eq value :eof)
      (report-eof stream eof-errorp eof-value)
      value))

(defun gray-read-char (&optional input-stream (eof-errorp t) eof-value recursive-p)
  (let ((stream (decode-read-arg input-stream)))
    (if (ansi-streamp stream)
        (funcall *ansi-read-char* stream eof-errorp eof-value recursive-p)
        (check-for-eof (stream-read-char stream) stream eof-errorp eof-value))))

(defun gray-peek-char (&optional peek-type input-stream (eof-errorp t)
                                 eof-value recursive-p)
  (let ((stream (decode-read-arg input-stream)))
    (if (ansi-streamp stream)
        (funcall *ansi-peek-char* peek-type stream eof-errorp eof-value recursive-p)
        (if (null peek-type)
            (check-for-eof (stream-peek-char stream) stream eof-errorp eof-value)
            (loop
              (let ((value (stream-peek-char stream)))
                (if (eq value :eof)
                    (return (report-eof stream eof-errorp eof-value))
                    (if (if (eq peek-type t)
                            (not (member value
                                         '(#\space #\tab #\newline #\return)))
                            (char= peek-type value))
                        (return value)
                        (stream-read-char stream)))))))))

(defun gray-unread-char (character &optional input-stream)
  (let ((stream (decode-read-arg input-stream)))
    (if (ansi-streamp stream)
        (funcall *ansi-unread-char* character stream)
        (stream-unread-char stream character))))

(defun gray-listen (&optional input-stream)
  (let ((stream (decode-read-arg input-stream)))
    (if (ansi-streamp stream)
        (funcall *ansi-listen* stream)
        (stream-listen stream))))

(defun gray-read-line (&optional input-stream (eof-error-p t)
                                 eof-value recursive-p)
  (let ((stream (decode-read-arg input-stream)))
    (if (ansi-streamp stream)
        (funcall *ansi-read-line* stream eof-error-p eof-value recursive-p)
        (multiple-value-bind (string eofp)
            (stream-read-line stream)
          (values (if (and eofp (zerop (length string)))
                      (report-eof stream eof-error-p eof-value)
                      string)
                  eofp)))))

(defun gray-clear-input (&optional input-stream)
  (let ((stream (decode-read-arg input-stream)))
    (if (ansi-streamp stream)
        (funcall *ansi-clear-input* stream)
        (stream-clear-input stream))))

(defun gray-output-object (object stream)
  (if (ansi-streamp stream)
      (funcall *sys-%output-object* object stream)
      (stream-write-string stream 
                           (with-output-to-string (s)
                             (funcall *sys-%output-object* object s)))))

(defun gray-read-char-no-hang (&optional input-stream (eof-errorp t)
                                         eof-value recursive-p)
  (let ((stream (decode-read-arg input-stream)))
    (if (ansi-streamp stream)
        (funcall *ansi-read-char-no-hang* stream eof-errorp eof-value recursive-p)
        (check-for-eof (stream-read-char-no-hang stream)
                       stream eof-errorp eof-value))))

(defun gray-write-char (character &optional output-stream)
  (let ((stream (decode-print-arg output-stream)))
    (if (ansi-streamp stream)
        (funcall *ansi-write-char* character stream)
        (stream-write-char stream character))))

(defun gray-fresh-line (&optional output-stream)
  (let ((stream (decode-print-arg output-stream)))
    (if (ansi-streamp stream)
        (funcall *ansi-fresh-line* stream)
        (stream-fresh-line stream))))

(defun gray-terpri (&optional output-stream)
  (let ((stream (decode-print-arg output-stream)))
    (if (ansi-streamp stream)
        (funcall *ansi-terpri* stream)
        (stream-terpri stream))))

(defun gray-write-string (string &optional output-stream &key (start 0) end)
  (let ((stream (decode-print-arg output-stream)))
    (if (ansi-streamp stream)
        (funcall *ansi-write-string* string stream :start start :end end)
        (stream-write-string stream string start end))))

(defun gray-write-line (string &optional output-stream &key (start 0) end)
  (let ((stream (decode-print-arg output-stream)))
    (if (ansi-streamp stream)
        (funcall *ansi-write-line* string stream :start start :end end)
        (progn
          (stream-write-string stream string start end)
          (stream-terpri stream)
          string))))

(defun gray-force-output (&optional output-stream)
  (let ((stream (decode-print-arg output-stream)))
    (if (ansi-streamp stream)
        (funcall *sys-%force-output* stream)
        (stream-force-output stream))))

(defun gray-finish-output (&optional output-stream)
  (let ((stream (decode-print-arg output-stream)))
    (if (ansi-streamp stream)
        (funcall *sys-%finish-output* stream)
        (stream-finish-output stream))))

(defun gray-clear-output (&optional output-stream)
  (let ((stream (decode-print-arg output-stream)))
    (if (ansi-streamp stream)
        (funcall *sys-%clear-output* stream)
        (stream-clear-output stream))))

(defun gray-read-byte (binary-input-stream &optional (eof-errorp t) eof-value)
  (if (ansi-streamp binary-input-stream)
      (funcall *ansi-read-byte* binary-input-stream eof-errorp eof-value)
      (check-for-eof (stream-read-byte binary-input-stream)
                     binary-input-stream eof-errorp eof-value)))

(defun gray-write-byte (integer binary-output-stream)
  (if (ansi-streamp binary-output-stream)
      (funcall *ansi-write-byte* integer binary-output-stream)
      (stream-write-byte binary-output-stream integer)))

(defmethod stream-line-column ((stream stream))
  nil)

(defun gray-stream-column (&optional input-stream)
  (let ((stream (decode-read-arg input-stream)))
    (if (ansi-streamp stream)
        nil ;(funcall *ansi-stream-column* stream)
        (stream-line-column stream))))

(defun gray-line-length (stream)
  (max 0
       (or *print-right-margin*
           (stream-line-length stream)
           xp::*default-right-margin*
           80)))

(defmethod gray-stream-element-type (stream)
  (if (ansi-streamp stream)
      (funcall *ansi-stream-element-type* stream)
      (bug-or-error stream 'gray-stream-element-type)))

(defmethod gray-stream-external-format (stream)
  (if (ansi-streamp stream)
      (funcall *ansi-stream-external-format* stream)
      (bug-or-error stream 'gray-stream-external-format)))

(defmethod (setf gray-stream-external-format) (new-value stream)
  (if (ansi-streamp stream)
      (sys::%set-stream-external-format stream new-value)
      (bug-or-error stream 'gray-stream-external-format)))

(defmethod gray-close (stream &key abort)
  (if (ansi-streamp stream)
      (funcall *ansi-close* stream :abort abort)
      (bug-or-error stream 'gray-close)))

(defmethod gray-input-stream-p (stream)
  (cond ((ansi-streamp stream)
         (funcall *ansi-input-stream-p* stream))
        (t
         (assert-stream stream)
         nil)))

(defmethod gray-input-character-stream-p (stream)
  (cond ((ansi-streamp stream)
         (funcall *ansi-input-character-stream-p* stream))
        (t
         (assert-stream stream)
         nil)))

(defmethod gray-output-stream-p (stream)
  (cond ((ansi-streamp stream)
         (funcall *ansi-output-stream-p* stream))
        (t
         (assert-stream stream)
         nil)))

(defmethod gray-interactive-stream-p (stream)
  (cond ((ansi-streamp stream)
         (funcall *ansi-interactive-stream-p* stream))
        (t
         (assert-stream stream)
         nil)))

(defmethod gray-open-stream-p (stream)
  (cond ((ansi-streamp stream)
         (funcall *ansi-open-stream-p* stream))
        (t
         (assert-stream stream)
         nil)))

(defmethod gray-streamp (stream)
  (funcall *ansi-streamp* stream))

(defmethod gray-pathname (pathspec)
  (unless (typep pathspec '(or string pathname stream))
    (error 'type-error :datum pathspec :expected-type '(or string pathname stream)))
  (funcall *ansi-pathname* pathspec))

(defmethod gray-truename (pathspec)
  (unless (typep pathspec '(or string pathname stream))
    (error 'type-error :datum pathspec :expected-type '(or string pathname stream)))
  (funcall *ansi-truename* pathspec))

(defun gray-write-sequence (sequence stream &key (start 0) end)
  (if (ansi-streamp stream)
      (funcall *ansi-write-sequence* sequence stream :start start :end end)
      (stream-write-sequence stream sequence start end)))

(defun gray-read-sequence (sequence stream &key (start 0) end)
  (if (ansi-streamp stream)
      (funcall *ansi-read-sequence* sequence stream :start start :end end)
      (stream-read-sequence stream sequence start end)))

(defgeneric stream-file-position (stream &optional position-spec))

(defun gray-file-position (stream &optional position-spec)
  (if position-spec
      (if (ansi-streamp stream)
          (funcall *ansi-file-position* stream position-spec)
          (stream-file-position stream position-spec))
      (if (ansi-streamp stream)
          (funcall *ansi-file-position* stream)
          (stream-file-position stream))))

(defgeneric stream-file-length (stream)
  (:method (stream)
    (error 'type-error
           :datum stream
           :expected-type 'file-stream)))

(defun gray-file-length (stream)
  (if (ansi-streamp stream)
      (funcall *ansi-file-length* stream)
      (stream-file-length stream)))

(defgeneric stream-file-string-length (stream object))

(defmethod stream-file-string-length
    ((stream fundamental-character-output-stream) object)
  (declare (ignore object))
  nil)

(defun gray-file-string-length (stream object)
  (if (ansi-streamp stream)
      (funcall *ansi-file-string-length* stream object)
      (stream-file-string-length stream object)))

#|
(defstruct (two-way-stream-g (:include stream))
  input-stream output-stream)

(defun gray-make-two-way-stream (in out)
  (if (and (ansi-streamp in) (ansi-streamp out))
      (funcall *ansi-make-two-way-stream* in out)
      (make-two-way-stream-g :input-stream in :output-stream out)))

(defun gray-two-way-stream-input-stream (stream)
  (if (ansi-streamp stream)
      (funcall *ansi-two-way-stream-input-stream* stream)
      (two-way-stream-g-input-stream stream)))

(defun gray-two-way-stream-output-stream (stream)
  (if (ansi-streamp stream)
      (funcall *ansi-two-way-stream-output-stream* stream)
      (two-way-stream-g-output-stream stream)))

|#

(setf (symbol-function 'common-lisp::read-char) #'gray-read-char)
(setf (symbol-function 'common-lisp::peek-char) #'gray-peek-char)
(setf (symbol-function 'common-lisp::unread-char) #'gray-unread-char)
(setf (symbol-function 'common-lisp::read-line) #'gray-read-line)
(setf (symbol-function 'common-lisp::clear-input) #'gray-clear-input)
(setf (symbol-function 'common-lisp::read-char-no-hang) #'gray-read-char-no-hang)
(setf (symbol-function 'common-lisp::write-char) #'gray-write-char)
(setf (symbol-function 'common-lisp::fresh-line) #'gray-fresh-line)
(setf (symbol-function 'common-lisp::terpri) #'gray-terpri)
(setf (symbol-function 'common-lisp::write-string) #'gray-write-string)
(setf (symbol-function 'common-lisp::write-line) #'gray-write-line)
(setf (symbol-function 'sys::%force-output) #'gray-force-output)
(setf (symbol-function 'sys::%finish-output) #'gray-finish-output)
(setf (symbol-function 'sys::%clear-output) #'gray-clear-output)
(setf (symbol-function 'sys::%output-object) #'gray-output-object)
(setf (symbol-function 'common-lisp::read-byte) #'gray-read-byte)
(setf (symbol-function 'common-lisp::write-byte) #'gray-write-byte)
(setf (symbol-function 'common-lisp::stream-column) #'gray-stream-column)
(setf (symbol-function 'common-lisp::stream-element-type) #'gray-stream-element-type)
(setf (fdefinition '(setf common-lisp::stream-element-type)) #'(setf gray-stream-element-type))
(setf (symbol-function 'common-lisp::stream-external-format) #'gray-stream-external-format)
(setf (fdefinition '(setf common-lisp::stream-external-format)) #'(setf gray-stream-external-format))
(setf (symbol-function 'common-lisp::close) #'gray-close)
(setf (symbol-function 'common-lisp::input-stream-p) #'gray-input-stream-p)
(setf (symbol-function 'common-lisp::input-character-stream-p) #'gray-input-character-stream-p)  ;; # fb 1.01
(setf (symbol-function 'common-lisp::output-stream-p) #'gray-output-stream-p)
(setf (symbol-function 'common-lisp::interactive-stream-p) #'gray-interactive-stream-p)
(setf (symbol-function 'common-lisp::open-stream-p) #'gray-open-stream-p)
(setf (symbol-function 'common-lisp::streamp) #'gray-streamp)
(setf (symbol-function 'common-lisp::read-sequence) #'gray-read-sequence)
(setf (symbol-function 'common-lisp::write-sequence) #'gray-write-sequence)
(setf (symbol-function 'common-lisp::file-position) #'gray-file-position)
(setf (symbol-function 'common-lisp::file-length) #'gray-file-length)
(setf (symbol-function 'common-lisp::file-string-length) #'gray-file-string-length)
(setf (symbol-function 'common-lisp::listen) #'gray-listen)
(setf (symbol-function 'ext:line-length) #'gray-line-length)
(setf (symbol-function 'common-lisp::pathname) #'gray-pathname)
(setf (symbol-function 'common-lisp::truename) #'gray-truename)

(dolist (e '((common-lisp::read-char gray-read-char)
             (common-lisp::peek-char gray-peek-char)
             (common-lisp::unread-char gray-unread-char)
             (common-lisp::read-line gray-read-line)
             (common-lisp::clear-input gray-clear-input)
             (common-lisp::read-char-no-hang gray-read-char-no-hang)
             (common-lisp::write-char gray-write-char)
             (common-lisp::fresh-line gray-fresh-line)
             (common-lisp::terpri gray-terpri)
             (common-lisp::write-string gray-write-string)
             (common-lisp::write-line gray-write-line)
             (sys::%force-output gray-force-output)
             (sys::%finish-output gray-finish-output)
             (sys::%clear-output gray-clear-output)
             (sys::%output-object gray-output-object)
             (common-lisp::read-byte gray-read-byte)
             (common-lisp::write-byte gray-write-byte)
             (common-lisp::stream-column gray-stream-column)
             (common-lisp::stream-element-type gray-stream-element-type)
             (common-lisp::stream-external-format gray-stream-external-format)
             (common-lisp::close gray-close)
             (common-lisp::input-stream-p gray-input-stream-p)
             (common-lisp::input-character-stream-p gray-input-character-stream-p) ;; # fb 1.01
             (common-lisp::output-stream-p gray-output-stream-p)
             (common-lisp::open-stream-p gray-open-stream-p)
             (common-lisp::streamp gray-streamp)
             (common-lisp::read-sequence gray-read-sequence)
             (common-lisp::write-sequence gray-write-sequence)
             (common-lisp::file-position gray-file-position)
             (common-lisp::file-length gray-file-length)
             (common-lisp::file-string-length gray-file-string-length)
             (common-lisp::listen gray-listen)
             (common-lisp::pathname gray-pathname)
             (common-lisp::truename gray-truename)))
  (sys::put (car e) 'sys::source (cl:get (second e) 'sys::source)))

#|
(setf (symbol-function 'common-lisp::make-two-way-stream) #'gray-make-two-way-stream)
(setf (symbol-function 'common-lisp::two-way-stream-input-stream) #'gray-two-way-stream-input-stream)
(setf (symbol-function 'common-lisp::two-way-stream-output-stream) #'gray-two-way-stream-output-stream)
|#

(eval-when (:load-toplevel)
  (mapcar (lambda (o) (mop:finalize-inheritance (find-class o)))
          '(fundamental-stream 
            fundamental-input-stream fundamental-output-stream
            fundamental-character-stream 
            fundamental-character-input-stream fundamental-character-output-stream
            fundamental-binary-stream
            fundamental-binary-input-stream fundamental-binary-output-stream)))
 
(provide 'gray-streams)

;;; Fixup Gray/ANSI stream relations 

(defparameter *sys--stream-charpos* #'sys::stream-charpos)
(defun sys::stream-charpos (stream)
  (cond         
    ((subtypep (type-of stream) 'gray-streams:fundamental-stream)
     (stream-line-column stream))
    ((streamp stream)
     (funcall *sys--stream-charpos* stream))))

(defparameter *sys--stream-%set-charpos* #'sys::stream-%set-charpos)
(defun sys::stream-%set-charpos (new-value stream)
  (cond 
    ((subtypep (type-of stream) 'gray-streams:fundamental-stream)
     (setf (stream-line-column stream) new-value))
    ((streamp stream)
     (funcall *sys--stream-%set-charpos* stream new-value))))
