;;; stream.
(defun copy-descriptor-to-stream (descriptor stream cookie external-format)
(incf (car cookie))
- (let* (handler
+ (let* ((handler nil)
(buf (make-array 256 :element-type '(unsigned-byte 8)))
- (read-end 0))
+ (read-end 0)
+ (et (stream-element-type stream))
+ (copy-fun
+ (cond
+ ((member et '(character base-char))
+ (lambda ()
+ (let* ((decode-end read-end)
+ (string (handler-case
+ (octets-to-string
+ buf :end read-end
+ :external-format external-format)
+ (end-of-input-in-character (e)
+ (setf decode-end
+ (octet-decoding-error-start e))
+ (octets-to-string
+ buf :end decode-end
+ :external-format external-format)))))
+ (unless (zerop (length string))
+ (write-string string stream)
+ (when (/= decode-end (length buf))
+ (replace buf buf :start2 decode-end :end2 read-end))
+ (decf read-end decode-end)))))
+ ((member et '(:default (unsigned-byte 8)) :test #'equal)
+ (lambda ()
+ (write-sequence buf stream :end read-end)
+ (setf read-end 0)))
+ (t
+ ;; FIXME.
+ (error "Don't know how to copy to stream of element-type ~S"
+ et)))))
(setf handler
(sb-sys:add-fd-handler
descriptor
(strerror errno)))
(t
(incf read-end count)
- (let* ((decode-end read-end)
- (string (handler-case
- (octets-to-string
- buf :end read-end
- :external-format external-format)
- (end-of-input-in-character (e)
- (setf decode-end
- (octet-decoding-error-start e))
- (octets-to-string
- buf :end decode-end
- :external-format external-format)))))
- (unless (zerop (length string))
- (write-string string stream)
- (when (/= decode-end (length buf))
- (replace buf buf :start2 decode-end :end2 read-end))
- (decf read-end decode-end))))))))))))
+ (funcall copy-fun))))))))))
;;; FIXME: something very like this is done in SB-POSIX to treat
;;; streams as file descriptor designators; maybe we can combine these
child process won't hang~:>" object))
|#
(let ((fd (make-temp-fd))
- (newline (string #\Newline)))
- (loop
- (multiple-value-bind
- (line no-cr)
- (read-line object nil nil)
- (unless line
- (return))
- (let ((vector (string-to-octets line)))
- (sb-unix:unix-write
- fd vector 0 (length vector)))
- (if no-cr
- (return)
- (sb-unix:unix-write fd newline 0 1))))
+ (et (stream-element-type object)))
+ (cond ((member et '(character base-char))
+ (loop
+ (multiple-value-bind
+ (line no-cr)
+ (read-line object nil nil)
+ (unless line
+ (return))
+ (let ((vector (string-to-octets
+ line
+ :external-format external-format)))
+ (sb-unix:unix-write
+ fd vector 0 (length vector)))
+ (if no-cr
+ (return)
+ (sb-unix:unix-write
+ fd #.(string #\Newline) 0 1)))))
+ ((member et '(:default (unsigned-byte 8))
+ :test 'equal)
+ (loop with buf = (make-array 256 :element-type '(unsigned-byte 8))
+ for p = (read-sequence buf object)
+ until (zerop p)
+ do (sb-unix:unix-write fd buf 0 p))))
(sb-unix:unix-lseek fd 0 sb-unix:l_set)
(push fd *close-in-parent*)
(return (values fd nil)))))
(sb-thread:join-thread writer)
(assert (equal "OK" (read-line out)))))
+(defclass buffer-stream (sb-gray:fundamental-binary-input-stream sb-gray:fundamental-binary-output-stream)
+ ((buffer :initform (make-array 128
+ :element-type '(unsigned-byte 8)
+ :adjustable t
+ :fill-pointer 0))
+ (mark :initform 0)))
+
+(defmethod stream-element-type ((stream buffer-stream))
+ '(unsigned-byte 8))
+
+(defmethod sb-gray:stream-read-sequence ((stream buffer-stream) seq &optional (start 0) end)
+ (let* ((buffer (slot-value stream 'buffer))
+ (end (or end (length seq)))
+ (mark (slot-value stream 'mark))
+ (fill-pointer (fill-pointer buffer))
+ (new-mark (+ mark (min fill-pointer (- end start)))))
+ (setf (slot-value stream 'mark) new-mark)
+ (replace seq buffer
+ :start1 start :end1 end
+ :start2 mark :end2 fill-pointer)
+ (min end (+ start (- fill-pointer mark)))))
+
+(defmethod sb-gray:stream-write-sequence ((stream buffer-stream) seq &optional (start 0) end)
+ (let* ((buffer (slot-value stream 'buffer))
+ (end (or end (length seq)))
+ (fill-pointer (fill-pointer buffer))
+ (new-fill (min (array-total-size buffer) (+ fill-pointer (- end start)))))
+ (setf (fill-pointer buffer) new-fill)
+ (replace buffer seq
+ :start1 fill-pointer
+ :start2 start :end2 end)
+ seq))
+
+(with-test (:name :run-program-cat-3)
+ ;; User-defined binary input and output streams.
+ (let ((in (make-instance 'buffer-stream))
+ (out (make-instance 'buffer-stream))
+ (data #(0 1 2 3 4 5 6 7 8 9 10 11 12)))
+ (write-sequence data in)
+ (let ((process (sb-ext:run-program "/bin/cat" '() :wait t :output out :input in))
+ (buf (make-array (length data))))
+ (assert (= 13 (read-sequence buf out)))
+ (assert (= 0 (read-sequence (make-array 8) out)))
+ (assert (equalp buf data)))))
+
;;; Test driving an external program (cat) through pipes wrapped in
;;; composite streams.