0.9.2.36:
[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-RESULT - 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 ;;; EOF-VALUE - the result returned from READ-FORM when hitting eof
25 ;;; UNREAD-FORM - ditto for unread-char
26 ;;; SKIPPED-CHAR-FORM - the form to execute when skipping a character
27 ;;; EOF-DETECTED-FORM - the form to execute when EOF has been detected
28 ;;;                     (this will default to EOF-RESULT)
29 (sb!xc:defmacro generalized-peeking-mechanism
30     (peek-type eof-value char-var read-form read-eof unread-form
31      &optional (skipped-char-form nil) (eof-detected-form nil))
32   `(let ((,char-var ,read-form))
33     (cond ((eql ,char-var ,read-eof)
34            ,(if eof-detected-form
35                 eof-detected-form
36                 eof-value))
37           ((characterp ,peek-type)
38            (do ((,char-var ,char-var ,read-form))
39                ((or (eql ,char-var ,read-eof) 
40                     (char= ,char-var ,peek-type))
41                 (cond ((eql ,char-var ,read-eof)
42                        ,(if eof-detected-form
43                             eof-detected-form
44                             eof-value))
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 ,read-eof)
51                     (not (whitespacep ,char-var)))
52                 (cond ((eql ,char-var ,read-eof)
53                        ,(if eof-detected-form
54                             eof-detected-form
55                             eof-value))
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 ;;; rudi (2004-08-09): There was an inline declaration for read-char,
66 ;;; unread-char, read-byte, listen here that was removed because these
67 ;;; functions are redefined when simple-streams are loaded.
68
69 #!-sb-fluid (declaim (inline ansi-stream-peek-char))
70 (defun ansi-stream-peek-char (peek-type stream eof-error-p eof-value
71                               recursive-p)
72   (cond ((typep stream 'echo-stream)
73          (echo-misc stream
74                     :peek-char
75                     peek-type
76                     (list eof-error-p eof-value)))
77         (t
78          (generalized-peeking-mechanism
79           peek-type eof-value char
80           (ansi-stream-read-char stream eof-error-p :eof recursive-p)
81           :eof
82           (ansi-stream-unread-char char stream)))))
83
84 (defun peek-char (&optional (peek-type nil)
85                             (stream *standard-input*)
86                             (eof-error-p t)
87                             eof-value
88                             recursive-p)
89   (the (or character boolean) peek-type)
90   (let ((stream (in-synonym-of stream)))
91     (if (ansi-stream-p stream)
92         (ansi-stream-peek-char peek-type stream eof-error-p eof-value
93                                recursive-p)
94         ;; by elimination, must be Gray streams FUNDAMENTAL-STREAM
95         (generalized-peeking-mechanism
96          peek-type :eof char
97          (if (null peek-type)
98              (stream-peek-char stream)
99              (stream-read-char stream))
100          :eof
101          (if (null peek-type)
102              ()
103              (stream-unread-char stream char))
104          ()
105          (eof-or-lose stream eof-error-p eof-value)))))
106
107 (defun echo-misc (stream operation &optional arg1 arg2)
108   (let* ((in (two-way-stream-input-stream stream))
109          (out (two-way-stream-output-stream stream)))
110     (case operation
111       (:listen
112        (or (not (null (echo-stream-unread-stuff stream)))
113            (if (ansi-stream-p in)
114                (or (/= (the fixnum (ansi-stream-in-index in))
115                        +ansi-stream-in-buffer-length+)
116                    (funcall (ansi-stream-misc in) in :listen))
117                (stream-misc-dispatch in :listen))))
118       (:unread (push arg1 (echo-stream-unread-stuff stream)))
119       (:element-type
120        (let ((in-type (stream-element-type in))
121              (out-type (stream-element-type out)))
122          (if (equal in-type out-type)
123              in-type `(and ,in-type ,out-type))))
124       (:close
125        (set-closed-flame stream))
126       (:peek-char
127        ;; For the special case of peeking into an echo-stream
128        ;; arg1 is PEEK-TYPE, arg2 is (EOF-ERROR-P EOF-VALUE)
129        ;; returns peeked-char, eof-value, or errors end-of-file
130        ;;
131        ;; Note: This code could be moved into PEEK-CHAR if desired.
132        ;; I am unsure whether this belongs with echo-streams because it is
133        ;; echo-stream specific, or PEEK-CHAR because it is peeking code.
134        ;; -- mrd 2002-11-18
135        ;;
136        ;; UNREAD-CHAR-P indicates whether the current character was one
137        ;; that was previously unread.  In that case, we need to ensure that
138        ;; the semantics for UNREAD-CHAR are held; the character should
139        ;; not be echoed again.
140        (let ((unread-char-p nil))
141          (flet ((outfn (c)
142                   (unless unread-char-p
143                     (if (ansi-stream-p out)
144                         (funcall (ansi-stream-out out) out c)
145                         ;; gray-stream
146                         (stream-write-char out c))))
147                 (infn ()
148                   ;; Obtain input from unread buffer or input stream,
149                   ;; and set the flag appropriately.
150                   (cond ((not (null (echo-stream-unread-stuff stream)))
151                          (setf unread-char-p t)
152                          (pop (echo-stream-unread-stuff stream)))
153                         (t
154                          (setf unread-char-p nil)
155                          (read-char in (first arg2) :eof)))))
156            (generalized-peeking-mechanism
157             arg1 (second arg2) char
158             (infn)
159             :eof
160             (unread-char char in)
161             (outfn char)))))
162       (t
163        (or (if (ansi-stream-p in)
164                (funcall (ansi-stream-misc in) in operation arg1 arg2)
165                (stream-misc-dispatch in operation arg1 arg2))
166            (if (ansi-stream-p out)
167                (funcall (ansi-stream-misc out) out operation arg1 arg2)
168                (stream-misc-dispatch out operation arg1 arg2)))))))
169