\f
;;; stream manipulation functions
-(defun input-stream-p (stream)
- (declare (type stream stream))
+(declaim (inline ansi-stream-input-stream-p))
+(defun ansi-stream-input-stream-p (stream)
+ (declare (type ansi-stream stream))
- #!+high-security
(when (synonym-stream-p stream)
(setf stream
(symbol-value (synonym-stream-symbol stream))))
- (and (ansi-stream-p stream)
- (not (eq (ansi-stream-in stream) #'closed-flame))
+ (and (not (eq (ansi-stream-in stream) #'closed-flame))
;;; KLUDGE: It's probably not good to have EQ tests on function
;;; values like this. What if someone's redefined the function?
;;; Is there a better way? (Perhaps just VALID-FOR-INPUT and
(or (not (eq (ansi-stream-in stream) #'ill-in))
(not (eq (ansi-stream-bin stream) #'ill-bin)))))
-(defun output-stream-p (stream)
+(defun input-stream-p (stream)
(declare (type stream stream))
+ (and (ansi-stream-p stream)
+ (ansi-stream-input-stream-p stream)))
+
+(declaim (inline ansi-stream-output-stream-p))
+(defun ansi-stream-output-stream-p (stream)
+ (declare (type ansi-stream stream))
- #!+high-security
(when (synonym-stream-p stream)
(setf stream (symbol-value
(synonym-stream-symbol stream))))
- (and (ansi-stream-p stream)
- (not (eq (ansi-stream-in stream) #'closed-flame))
+ (and (not (eq (ansi-stream-in stream) #'closed-flame))
(or (not (eq (ansi-stream-out stream) #'ill-out))
(not (eq (ansi-stream-bout stream) #'ill-bout)))))
-(defun open-stream-p (stream)
+(defun output-stream-p (stream)
(declare (type stream stream))
+
+ (and (ansi-stream-p stream)
+ (ansi-stream-output-stream-p stream)))
+
+(declaim (inline ansi-stream-open-stream-p))
+(defun ansi-stream-open-stream-p (stream)
+ (declare (type ansi-stream stream))
+ ;; CLHS 22.1.4 lets us not worry about synonym streams here.
(not (eq (ansi-stream-in stream) #'closed-flame)))
-(defun stream-element-type (stream)
- (declare (type stream stream))
+(defun open-stream-p (stream)
+ (ansi-stream-open-stream-p stream))
+
+(declaim (inline ansi-stream-element-type))
+(defun ansi-stream-element-type (stream)
+ (declare (type ansi-stream stream))
(funcall (ansi-stream-misc stream) stream :element-type))
+(defun stream-element-type (stream)
+ (ansi-stream-element-type stream))
+
(defun interactive-stream-p (stream)
(declare (type stream stream))
(funcall (ansi-stream-misc stream) stream :interactive-p))
-(defun close (stream &key abort)
- (declare (type stream stream))
+(declaim (inline ansi-stream-close))
+(defun ansi-stream-close (stream abort)
+ (declare (type ansi-stream stream))
(when (open-stream-p stream)
(funcall (ansi-stream-misc stream) stream :close abort))
t)
+(defun close (stream &key abort)
+ (ansi-stream-close stream abort))
+
(defun set-closed-flame (stream)
(setf (ansi-stream-in stream) #'closed-flame)
(setf (ansi-stream-bin stream) #'closed-flame)
;;; Call the MISC method with the :FILE-POSITION operation.
(defun file-position (stream &optional position)
(declare (type stream stream))
- (declare (type (or index (member nil :start :end)) position))
+ (declare (type (or index (alien sb!unix:off-t) (member nil :start :end)) position))
(cond
(position
(setf (ansi-stream-in-index stream) +ansi-stream-in-buffer-length+)
;; private predicate function..) is ugly and confusing, but
;; I can't see any other way. -- WHN 2001-04-14
:expected-type '(satisfies stream-associated-with-file-p)
- :format-string
+ :format-control
"~@<The stream ~2I~_~S ~I~_isn't associated with a file.~:>"
:format-arguments (list stream))))
;;; like FILE-POSITION, only using :FILE-LENGTH
(defun file-length (stream)
- (declare (type (or file-stream synonym-stream) stream))
+ ;; FIXME: The following declaration uses yet undefined types, which
+ ;; cause cross-compiler hangup.
+ ;;
+ ;; (declare (type (or file-stream synonym-stream) stream))
(stream-must-be-associated-with-file stream)
(funcall (ansi-stream-misc stream) stream :file-length))
\f
(stream-unread-char stream character)))
nil)
-
-;;; In the interest of ``once and only once'' this macro contains the
-;;; framework necessary to implement a peek-char function, which has
-;;; two special-cases (one for gray streams and one for echo streams)
-;;; in addition to the normal case.
-;;;
-;;; All arguments are forms which will be used for a specific purpose
-;;; PEEK-TYPE - the current peek-type as defined by ANSI CL
-;;; EOF-VALUE - the eof-value argument to peek-char
-;;; CHAR-VAR - the variable which will be used to store the current character
-;;; READ-FORM - the form which will be used to read a character
-;;; UNREAD-FORM - ditto for unread-char
-;;; SKIPPED-CHAR-FORM - the form to execute when skipping a character
-;;; EOF-DETECTED-FORM - the form to execute when EOF has been detected
-;;; (this will default to CHAR-VAR)
-(defmacro generalized-peeking-mechanism (peek-type eof-value char-var read-form unread-form &optional (skipped-char-form nil) (eof-detected-form nil))
- `(let ((,char-var ,read-form))
- (cond ((eql ,char-var ,eof-value)
- ,(if eof-detected-form
- eof-detected-form
- char-var))
- ((characterp ,peek-type)
- (do ((,char-var ,char-var ,read-form))
- ((or (eql ,char-var ,eof-value)
- (char= ,char-var ,peek-type))
- (cond ((eql ,char-var ,eof-value)
- ,(if eof-detected-form
- eof-detected-form
- char-var))
- (t ,unread-form
- ,char-var)))
- ,skipped-char-form))
- ((eql ,peek-type t)
- (do ((,char-var ,char-var ,read-form))
- ((or (eql ,char-var ,eof-value)
- (not (whitespace-char-p ,char-var)))
- (cond ((eql ,char-var ,eof-value)
- ,(if eof-detected-form
- eof-detected-form
- char-var))
- (t ,unread-form
- ,char-var)))
- ,skipped-char-form))
- ((null ,peek-type)
- ,unread-form
- ,char-var)
- (t
- (bug "Impossible case reached in PEEK-CHAR")))))
-
-(defun peek-char (&optional (peek-type nil)
- (stream *standard-input*)
- (eof-error-p t)
- eof-value
- recursive-p)
- (declare (ignore recursive-p))
- ;; FIXME: The type of PEEK-TYPE is also declared in a DEFKNOWN, but
- ;; the compiler doesn't seem to be smart enough to go from there to
- ;; imposing a type check. Figure out why (because PEEK-TYPE is an
- ;; &OPTIONAL argument?) and fix it, and then this explicit type
- ;; check can go away.
- (unless (typep peek-type '(or character boolean))
- (error 'simple-type-error
- :datum peek-type
- :expected-type '(or character boolean)
- :format-control "~@<bad PEEK-TYPE=~S, ~_expected ~S~:>"
- :format-arguments (list peek-type '(or character boolean))))
- (let ((stream (in-synonym-of stream)))
- (cond ((typep stream 'echo-stream)
- (echo-misc stream
- :peek-char
- peek-type
- (list eof-error-p eof-value)))
- ((ansi-stream-p stream)
- (generalized-peeking-mechanism
- peek-type eof-value char
- (read-char stream eof-error-p eof-value)
- (unread-char char stream)))
- (t
- ;; by elimination, must be Gray streams FUNDAMENTAL-STREAM
- (generalized-peeking-mechanism
- peek-type :eof char
- (if (null peek-type)
- (stream-peek-char stream)
- (stream-read-char stream))
- (if (null peek-type)
- ()
- (stream-unread-char stream char))
- ()
- (eof-or-lose stream eof-error-p eof-value))))))
-
(defun listen (&optional (stream *standard-input*))
(let ((stream (in-synonym-of stream)))
(if (ansi-stream-p stream)
(defun %write-string (string stream start end)
(declare (type string string))
- (declare (type streamlike stream))
+ (declare (type stream-designator stream))
(declare (type index start end))
(let ((stream (out-synonym-of stream)))
(cond ((ansi-stream-p stream)
(t ; must be Gray streams FUNDAMENTAL-STREAM
(stream-write-string stream string start end)))))
+;;; A wrapper function for all those (MACROLET OUT-FUN) definitions,
+;;; which cannot deal with keyword arguments.
+(declaim (inline write-string-no-key))
+(defun write-string-no-key (string stream start end)
+ (write-string string stream :start start :end end))
+
(defun write-line (string &optional (stream *standard-output*)
&key (start 0) end)
(declare (type string string))
(stream-write-byte integer))
integer)
\f
+
+;;; (These were inline throughout this file, but that's not appropriate
+;;; globally. And we must not inline them in the rest of this file if
+;;; dispatch to gray or simple streams is to work, since both redefine
+;;; these functions later.)
+(declaim (maybe-inline read-char unread-char read-byte listen))
+
;;; This is called from ANSI-STREAM routines that encapsulate CLOS
;;; streams to handle the misc routines and dispatch to the
-;;; appropriate Gray stream functions.
+;;; appropriate SIMPLE- or FUNDAMENTAL-STREAM functions.
(defun stream-misc-dispatch (stream operation &optional arg1 arg2)
- (declare (type fundamental-stream stream)
- (ignore arg2))
- (case operation
+ (declare (type stream stream) (ignore arg2))
+ (ecase operation
(:listen
;; Return T if input available, :EOF for end-of-file, otherwise NIL.
- (let ((char (stream-read-char-no-hang stream)))
+ (let ((char (read-char-no-hang stream nil :eof)))
(when (characterp char)
- (stream-unread-char stream char))
+ (unread-char char stream))
char))
(:unread
- (stream-unread-char stream arg1))
+ (unread-char arg1 stream))
(:close
(close stream))
(:clear-input
- (stream-clear-input stream))
+ (clear-input stream))
(:force-output
- (stream-force-output stream))
+ (force-output stream))
(:finish-output
- (stream-finish-output stream))
+ (finish-output stream))
(:element-type
(stream-element-type stream))
(:interactive-p
(interactive-stream-p stream))
(:line-length
- (stream-line-length stream))
+ (line-length stream))
(:charpos
- (stream-line-column stream))
+ (charpos stream))
(:file-length
(file-length stream))
(:file-position
(defun make-broadcast-stream (&rest streams)
(dolist (stream streams)
- (unless (or (and (synonym-stream-p stream)
- (output-stream-p (symbol-value
- (synonym-stream-symbol stream))))
- (output-stream-p stream))
+ (unless (output-stream-p stream)
(error 'type-error
:datum stream
:expected-type '(satisfies output-stream-p))))
(apply #'%make-broadcast-stream streams))
-(macrolet ((out-fun (fun method stream-method &rest args)
- `(defun ,fun (stream ,@args)
+(macrolet ((out-fun (name fun &rest args)
+ `(defun ,name (stream ,@args)
(dolist (stream (broadcast-stream-streams stream))
- (if (ansi-stream-p stream)
- (funcall (,method stream) stream ,@args)
- (,stream-method stream ,@args))))))
- (out-fun broadcast-out ansi-stream-out stream-write-char char)
- (out-fun broadcast-bout ansi-stream-bout stream-write-byte byte)
- (out-fun broadcast-sout ansi-stream-sout stream-write-string
- string start end))
+ (,fun ,(car args) stream ,@(cdr args))))))
+ (out-fun broadcast-out write-char char)
+ (out-fun broadcast-bout write-byte byte)
+ (out-fun broadcast-sout write-string-no-key string start end))
(defun broadcast-misc (stream operation &optional arg1 arg2)
(let ((streams (broadcast-stream-streams stream)))
(case operation
+ ;; FIXME: This may not be the best place to note this, but I
+ ;; think the :CHARPOS protocol needs revision. Firstly, I think
+ ;; this is the last place where a NULL return value was possible
+ ;; (before adjusting it to be 0), so a bunch of conditionals IF
+ ;; CHARPOS can be removed; secondly, it is my belief that
+ ;; FD-STREAMS, when running FILE-POSITION, do not update the
+ ;; CHARPOS, and consequently there will be much wrongness.
+ ;;
+ ;; FIXME: see also TWO-WAY-STREAM treatment of :CHARPOS -- why
+ ;; is it testing the :charpos of an input stream?
+ ;;
+ ;; -- CSR, 2004-02-04
(:charpos
- (dolist (stream streams)
+ (dolist (stream streams 0)
(let ((charpos (charpos stream)))
(if charpos (return charpos)))))
(:line-length
(let ((res (line-length stream)))
(when res (setq min (if min (min res min) res)))))))
(:element-type
+ #+nil ; old, arguably more logical, version
(let (res)
- (dolist (stream streams (if (> (length res) 1) `(and ,@res) res))
- (pushnew (stream-element-type stream) res :test #'equal))))
- (:close)
+ (dolist (stream streams (if (> (length res) 1) `(and ,@res) t))
+ (pushnew (stream-element-type stream) res :test #'equal)))
+ ;; ANSI-specified version (under System Class BROADCAST-STREAM)
+ (let ((res t))
+ (do ((streams streams (cdr streams)))
+ ((null streams) res)
+ (when (null (cdr streams))
+ (setq res (stream-element-type (car streams)))))))
+ (:file-position
+ (if arg1
+ (let ((res (or (eql arg1 :start) (eql arg1 0))))
+ (dolist (stream streams res)
+ (setq res (file-position stream arg1))))
+ (let ((res 0))
+ (dolist (stream streams res)
+ (setq res (file-position stream))))))
+ (:close
+ (set-closed-flame stream))
(t
(let ((res nil))
(dolist (stream streams res)
(print-unreadable-object (x stream :type t :identity t)
(format stream ":SYMBOL ~S" (synonym-stream-symbol x))))
-;;; The output simple output methods just call the corresponding method
-;;; in the synonymed stream.
-(macrolet ((out-fun (name slot stream-method &rest args)
+;;; The output simple output methods just call the corresponding
+;;; function on the synonymed stream.
+(macrolet ((out-fun (name fun &rest args)
`(defun ,name (stream ,@args)
(declare (optimize (safety 1)))
(let ((syn (symbol-value (synonym-stream-symbol stream))))
- (if (ansi-stream-p syn)
- (funcall (,slot syn) syn ,@args)
- (,stream-method syn ,@args))))))
- (out-fun synonym-out ansi-stream-out stream-write-char ch)
- (out-fun synonym-bout ansi-stream-bout stream-write-byte n)
- (out-fun synonym-sout ansi-stream-sout stream-write-string string start end))
+ (,fun ,(car args) syn ,@(cdr args))))))
+ (out-fun synonym-out write-char ch)
+ (out-fun synonym-bout write-byte n)
+ (out-fun synonym-sout write-string-no-key string start end))
;;; For the input methods, we just call the corresponding function on the
;;; synonymed stream. These functions deal with getting input out of
;; FIXME: This idiom of the-real-stream-of-a-possibly-synonym-stream
;; should be encapsulated in a function, and used here and most of
;; the other places that SYNONYM-STREAM-P appears.
- (unless (or (and (synonym-stream-p output-stream)
- (output-stream-p (symbol-value
- (synonym-stream-symbol output-stream))))
- (output-stream-p output-stream))
+ (unless (output-stream-p output-stream)
(error 'type-error
:datum output-stream
:expected-type '(satisfies output-stream-p)))
- (unless (or (and (synonym-stream-p input-stream)
- (input-stream-p (symbol-value
- (synonym-stream-symbol input-stream))))
- (input-stream-p input-stream))
+ (unless (input-stream-p input-stream)
(error 'type-error
:datum input-stream
:expected-type '(satisfies input-stream-p)))
(funcall #'%make-two-way-stream input-stream output-stream))
-(macrolet ((out-fun (name slot stream-method &rest args)
+(macrolet ((out-fun (name fun &rest args)
`(defun ,name (stream ,@args)
(let ((syn (two-way-stream-output-stream stream)))
- (if (ansi-stream-p syn)
- (funcall (,slot syn) syn ,@args)
- (,stream-method syn ,@args))))))
- (out-fun two-way-out ansi-stream-out stream-write-char ch)
- (out-fun two-way-bout ansi-stream-bout stream-write-byte n)
- (out-fun two-way-sout ansi-stream-sout stream-write-string string start end))
+ (,fun ,(car args) syn ,@(cdr args))))))
+ (out-fun two-way-out write-char ch)
+ (out-fun two-way-bout write-byte n)
+ (out-fun two-way-sout write-string-no-key string start end))
(macrolet ((in-fun (name fun &rest args)
`(defun ,name (stream ,@args)
(bin #'concatenated-bin)
(n-bin #'concatenated-n-bin)
(misc #'concatenated-misc))
- (:constructor %make-concatenated-stream
- (&rest streams &aux (current streams)))
+ (:constructor %make-concatenated-stream (&rest streams))
(:copier nil))
;; The car of this is the substream we are reading from now.
- current
- ;; This is a list of all the substreams there ever were. We need to
- ;; remember them so that we can close them.
- ;;
- ;; FIXME: ANSI says this is supposed to be the list of streams that
- ;; we still have to read from. So either this needs to become a
- ;; private member %STREAM (with CONCATENATED-STREAM-STREAMS a wrapper
- ;; around it which discards closed files from the head of the list)
- ;; or we need to update it as we run out of files.
- (streams nil :type list :read-only t))
+ (streams nil :type list))
(def!method print-object ((x concatenated-stream) stream)
(print-unreadable-object (x stream :type t :identity t)
(format stream
"Return a stream which takes its input from each of the streams in turn,
going on to the next at EOF."
(dolist (stream streams)
- (unless (or (and (synonym-stream-p stream)
- (input-stream-p (symbol-value
- (synonym-stream-symbol stream))))
- (input-stream-p stream))
+ (unless (input-stream-p stream)
(error 'type-error
:datum stream
:expected-type '(satisfies input-stream-p))))
(macrolet ((in-fun (name fun)
`(defun ,name (stream eof-error-p eof-value)
- (do ((current (concatenated-stream-current stream)
- (cdr current)))
- ((null current)
+ (do ((streams (concatenated-stream-streams stream)
+ (cdr streams)))
+ ((null streams)
(eof-or-lose stream eof-error-p eof-value))
- (let* ((stream (car current))
+ (let* ((stream (car streams))
(result (,fun stream nil nil)))
(when result (return result)))
- (setf (concatenated-stream-current stream) current)))))
+ (pop (concatenated-stream-streams stream))))))
(in-fun concatenated-in read-char)
(in-fun concatenated-bin read-byte))
(defun concatenated-n-bin (stream buffer start numbytes eof-errorp)
- (do ((current (concatenated-stream-current stream) (cdr current))
+ (do ((streams (concatenated-stream-streams stream) (cdr streams))
(current-start start)
(remaining-bytes numbytes))
- ((null current)
+ ((null streams)
(if eof-errorp
(error 'end-of-file :stream stream)
(- numbytes remaining-bytes)))
- (let* ((stream (car current))
+ (let* ((stream (car streams))
(bytes-read (read-n-bytes stream buffer current-start
remaining-bytes nil)))
(incf current-start bytes-read)
(decf remaining-bytes bytes-read)
(when (zerop remaining-bytes) (return numbytes)))
- (setf (concatenated-stream-current stream) (cdr current))))
+ (setf (concatenated-stream-streams stream) (cdr streams))))
(defun concatenated-misc (stream operation &optional arg1 arg2)
- (let ((left (concatenated-stream-current stream)))
- (when left
- (let* ((current (car left)))
- (case operation
- (:listen
- (loop
- (let ((stuff (if (ansi-stream-p current)
- (funcall (ansi-stream-misc current) current
- :listen)
- (stream-misc-dispatch current :listen))))
- (cond ((eq stuff :eof)
- ;; Advance CURRENT, and try again.
- (pop (concatenated-stream-current stream))
- (setf current
- (car (concatenated-stream-current stream)))
- (unless current
- ;; No further streams. EOF.
- (return :eof)))
- (stuff
- ;; Stuff's available.
- (return t))
- (t
- ;; Nothing is available yet.
- (return nil))))))
- (:clear-input (clear-input current))
- (:unread (unread-char arg1 current))
- (:close
- (set-closed-flame stream))
- (t
- (if (ansi-stream-p current)
- (funcall (ansi-stream-misc current) current operation arg1 arg2)
- (stream-misc-dispatch current operation arg1 arg2))))))))
+ (let* ((left (concatenated-stream-streams stream))
+ (current (car left)))
+ (case operation
+ (:listen
+ (unless left
+ (return-from concatenated-misc :eof))
+ (loop
+ (let ((stuff (if (ansi-stream-p current)
+ (funcall (ansi-stream-misc current) current
+ :listen)
+ (stream-misc-dispatch current :listen))))
+ (cond ((eq stuff :eof)
+ ;; Advance STREAMS, and try again.
+ (pop (concatenated-stream-streams stream))
+ (setf current
+ (car (concatenated-stream-streams stream)))
+ (unless current
+ ;; No further streams. EOF.
+ (return :eof)))
+ (stuff
+ ;; Stuff's available.
+ (return t))
+ (t
+ ;; Nothing is available yet.
+ (return nil))))))
+ (:clear-input (when left (clear-input current)))
+ (:unread (when left (unread-char arg1 current)))
+ (:close
+ (set-closed-flame stream))
+ (t
+ (when left
+ (if (ansi-stream-p current)
+ (funcall (ansi-stream-misc current) current operation arg1 arg2)
+ (stream-misc-dispatch current operation arg1 arg2)))))))
\f
;;;; echo streams
(in #'echo-in)
(bin #'echo-bin)
(misc #'echo-misc)
- (n-bin #'ill-bin))
+ (n-bin #'echo-n-bin))
(:constructor %make-echo-stream (input-stream output-stream))
(:copier nil))
unread-stuff)
"Return a bidirectional stream which gets its input from INPUT-STREAM and
sends its output to OUTPUT-STREAM. In addition, all input is echoed to
the output stream."
- (unless (or (and (synonym-stream-p output-stream)
- (output-stream-p (symbol-value
- (synonym-stream-symbol output-stream))))
- (output-stream-p output-stream))
+ (unless (output-stream-p output-stream)
(error 'type-error
:datum output-stream
:expected-type '(satisfies output-stream-p)))
- (unless (or (and (synonym-stream-p input-stream)
- (input-stream-p (symbol-value
- (synonym-stream-symbol input-stream))))
- (input-stream-p input-stream))
+ (unless (input-stream-p input-stream)
(error 'type-error
:datum input-stream
:expected-type '(satisfies input-stream-p)))
(funcall #'%make-echo-stream input-stream output-stream))
-(macrolet ((in-fun (name fun out-slot stream-method &rest args)
+(macrolet ((in-fun (name in-fun out-fun &rest args)
`(defun ,name (stream ,@args)
(or (pop (echo-stream-unread-stuff stream))
(let* ((in (echo-stream-input-stream stream))
(out (echo-stream-output-stream stream))
- (result (,fun in ,@args)))
- (if (ansi-stream-p out)
- (funcall (,out-slot out) out result)
- (,stream-method out result))
- result)))))
- (in-fun echo-in read-char ansi-stream-out stream-write-char
- eof-error-p eof-value)
- (in-fun echo-bin read-byte ansi-stream-bout stream-write-byte
- eof-error-p eof-value))
-
-(defun echo-misc (stream operation &optional arg1 arg2)
- (let* ((in (two-way-stream-input-stream stream))
- (out (two-way-stream-output-stream stream)))
- (case operation
- (:listen
- (or (not (null (echo-stream-unread-stuff stream)))
- (if (ansi-stream-p in)
- (or (/= (the fixnum (ansi-stream-in-index in))
- +ansi-stream-in-buffer-length+)
- (funcall (ansi-stream-misc in) in :listen))
- (stream-misc-dispatch in :listen))))
- (:unread (push arg1 (echo-stream-unread-stuff stream)))
- (:element-type
- (let ((in-type (stream-element-type in))
- (out-type (stream-element-type out)))
- (if (equal in-type out-type)
- in-type `(and ,in-type ,out-type))))
- (:close
- (set-closed-flame stream))
- (:peek-char
- ;; For the special case of peeking into an echo-stream
- ;; arg1 is PEEK-TYPE, arg2 is (EOF-ERROR-P EOF-VALUE)
- ;; returns peeked-char, eof-value, or errors end-of-file
- ;;
- ;; Note: This code could be moved into PEEK-CHAR if desired.
- ;; I am unsure whether this belongs with echo-streams because it is
- ;; echo-stream specific, or PEEK-CHAR because it is peeking code.
- ;; -- mrd 2002-11-18
- ;;
- ;; UNREAD-CHAR-P indicates whether the current character was one
- ;; that was previously unread. In that case, we need to ensure that
- ;; the semantics for UNREAD-CHAR are held; the character should
- ;; not be echoed again.
- (let ((unread-char-p nil))
- (flet ((outfn (c)
- (unless unread-char-p
- (if (ansi-stream-p out)
- (funcall (ansi-stream-out out) out c)
- ;; gray-stream
- (stream-write-char out c))))
- (infn ()
- ;; Obtain input from unread buffer or input stream,
- ;; and set the flag appropriately.
- (cond ((not (null (echo-stream-unread-stuff stream)))
- (setf unread-char-p t)
- (pop (echo-stream-unread-stuff stream)))
- (t
- (setf unread-char-p nil)
- (read-char in (first arg2) (second arg2))))))
- (generalized-peeking-mechanism
- arg1 (second arg2) char
- (infn)
- (unread-char char in)
- (outfn char)))))
- (t
- (or (if (ansi-stream-p in)
- (funcall (ansi-stream-misc in) in operation arg1 arg2)
- (stream-misc-dispatch in operation arg1 arg2))
- (if (ansi-stream-p out)
- (funcall (ansi-stream-misc out) out operation arg1 arg2)
- (stream-misc-dispatch out operation arg1 arg2)))))))
+ (result (if eof-error-p
+ (,in-fun in ,@args)
+ (,in-fun in nil in))))
+ (cond
+ ((eql result in) eof-value)
+ (t (,out-fun result out) result)))))))
+ (in-fun echo-in read-char write-char eof-error-p eof-value)
+ (in-fun echo-bin read-byte write-byte eof-error-p eof-value))
+
+(defun echo-n-bin (stream buffer start numbytes eof-error-p)
+ (let ((new-start start)
+ (read 0))
+ (loop
+ (let ((thing (pop (echo-stream-unread-stuff stream))))
+ (cond
+ (thing
+ (setf (aref buffer new-start) thing)
+ (incf new-start)
+ (incf read)
+ (when (= read numbytes)
+ (return-from echo-n-bin numbytes)))
+ (t (return nil)))))
+ (let ((bytes-read (read-n-bytes (echo-stream-input-stream stream) buffer
+ new-start (- numbytes read) nil)))
+ (cond
+ ((not eof-error-p)
+ (write-sequence buffer (echo-stream-output-stream stream)
+ :start new-start :end (+ new-start bytes-read))
+ (+ bytes-read read))
+ ((> numbytes (+ read bytes-read))
+ (write-sequence buffer (echo-stream-output-stream stream)
+ :start new-start :end (+ new-start bytes-read))
+ (error 'end-of-file :stream stream))
+ (t
+ (write-sequence buffer (echo-stream-output-stream stream)
+ :start new-start :end (+ new-start bytes-read))
+ (aver (= numbytes (+ new-start bytes-read)))
+ numbytes)))))
\f
;;;; base STRING-STREAM stuff
(:include ansi-stream)
(:constructor nil)
(:copier nil))
- (string nil :type string))
+ ;; FIXME: This type declaration is true, and will probably continue
+ ;; to be true. However, note well the comments in DEFTRANSFORM
+ ;; REPLACE, implying that performance of REPLACE is somewhat
+ ;; critical to performance of string streams. If (VECTOR CHARACTER)
+ ;; ever becomes different from (VECTOR BASE-CHAR), the transform
+ ;; probably needs to be extended.
+ (string (missing-arg) :type (vector character)))
\f
;;;; STRING-INPUT-STREAM stuff
(defstruct (string-input-stream
(:include string-stream
(in #'string-inch)
- (bin #'string-binch)
+ (bin #'ill-bin)
(n-bin #'string-stream-read-n-bytes)
(misc #'string-in-misc)
- (string nil :type simple-string))
+ (string (missing-arg) :type simple-string))
(:constructor internal-make-string-input-stream
(string current end))
(:copier nil))
- (current nil :type index)
- (end nil :type index))
+ (current (missing-arg) :type index)
+ (end (missing-arg) :type index))
(defun string-inch (stream eof-error-p eof-value)
+ (declare (type string-input-stream stream))
(let ((string (string-input-stream-string stream))
(index (string-input-stream-current stream)))
- (declare (simple-string string) (fixnum index))
- (cond ((= index (the index (string-input-stream-end stream)))
+ (cond ((>= index (the index (string-input-stream-end stream)))
(eof-or-lose stream eof-error-p eof-value))
(t
(setf (string-input-stream-current stream) (1+ index))
- (aref string index)))))
+ (char string index)))))
(defun string-binch (stream eof-error-p eof-value)
+ (declare (type string-input-stream stream))
(let ((string (string-input-stream-string stream))
(index (string-input-stream-current stream)))
- (declare (simple-string string)
- (type index index))
- (cond ((= index (the index (string-input-stream-end stream)))
+ (cond ((>= index (the index (string-input-stream-end stream)))
(eof-or-lose stream eof-error-p eof-value))
(t
(setf (string-input-stream-current stream) (1+ index))
- (char-code (aref string index))))))
+ (char-code (char string index))))))
(defun string-stream-read-n-bytes (stream buffer start requested eof-error-p)
(declare (type string-input-stream stream)
(index (string-input-stream-current stream))
(available (- (string-input-stream-end stream) index))
(copy (min available requested)))
- (declare (simple-string string)
- (type index index available copy))
+ (declare (type simple-string string))
(when (plusp copy)
(setf (string-input-stream-current stream)
(truly-the index (+ index copy)))
copy)))
(defun string-in-misc (stream operation &optional arg1 arg2)
- (declare (ignore arg2))
+ (declare (type string-input-stream stream)
+ (ignore arg2))
(case operation
(:file-position
(if arg1
- (setf (string-input-stream-current stream) arg1)
+ (setf (string-input-stream-current stream)
+ (case arg1
+ (:start 0)
+ (:end (string-input-stream-end stream))
+ ;; We allow moving position beyond EOF. Errors happen
+ ;; on read, not move -- or the user may extend the
+ ;; input string.
+ (t arg1)))
(string-input-stream-current stream)))
- (:file-length (length (string-input-stream-string stream)))
+ ;; According to ANSI: "Should signal an error of type type-error
+ ;; if stream is not a stream associated with a file."
+ ;; This is checked by FILE-LENGTH, so no need to do it here either.
+ ;; (:file-length (length (string-input-stream-string stream)))
(:unread (decf (string-input-stream-current stream)))
- (:listen (or (/= (the fixnum (string-input-stream-current stream))
- (the fixnum (string-input-stream-end stream)))
+ (:close (set-closed-flame stream))
+ (:listen (or (/= (the index (string-input-stream-current stream))
+ (the index (string-input-stream-end stream)))
:eof))
- (:element-type 'base-char)))
+ (:element-type (array-element-type (string-input-stream-string stream)))))
-(defun make-string-input-stream (string &optional
- (start 0) end)
+(defun make-string-input-stream (string &optional (start 0) end)
#!+sb-doc
"Return an input stream which will supply the characters of STRING between
START and END in order."
(declare (type string string)
(type index start)
(type (or index null) end))
-
- (internal-make-string-input-stream
- (coerce string 'simple-string)
- start
- (%check-vector-sequence-bounds string start end)))
+ (let* ((string (coerce string '(simple-array character (*))))
+ (end (%check-vector-sequence-bounds string start end)))
+ (with-array-data ((string string) (start start) (end end))
+ (internal-make-string-input-stream
+ string ;; now simple
+ start
+ end))))
\f
;;;; STRING-OUTPUT-STREAM stuff
(sout #'string-sout)
(misc #'string-out-misc)
;; The string we throw stuff in.
- (string (make-string 40) :type simple-string))
- (:constructor make-string-output-stream ())
+ (string (missing-arg)
+ :type (simple-array character (*))))
+ (:constructor make-string-output-stream
+ (&key (element-type 'character)
+ &aux (string (make-string 40))))
(:copier nil))
;; Index of the next location to use.
- (index 0 :type fixnum))
+ (index 0 :type fixnum)
+ ;; Index cache for string-output-stream-last-index
+ (index-cache 0 :type fixnum)
+ ;; Requested element type
+ (element-type 'character))
#!+sb-doc
(setf (fdocumentation 'make-string-output-stream 'function)
"Return an output stream which will accumulate all output given it for
the benefit of the function GET-OUTPUT-STREAM-STRING.")
+(defun string-output-stream-last-index (stream)
+ (max (string-output-stream-index stream)
+ (string-output-stream-index-cache stream)))
+
(defun string-ouch (stream character)
(let ((current (string-output-stream-index stream))
(workspace (string-output-stream-string stream)))
- (declare (simple-string workspace) (fixnum current))
+ (declare (type (simple-array character (*)) workspace)
+ (type fixnum current))
(if (= current (the fixnum (length workspace)))
(let ((new-workspace (make-string (* current 2))))
(replace new-workspace workspace)
- (setf (aref new-workspace current) character)
- (setf (string-output-stream-string stream) new-workspace))
+ (setf (aref new-workspace current) character
+ (string-output-stream-string stream) new-workspace))
(setf (aref workspace current) character))
(setf (string-output-stream-index stream) (1+ current))))
(defun string-sout (stream string start end)
- (declare (simple-string string) (fixnum start end))
- (let* ((current (string-output-stream-index stream))
+ (declare (type simple-string string)
+ (type fixnum start end))
+ (let* ((string (if (typep string '(simple-array character (*)))
+ string
+ (coerce string '(simple-array character (*)))))
+ (current (string-output-stream-index stream))
(length (- end start))
(dst-end (+ length current))
(workspace (string-output-stream-string stream)))
- (declare (simple-string workspace)
- (fixnum current length dst-end))
+ (declare (type (simple-array character (*)) workspace string)
+ (type fixnum current length dst-end))
(if (> dst-end (the fixnum (length workspace)))
(let ((new-workspace (make-string (+ (* current 2) length))))
(replace new-workspace workspace :end2 current)
(declare (ignore arg2))
(case operation
(:file-position
- (if (null arg1)
+ (if arg1
+ (let ((end (string-output-stream-last-index stream)))
+ (setf (string-output-stream-index-cache stream) end
+ (string-output-stream-index stream)
+ (case arg1
+ (:start 0)
+ (:end end)
+ (t
+ ;; We allow moving beyond the end of stream,
+ ;; implicitly extending the output stream.
+ (let ((buffer (string-output-stream-string stream)))
+ (when (> arg1 (length buffer))
+ (setf (string-output-stream-string stream)
+ (make-string
+ arg1 :element-type (array-element-type buffer))
+ (subseq (string-output-stream-string stream)
+ 0 end)
+ (subseq buffer 0 end))))
+ arg1))))
(string-output-stream-index stream)))
+ (:close (set-closed-flame stream))
(:charpos
(do ((index (1- (the fixnum (string-output-stream-index stream)))
(1- index))
(count 0 (1+ count))
(string (string-output-stream-string stream)))
((< index 0) count)
- (declare (simple-string string)
- (fixnum index count))
+ (declare (type (simple-array character (*)) string)
+ (type fixnum index count))
(if (char= (schar string index) #\newline)
(return count))))
- (:element-type 'base-char)))
+ (:element-type (array-element-type (string-output-stream-string stream)))))
;;; Return a string of all the characters sent to a stream made by
;;; MAKE-STRING-OUTPUT-STREAM since the last call to this function.
(defun get-output-stream-string (stream)
(declare (type string-output-stream stream))
- (let* ((length (string-output-stream-index stream))
- (result (make-string length)))
- (replace result (string-output-stream-string stream))
- (setf (string-output-stream-index stream) 0)
+ (let* ((length (string-output-stream-last-index stream))
+ (element-type (string-output-stream-element-type stream))
+ (result
+ (case element-type
+ ;; Overwhelmingly common case; can be inlined.
+ ((character) (make-string length))
+ (t (make-string length :element-type element-type)))))
+ ;; For the benefit of the REPLACE transform, let's do this, so
+ ;; that the common case isn't ludicrously expensive.
+ (etypecase result
+ ((simple-array character (*))
+ (replace result (string-output-stream-string stream)))
+ ((simple-array nil (*))
+ (replace result (string-output-stream-string stream))))
+ (setf (string-output-stream-index stream) 0
+ (string-output-stream-index-cache stream) 0)
result))
;;; Dump the characters buffer up in IN-STREAM to OUT-STREAM as
(%write-string (string-output-stream-string in-stream)
out-stream
0
- (string-output-stream-index in-stream))
- (setf (string-output-stream-index in-stream) 0))
+ (string-output-stream-last-index in-stream))
+ (setf (string-output-stream-index in-stream) 0
+ (string-output-stream-index-cache in-stream) 0))
\f
;;;; fill-pointer streams
;;; WITH-OUTPUT-TO-STRING.
(deftype string-with-fill-pointer ()
- '(and string
+ '(and (vector character)
(satisfies array-has-fill-pointer-p)))
(defstruct (fill-pointer-output-stream
(misc #'fill-pointer-misc)
;; a string with a fill pointer where we stuff
;; the stuff we write
- (string (error "missing argument")
+ (string (missing-arg)
:type string-with-fill-pointer
:read-only t))
(:constructor make-fill-pointer-output-stream (string))
(current+1 (1+ current)))
(declare (fixnum current))
(with-array-data ((workspace buffer) (start) (end))
- (declare (simple-string workspace))
+ (declare (type (simple-array character (*)) workspace))
(let ((offset-current (+ start current)))
(declare (fixnum offset-current))
(if (= offset-current end)
(declare (simple-string new-workspace))
(%byte-blt workspace start
new-workspace 0 current)
- (setf workspace new-workspace)
- (setf offset-current current)
+ (setf workspace new-workspace
+ offset-current current)
(set-array-header buffer workspace new-length
current+1 0 new-length nil))
(setf (fill-pointer buffer) current+1))
(defun fill-pointer-sout (stream string start end)
(declare (simple-string string) (fixnum start end))
- (let* ((buffer (fill-pointer-output-stream-string stream))
+ (let* ((string (if (typep string '(simple-array character (*)))
+ string
+ (coerce string '(simple-array character (*)))))
+ (buffer (fill-pointer-output-stream-string stream))
(current (fill-pointer buffer))
(string-len (- end start))
(dst-end (+ string-len current)))
(declare (fixnum current dst-end string-len))
(with-array-data ((workspace buffer) (dst-start) (dst-length))
- (declare (simple-string workspace))
+ (declare (type (simple-array character (*)) workspace))
(let ((offset-dst-end (+ dst-start dst-end))
(offset-current (+ dst-start current)))
(declare (fixnum offset-dst-end offset-current))
(if (> offset-dst-end dst-length)
(let* ((new-length (+ (the fixnum (* current 2)) string-len))
(new-workspace (make-string new-length)))
- (declare (simple-string new-workspace))
+ (declare (type (simple-array character (*)) new-workspace))
(%byte-blt workspace dst-start
new-workspace 0 current)
(setf workspace new-workspace)
dst-end))
(defun fill-pointer-misc (stream operation &optional arg1 arg2)
- (declare (ignore arg1 arg2))
+ (declare (ignore arg2))
(case operation
+ (:file-position
+ (let ((buffer (fill-pointer-output-stream-string stream)))
+ (if arg1
+ (setf (fill-pointer buffer)
+ (case arg1
+ (:start 0)
+ ;; Fill-pointer is always at fill-pointer we will
+ ;; make :END move to the end of the actual string.
+ (:end (array-total-size buffer))
+ ;; We allow moving beyond the end of string if the
+ ;; string is adjustable.
+ (t (when (>= arg1 (array-total-size buffer))
+ (if (adjustable-array-p buffer)
+ (adjust-array buffer arg1)
+ (error "Cannot move FILE-POSITION beyond the end ~
+ of WITH-OUTPUT-TO-STRING stream ~
+ constructed with non-adjustable string.")))
+ arg1)))
+ (fill-pointer buffer))))
(:charpos
(let* ((buffer (fill-pointer-output-stream-string stream))
(current (fill-pointer buffer)))
(if found
(- end (the fixnum found))
current)))))
- (:element-type 'base-char)))
+ (:element-type (array-element-type
+ (fill-pointer-output-stream-string stream)))))
\f
;;;; indenting streams
(defun case-frob-misc (stream op &optional arg1 arg2)
(declare (type case-frob-stream stream))
(case op
- (:close)
+ (:close
+ (set-closed-flame stream))
(t
(let ((target (case-frob-stream-target stream)))
(if (ansi-stream-p target)
(funcall (ansi-stream-sout target) target str 0 len)
(stream-write-string target str 0 len))))
\f
-;;;; stream commands
-
-(defstruct (stream-command (:constructor make-stream-command
- (name &optional args))
- (:copier nil))
- (name nil :type symbol)
- (args nil :type list))
-(def!method print-object ((obj stream-command) str)
- (print-unreadable-object (obj str :type t :identity t)
- (prin1 (stream-command-name obj) str)))
-
-;;; Take a stream and wait for text or a command to appear on it. If
-;;; text appears before a command, return NIL, otherwise return a
-;;; command.
-;;;
-;;; We can't simply call the stream's misc method because NIL is an
-;;; ambiguous return value: does it mean text arrived, or does it mean
-;;; the stream's misc method had no :GET-COMMAND implementation? We
-;;; can't return NIL until there is text input. We don't need to loop
-;;; because any stream implementing :GET-COMMAND would wait until it
-;;; had some input. If the LISTEN fails, then we have some stream we
-;;; must wait on.
-(defun get-stream-command (stream)
- (let ((cmdp (funcall (ansi-stream-misc stream) stream :get-command)))
- (cond (cmdp)
- ((listen stream)
- nil)
- (t
- ;; This waits for input and returns NIL when it arrives.
- (unread-char (read-char stream) stream)))))
-\f
;;;; READ-SEQUENCE
(defun read-sequence (seq stream &key (start 0) end)
(simple-array (signed-byte 8) (*))
simple-string)
(let* ((numbytes (- end start))
- (bytes-read (sb!sys:read-n-bytes stream
- data
- offset-start
- numbytes
- nil)))
+ (bytes-read (read-n-bytes stream data offset-start
+ numbytes nil)))
(if (< bytes-read numbytes)
(+ start bytes-read)
end)))
(funcall write-function (aref seq i) stream)))))))
\f
;;;; etc.
-
-;;; (These were inline throughout this file, but that's not appropriate
-;;; globally.)
-(declaim (maybe-inline read-char unread-char read-byte listen))