0.8.7.19:
[sbcl.git] / src / code / target-stream.lisp
1 ;;;; os-independent stream functions requiring reader machinery
2
3 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; more information.
5 ;;;;
6 ;;;; This software is derived from the CMU CL system, which was
7 ;;;; written at Carnegie Mellon University and released into the
8 ;;;; public domain. The software is in the public domain and is
9 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
10 ;;;; files for more information.
11
12 (in-package "SB!IMPL")
13
14 ;;; In the interest of ``once and only once'' this macro contains the
15 ;;; framework necessary to implement a peek-char function, which has
16 ;;; two special-cases (one for gray streams and one for echo streams)
17 ;;; in addition to the normal case.
18 ;;;
19 ;;; All arguments are forms which will be used for a specific purpose
20 ;;; PEEK-TYPE - the current peek-type as defined by ANSI CL
21 ;;; EOF-VALUE - the eof-value argument to peek-char
22 ;;; CHAR-VAR - the variable which will be used to store the current character
23 ;;; READ-FORM - the form which will be used to read a character
24 ;;; UNREAD-FORM - ditto for unread-char
25 ;;; SKIPPED-CHAR-FORM - the form to execute when skipping a character
26 ;;; EOF-DETECTED-FORM - the form to execute when EOF has been detected
27 ;;;                     (this will default to CHAR-VAR)
28 (eval-when (:compile-toplevel :execute)
29   (sb!xc:defmacro generalized-peeking-mechanism
30       (peek-type eof-value char-var read-form unread-form
31        &optional (skipped-char-form nil) (eof-detected-form nil))
32     `(let ((,char-var ,read-form))
33       (cond ((eql ,char-var ,eof-value) 
34              ,(if eof-detected-form
35                   eof-detected-form
36                   char-var))
37             ((characterp ,peek-type)
38              (do ((,char-var ,char-var ,read-form))
39                  ((or (eql ,char-var ,eof-value) 
40                       (char= ,char-var ,peek-type))
41                   (cond ((eql ,char-var ,eof-value)
42                          ,(if eof-detected-form
43                               eof-detected-form
44                               char-var))
45                         (t ,unread-form
46                            ,char-var)))
47                ,skipped-char-form))
48             ((eql ,peek-type t)
49              (do ((,char-var ,char-var ,read-form))
50                  ((or (eql ,char-var ,eof-value)
51                       (not (whitespacep ,char-var)))
52                   (cond ((eql ,char-var ,eof-value)
53                          ,(if eof-detected-form
54                               eof-detected-form
55                               char-var))
56                         (t ,unread-form
57                            ,char-var)))
58                ,skipped-char-form))
59             ((null ,peek-type)
60              ,unread-form
61              ,char-var)
62             (t
63              (bug "Impossible case reached in PEEK-CHAR"))))))
64
65 ;;; We proclaim them INLINE here, then proclaim them MAYBE-INLINE at EOF,
66 ;;; so, except in this file, they are not inline by default, but they can be.
67 #!-sb-fluid (declaim (inline read-char unread-char read-byte listen))
68
69 (defun peek-char (&optional (peek-type nil)
70                             (stream *standard-input*)
71                             (eof-error-p t)
72                             eof-value
73                             recursive-p)
74   (declare (ignore recursive-p))
75   (the (or character boolean) peek-type)
76   (let ((stream (in-synonym-of stream)))
77     (cond ((typep stream 'echo-stream)
78            (echo-misc stream
79                       :peek-char
80                       peek-type
81                       (list eof-error-p eof-value)))
82           ((ansi-stream-p stream)
83            (generalized-peeking-mechanism
84             peek-type eof-value char
85             (read-char stream eof-error-p eof-value)
86             (unread-char char stream)))
87           (t
88            ;; by elimination, must be Gray streams FUNDAMENTAL-STREAM
89            (generalized-peeking-mechanism
90             peek-type :eof char
91             (if (null peek-type)
92                 (stream-peek-char stream)
93                 (stream-read-char stream))
94             (if (null peek-type)
95                 ()
96                 (stream-unread-char stream char))
97             ()
98             (eof-or-lose stream eof-error-p eof-value))))))
99
100 (defun echo-misc (stream operation &optional arg1 arg2)
101   (let* ((in (two-way-stream-input-stream stream))
102          (out (two-way-stream-output-stream stream)))
103     (case operation
104       (:listen
105        (or (not (null (echo-stream-unread-stuff stream)))
106            (if (ansi-stream-p in)
107                (or (/= (the fixnum (ansi-stream-in-index in))
108                        +ansi-stream-in-buffer-length+)
109                    (funcall (ansi-stream-misc in) in :listen))
110                (stream-misc-dispatch in :listen))))
111       (:unread (push arg1 (echo-stream-unread-stuff stream)))
112       (:element-type
113        (let ((in-type (stream-element-type in))
114              (out-type (stream-element-type out)))
115          (if (equal in-type out-type)
116              in-type `(and ,in-type ,out-type))))
117       (:close
118        (set-closed-flame stream))
119       (:peek-char
120        ;; For the special case of peeking into an echo-stream
121        ;; arg1 is PEEK-TYPE, arg2 is (EOF-ERROR-P EOF-VALUE)
122        ;; returns peeked-char, eof-value, or errors end-of-file
123        ;;
124        ;; Note: This code could be moved into PEEK-CHAR if desired.
125        ;; I am unsure whether this belongs with echo-streams because it is
126        ;; echo-stream specific, or PEEK-CHAR because it is peeking code.
127        ;; -- mrd 2002-11-18
128        ;;
129        ;; UNREAD-CHAR-P indicates whether the current character was one
130        ;; that was previously unread.  In that case, we need to ensure that
131        ;; the semantics for UNREAD-CHAR are held; the character should
132        ;; not be echoed again.
133        (let ((unread-char-p nil))
134          (flet ((outfn (c)
135                   (unless unread-char-p
136                     (if (ansi-stream-p out)
137                         (funcall (ansi-stream-out out) out c)
138                         ;; gray-stream
139                         (stream-write-char out c))))
140                 (infn ()
141                   ;; Obtain input from unread buffer or input stream,
142                   ;; and set the flag appropriately.
143                   (cond ((not (null (echo-stream-unread-stuff stream)))
144                          (setf unread-char-p t)
145                          (pop (echo-stream-unread-stuff stream)))
146                         (t
147                          (setf unread-char-p nil)
148                          (read-char in (first arg2) (second arg2))))))
149            (generalized-peeking-mechanism
150             arg1 (second arg2) char
151             (infn)
152             (unread-char char in)
153             (outfn char)))))
154       (t
155        (or (if (ansi-stream-p in)
156                (funcall (ansi-stream-misc in) in operation arg1 arg2)
157                (stream-misc-dispatch in operation arg1 arg2))
158            (if (ansi-stream-p out)
159                (funcall (ansi-stream-misc out) out operation arg1 arg2)
160                (stream-misc-dispatch out operation arg1 arg2)))))))
161
162 (declaim (maybe-inline read-char unread-char read-byte listen))