(copy-descriptor-to-stream new-fd pty cookie external-format)))
(values name
(sb-sys:make-fd-stream master :input t :output t
+ :external-format external-format
:element-type :default
:dual-channel-p t)))))
-(defmacro round-bytes-to-words (n)
+;; Null terminate strings only C-side: otherwise we can run into
+;; A-T-S-L even for simple encodings like ASCII. Multibyte encodings
+;; may need more than a single byte of zeros; assume 4 byte is enough
+;; for everyone.
+(defmacro round-null-terminated-bytes-to-words (n)
(let ((bytes-per-word (/ sb-vm:n-machine-word-bits sb-vm:n-byte-bits)))
- `(logandc2 (the fixnum (+ (the fixnum ,n)
- (1- ,bytes-per-word))) (1- ,bytes-per-word))))
+ `(logandc2 (the sb-vm:signed-word (+ (the fixnum ,n)
+ 4 (1- ,bytes-per-word)))
+ (1- ,bytes-per-word))))
(defun string-list-to-c-strvec (string-list)
(let* ((bytes-per-word (/ sb-vm:n-machine-word-bits sb-vm:n-byte-bits))
;; clobbers argv[-1].
(vec-bytes (* bytes-per-word (+ (length string-list) 2)))
(octet-vector-list (mapcar (lambda (s)
- (string-to-octets s :null-terminate t))
+ (string-to-octets s))
string-list))
(string-bytes (reduce #'+ octet-vector-list
:key (lambda (s)
- (round-bytes-to-words (length s)))))
+ (round-null-terminated-bytes-to-words
+ (length s)))))
(total-bytes (+ string-bytes vec-bytes))
;; Memory to hold the vector of pointers and all the strings.
(vec-sap (sb-sys:allocate-system-memory total-bytes))
(string-sap (sap+ vec-sap vec-bytes))
;; Index starts from [1]!
(vec-index-offset bytes-per-word))
- (declare (index string-bytes vec-bytes total-bytes)
+ (declare (sb-vm:signed-word vec-bytes)
+ (sb-vm:word string-bytes total-bytes)
(sb-sys:system-area-pointer vec-sap string-sap))
(dolist (octets octet-vector-list)
(declare (type (simple-array (unsigned-byte 8) (*)) octets))
(let ((size (length octets)))
;; Copy string.
(sb-kernel:copy-ub8-to-system-area octets 0 string-sap 0 size)
+ ;; NULL-terminate it
+ (sb-kernel:system-area-ub8-fill 0 string-sap size 4)
;; Put the pointer in the vector.
(setf (sap-ref-sap vec-sap vec-index-offset) string-sap)
;; Advance string-sap for the next string.
- (setf string-sap (sap+ string-sap (round-bytes-to-words size)))
+ (setf string-sap (sap+ string-sap
+ (round-null-terminated-bytes-to-words size)))
(incf vec-index-offset bytes-per-word)))
;; Final null pointer.
(setf (sap-ref-sap vec-sap vec-index-offset) (int-sap 0))
(if-output-exists :error)
(error :output)
(if-error-exists :error)
- status-hook)
+ status-hook
+ (external-format :default))
#+sb-doc
#.(concatenate
'string
same place as normal output.
:STATUS-HOOK
This is a function the system calls whenever the status of the
- process changes. The function takes the process as an argument.")
+ process changes. The function takes the process as an argument.
+ :EXTERNAL-FORMAT
+ The external-format to use for :INPUT, :OUTPUT, and :ERROR :STREAMs.")
#-win32
(when (and env-p environment-p)
(error "can't specify :ENV and :ENVIRONMENT simultaneously"))
#+win32 `(declare (ignore ,pty ,cookie))
#+win32 `(let (,pty-name ,pty-stream) ,@body)
#-win32 `(multiple-value-bind (,pty-name ,pty-stream)
- (open-pty ,pty ,cookie)
+ (open-pty ,pty ,cookie :external-format external-format)
,@body))
(with-args-vec ((vec args) &body body)
`(with-c-strvec (,vec ,args)
input cookie
:direction :input
:if-does-not-exist if-input-does-not-exist
- :external-format :default
+ :external-format external-format
:wait wait)
(with-fd-and-stream-for ((stdout output-stream) :output
output cookie
:direction :output
:if-exists if-output-exists
- :external-format :default)
+ :external-format external-format)
(with-fd-and-stream-for ((stderr error-stream) :error
error cookie
:direction :output
:if-exists if-error-exists
- :external-format :default)
+ :external-format external-format)
(with-open-pty ((pty-name pty-stream) (pty cookie))
;; Make sure we are not notified about the child
;; death before we have installed the PROCESS
(unless proc
(dolist (fd *close-on-error*)
(sb-unix:unix-close fd))
- ;; FIXME: nothing seems to set this.
#-win32
(dolist (handler *handlers-installed*)
- (sb-sys:remove-fd-handler handler))))
- #-win32
- (when (and wait proc)
- (process-wait proc))
+ (sb-sys:remove-fd-handler handler)))
+ #-win32
+ (when (and wait proc)
+ (unwind-protect
+ (process-wait proc)
+ (dolist (handler *handlers-installed*)
+ (sb-sys:remove-fd-handler handler)))))
proc)))
;;; Install a handler for any input that shows up on the file
;;; 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))))))))
+ #-win32
+ (push handler *handlers-installed*)))
;;; FIXME: something very like this is done in SB-POSIX to treat
;;; streams as file descriptor designators; maybe we can combine these
(cond ((eq object t)
;; No new descriptor is needed.
(values -1 nil))
- ((eq object nil)
+ ((or (eq object nil)
+ (and (typep object 'broadcast-stream)
+ (not (broadcast-stream-streams object))))
;; Use /dev/null.
(multiple-value-bind
(fd errno)
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)))
+ (t
+ (error "Don't know how to copy from stream of element-type ~S"
+ et)))
(sb-unix:unix-lseek fd 0 sb-unix:l_set)
(push fd *close-in-parent*)
(return (values fd nil)))))