- (list
- (let* ((key-as-string (subseq string 0 =-pos))
- (key-as-upcase-string (string-upcase key-as-string))
- (key (keywordicate key-as-upcase-string))
- (val (subseq string (1+ =-pos))))
- (unless (string= key-as-string key-as-upcase-string)
- (warn "smashing case of ~S in conversion to CMU-CL-style ~
+ (list
+ (let* ((key-as-string (subseq string 0 =-pos))
+ (key-as-upcase-string (string-upcase key-as-string))
+ (key (keywordicate key-as-upcase-string))
+ (val (subseq string (1+ =-pos))))
+ (unless (string= key-as-string key-as-upcase-string)
+ (warn "smashing case of ~S in conversion to CMU-CL-style ~
- (zerop pid))
- nil)
- ((eql (ldb (byte 8 0) status)
- sb-unix:wstopped)
- (values pid
- :stopped
- (ldb (byte 8 8) status)))
- ((zerop (ldb (byte 7 0) status))
- (values pid
- :exited
- (ldb (byte 8 8) status)))
- (t
- (let ((signal (ldb (byte 7 0) status)))
- (values pid
- (if (position signal
- #.(vector
- sb-unix:sigstop
- sb-unix:sigtstp
- sb-unix:sigttin
- sb-unix:sigttou))
- :stopped
- :signaled)
- signal
- (not (zerop (ldb (byte 1 7) status)))))))))
+ (zerop pid))
+ nil)
+ ((eql (ldb (byte 8 0) status)
+ sb-unix:wstopped)
+ (values pid
+ :stopped
+ (ldb (byte 8 8) status)))
+ ((zerop (ldb (byte 7 0) status))
+ (values pid
+ :exited
+ (ldb (byte 8 8) status)))
+ (t
+ (let ((signal (ldb (byte 7 0) status)))
+ (values pid
+ (if (position signal
+ #.(vector
+ sb-unix:sigstop
+ sb-unix:sigtstp
+ sb-unix:sigttin
+ sb-unix:sigttou))
+ :stopped
+ :signaled)
+ signal
+ (not (zerop (ldb (byte 1 7) status)))))))))
- exit-code ; either exit code or signal
- core-dumped ; T if a core image was dumped
- pty ; stream to child's pty, or NIL
- input ; stream to child's input, or NIL
- output ; stream from child's output, or NIL
- error ; stream from child's error output, or NIL
- status-hook ; closure to call when PROC changes status
- plist ; a place for clients to stash things
+ exit-code ; either exit code or signal
+ core-dumped ; T if a core image was dumped
+ pty ; stream to child's pty, or NIL
+ input ; stream to child's input, or NIL
+ output ; stream from child's output, or NIL
+ error ; stream from child's error output, or NIL
+ status-hook ; closure to call when PROC changes status
+ plist ; a place for clients to stash things
- (values nil errno))
- ((and (eql pid (process-pid proc))
- (= signal sb-unix:sigcont))
- (setf (process-%status proc) :running)
- (setf (process-exit-code proc) nil)
- (when (process-status-hook proc)
- (funcall (process-status-hook proc) proc))
- t)
- (t
- t)))))
+ (values nil errno))
+ ((and (eql pid (process-pid proc))
+ (= signal sb-unix:sigcont))
+ (setf (process-%status proc) :running)
+ (setf (process-exit-code proc) nil)
+ (when (process-status-hook proc)
+ (funcall (process-status-hook proc) proc))
+ t)
+ (t
+ t)))))
- (wait3 t t)
- (unless pid
- (return))
- (let ((proc (find pid *active-processes* :key #'process-pid)))
- (when proc
- (setf (process-%status proc) what)
- (setf (process-exit-code proc) code)
- (setf (process-core-dumped proc) core)
- (when (process-status-hook proc)
- (funcall (process-status-hook proc) proc))
- (when (position what #(:exited :signaled))
- (sb-sys:without-interrupts
- (setf *active-processes*
- (delete proc *active-processes*)))))))))
+ (wait3 t t)
+ (unless pid
+ (return))
+ (let ((proc (with-active-processes-lock ()
+ (find pid *active-processes* :key #'process-pid))))
+ (when proc
+ (setf (process-%status proc) what)
+ (setf (process-exit-code proc) code)
+ (setf (process-core-dumped proc) core)
+ (when (process-status-hook proc)
+ (funcall (process-status-hook proc) proc))
+ (when (position what #(:exited :signaled))
+ (with-active-processes-lock ()
+ (setf *active-processes*
+ (delete proc *active-processes*)))))))))
- (declare (simple-string s))
- (let ((n (length s)))
- ;; Blast the string into place.
- (sb-kernel:copy-to-system-area (the simple-base-string
- ;; FIXME
- (coerce s 'simple-base-string))
- (* sb-vm:vector-data-offset
- sb-vm:n-word-bits)
- string-sap 0
- (* (1+ n) sb-vm:n-byte-bits))
- ;; Blast the pointer to the string into place.
- (setf (sap-ref-sap vec-sap i) string-sap)
- (setf string-sap (sap+ string-sap (round-bytes-to-words (1+ n))))
- (incf i #-alpha 4 #+alpha 8)))
+ (declare (simple-string s))
+ (let ((n (length s)))
+ ;; Blast the string into place.
+ (sb-kernel:copy-ub8-to-system-area (the simple-base-string
+ ;; FIXME
+ (coerce s 'simple-base-string))
+ 0
+ string-sap 0
+ (1+ n))
+ ;; Blast the pointer to the string into place.
+ (setf (sap-ref-sap vec-sap i) string-sap)
+ (setf string-sap (sap+ string-sap (round-bytes-to-words (1+ n))))
+ (incf i #.(/ sb-vm::n-machine-word-bits sb-vm::n-byte-bits))))
- and start = 0 then (and end (1+ end))
- while start
- ;; <Krystof> the truename of a file naming a directory is the
- ;; directory, at least until pfdietz comes along and says why
- ;; that's noncompliant -- CSR, c. 2003-08-10
- for truename = (probe-file (subseq search-path start end))
- for fullpath = (when truename (merge-pathnames pathname truename))
- when (and fullpath
- (unix-filename-is-executable-p (namestring fullpath)))
- return fullpath))
+ and start = 0 then (and end (1+ end))
+ while start
+ ;; <Krystof> the truename of a file naming a directory is the
+ ;; directory, at least until pfdietz comes along and says why
+ ;; that's noncompliant -- CSR, c. 2003-08-10
+ for truename = (probe-file (subseq search-path start end))
+ for fullpath = (when truename (merge-pathnames pathname truename))
+ when (and fullpath
+ (unix-filename-is-executable-p (namestring fullpath)))
+ return fullpath))
- input for the current process is inherited. If NIL, /dev/null
- is used. If a pathname, the file so specified is used. If a stream,
- all the input is read from that stream and send to the subprocess. If
- :STREAM, the PROCESS-INPUT slot is filled in with a stream that sends
- its output to the process. Defaults to NIL.
+ input for the current process is inherited. If NIL, /dev/null
+ is used. If a pathname, the file so specified is used. If a stream,
+ all the input is read from that stream and send to the subprocess. If
+ :STREAM, the PROCESS-INPUT slot is filled in with a stream that sends
+ its output to the process. Defaults to NIL.
- output for the current process is inherited. If NIL, /dev/null
- is used. If a pathname, the file so specified is used. If a stream,
- all the output from the process is written to this stream. If
- :STREAM, the PROCESS-OUTPUT slot is filled in with a stream that can
- be read to get the output. Defaults to NIL.
+ output for the current process is inherited. If NIL, /dev/null
+ is used. If a pathname, the file so specified is used. If a stream,
+ all the output from the process is written to this stream. If
+ :STREAM, the PROCESS-OUTPUT slot is filled in with a stream that can
+ be read to get the output. Defaults to NIL.
- ;; communicate cleanup info.
- *close-on-error*
- *close-in-parent*
- *handlers-installed*
- ;; Establish PROC at this level so that we can return it.
- proc
- ;; It's friendly to allow the caller to pass any string
- ;; designator, but internally we'd like SIMPLE-STRINGs.
- (simple-args (mapcar (lambda (x) (coerce x 'simple-string)) args)))
+ ;; communicate cleanup info.
+ *close-on-error*
+ *close-in-parent*
+ *handlers-installed*
+ ;; Establish PROC at this level so that we can return it.
+ proc
+ ;; It's friendly to allow the caller to pass any string
+ ;; designator, but internally we'd like SIMPLE-STRINGs.
+ (simple-args (mapcar (lambda (x) (coerce x 'simple-string)) args)))
- (let ((pfile
- (if search
- (let ((p (find-executable-in-search-path program)))
- (and p (unix-namestring p t)))
- (unix-namestring program t)))
- (cookie (list 0)))
- (unless pfile
- (error "no such program: ~S" program))
- (unless (unix-filename-is-executable-p pfile)
- (error "not executable: ~S" program))
- (multiple-value-bind (stdin input-stream)
- (get-descriptor-for input cookie
- :direction :input
- :if-does-not-exist if-input-does-not-exist)
- (multiple-value-bind (stdout output-stream)
- (get-descriptor-for output cookie
- :direction :output
- :if-exists if-output-exists)
- (multiple-value-bind (stderr error-stream)
- (if (eq error :output)
- (values stdout output-stream)
- (get-descriptor-for error cookie
- :direction :output
- :if-exists if-error-exists))
- (multiple-value-bind (pty-name pty-stream)
- (open-pty pty cookie)
- ;; Make sure we are not notified about the child
- ;; death before we have installed the PROCESS
- ;; structure in *ACTIVE-PROCESSES*.
- (sb-sys:without-interrupts
- (with-c-strvec (args-vec simple-args)
- (with-c-strvec (environment-vec environment)
- (let ((child-pid
- (without-gcing
- (spawn pfile args-vec environment-vec pty-name
- stdin stdout stderr))))
- (when (< child-pid 0)
- (error "couldn't fork child process: ~A"
- (strerror)))
- (setf proc (make-process :pid child-pid
- :%status :running
- :pty pty-stream
- :input input-stream
- :output output-stream
- :error error-stream
- :status-hook status-hook
- :cookie cookie))
- (push proc *active-processes*))))))))))
+ (let ((pfile
+ (if search
+ (let ((p (find-executable-in-search-path program)))
+ (and p (unix-namestring p t)))
+ (unix-namestring program t)))
+ (cookie (list 0)))
+ (unless pfile
+ (error "no such program: ~S" program))
+ (unless (unix-filename-is-executable-p pfile)
+ (error "not executable: ~S" program))
+ (multiple-value-bind (stdin input-stream)
+ (get-descriptor-for input cookie
+ :direction :input
+ :if-does-not-exist if-input-does-not-exist)
+ (multiple-value-bind (stdout output-stream)
+ (get-descriptor-for output cookie
+ :direction :output
+ :if-exists if-output-exists)
+ (multiple-value-bind (stderr error-stream)
+ (if (eq error :output)
+ (values stdout output-stream)
+ (get-descriptor-for error cookie
+ :direction :output
+ :if-exists if-error-exists))
+ (multiple-value-bind (pty-name pty-stream)
+ (open-pty pty cookie)
+ ;; Make sure we are not notified about the child
+ ;; death before we have installed the PROCESS
+ ;; structure in *ACTIVE-PROCESSES*.
+ (with-active-processes-lock ()
+ (with-c-strvec (args-vec simple-args)
+ (with-c-strvec (environment-vec environment)
+ (let ((child-pid
+ (without-gcing
+ (spawn pfile args-vec environment-vec pty-name
+ stdin stdout stderr))))
+ (when (< child-pid 0)
+ (error "couldn't fork child process: ~A"
+ (strerror)))
+ (setf proc (make-process :pid child-pid
+ :%status :running
+ :pty pty-stream
+ :input input-stream
+ :output output-stream
+ :error error-stream
+ :status-hook status-hook
+ :cookie cookie))
+ (push proc *active-processes*))))))))))
- (strerror readable/errno)))
- ((zerop result)
- (return))))
- (sb-alien:with-alien ((buf (sb-alien:array
- sb-alien:char
- 256)))
- (multiple-value-bind
- (count errno)
- (sb-unix:unix-read descriptor
- (alien-sap buf)
- 256)
- (cond ((or (and (null count)
- (eql errno sb-unix:eio))
- (eql count 0))
- (sb-sys:remove-fd-handler handler)
- (setf handler nil)
- (decf (car cookie))
- (sb-unix:unix-close descriptor)
- (return))
- ((null count)
- (sb-sys:remove-fd-handler handler)
- (setf handler nil)
- (decf (car cookie))
- (error
- "~@<couldn't read input from sub-process: ~
+ (strerror readable/errno)))
+ ((zerop result)
+ (return))))
+ (sb-alien:with-alien ((buf (sb-alien:array
+ sb-alien:char
+ 256)))
+ (multiple-value-bind
+ (count errno)
+ (sb-unix:unix-read descriptor
+ (alien-sap buf)
+ 256)
+ (cond ((or (and (null count)
+ (eql errno sb-unix:eio))
+ (eql count 0))
+ (sb-sys:remove-fd-handler handler)
+ (setf handler nil)
+ (decf (car cookie))
+ (sb-unix:unix-close descriptor)
+ (return))
+ ((null count)
+ (sb-sys:remove-fd-handler handler)
+ (setf handler nil)
+ (decf (car cookie))
+ (error
+ "~@<couldn't read input from sub-process: ~
- ;; No new descriptor is needed.
- (values -1 nil))
- ((eq object nil)
- ;; Use /dev/null.
- (multiple-value-bind
- (fd errno)
- (sb-unix:unix-open #.(coerce "/dev/null" 'base-string)
- (case direction
- (:input sb-unix:o_rdonly)
- (:output sb-unix:o_wronly)
- (t sb-unix:o_rdwr))
- #o666)
- (unless fd
- (error "~@<couldn't open \"/dev/null\": ~2I~_~A~:>"
- (strerror errno)))
- (push fd *close-in-parent*)
- (values fd nil)))
- ((eq object :stream)
- (multiple-value-bind (read-fd write-fd) (sb-unix:unix-pipe)
- (unless read-fd
- (error "couldn't create pipe: ~A" (strerror write-fd)))
- (case direction
- (:input
- (push read-fd *close-in-parent*)
- (push write-fd *close-on-error*)
- (let ((stream (sb-sys:make-fd-stream write-fd :output t)))
- (values read-fd stream)))
- (:output
- (push read-fd *close-on-error*)
- (push write-fd *close-in-parent*)
- (let ((stream (sb-sys:make-fd-stream read-fd :input t)))
- (values write-fd stream)))
- (t
- (sb-unix:unix-close read-fd)
- (sb-unix:unix-close write-fd)
- (error "Direction must be either :INPUT or :OUTPUT, not ~S."
- direction)))))
- ((or (pathnamep object) (stringp object))
- (with-open-stream (file (apply #'open object keys))
- (multiple-value-bind
- (fd errno)
- (sb-unix:unix-dup (sb-sys:fd-stream-fd file))
- (cond (fd
- (push fd *close-in-parent*)
- (values fd nil))
- (t
- (error "couldn't duplicate file descriptor: ~A"
- (strerror errno)))))))
- ((sb-sys:fd-stream-p object)
- (values (sb-sys:fd-stream-fd object) nil))
- ((streamp object)
- (ecase direction
- (:input
- ;; FIXME: We could use a better way of setting up
- ;; temporary files, both here and in LOAD-FOREIGN.
- (dotimes (count
- 256
- (error "could not open a temporary file in /tmp"))
- (let* ((name (coerce (format nil "/tmp/.run-program-~D" count) 'base-string))
- (fd (sb-unix:unix-open name
- (logior sb-unix:o_rdwr
- sb-unix:o_creat
- sb-unix:o_excl)
- #o666)))
- (sb-unix:unix-unlink name)
- (when fd
- (let ((newline (string #\Newline)))
- (loop
- (multiple-value-bind
- (line no-cr)
- (read-line object nil nil)
- (unless line
- (return))
- (sb-unix:unix-write
+ ;; No new descriptor is needed.
+ (values -1 nil))
+ ((eq object nil)
+ ;; Use /dev/null.
+ (multiple-value-bind
+ (fd errno)
+ (sb-unix:unix-open #.(coerce "/dev/null" 'base-string)
+ (case direction
+ (:input sb-unix:o_rdonly)
+ (:output sb-unix:o_wronly)
+ (t sb-unix:o_rdwr))
+ #o666)
+ (unless fd
+ (error "~@<couldn't open \"/dev/null\": ~2I~_~A~:>"
+ (strerror errno)))
+ (push fd *close-in-parent*)
+ (values fd nil)))
+ ((eq object :stream)
+ (multiple-value-bind (read-fd write-fd) (sb-unix:unix-pipe)
+ (unless read-fd
+ (error "couldn't create pipe: ~A" (strerror write-fd)))
+ (case direction
+ (:input
+ (push read-fd *close-in-parent*)
+ (push write-fd *close-on-error*)
+ (let ((stream (sb-sys:make-fd-stream write-fd :output t)))
+ (values read-fd stream)))
+ (:output
+ (push read-fd *close-on-error*)
+ (push write-fd *close-in-parent*)
+ (let ((stream (sb-sys:make-fd-stream read-fd :input t)))
+ (values write-fd stream)))
+ (t
+ (sb-unix:unix-close read-fd)
+ (sb-unix:unix-close write-fd)
+ (error "Direction must be either :INPUT or :OUTPUT, not ~S."
+ direction)))))
+ ((or (pathnamep object) (stringp object))
+ (with-open-stream (file (apply #'open object keys))
+ (multiple-value-bind
+ (fd errno)
+ (sb-unix:unix-dup (sb-sys:fd-stream-fd file))
+ (cond (fd
+ (push fd *close-in-parent*)
+ (values fd nil))
+ (t
+ (error "couldn't duplicate file descriptor: ~A"
+ (strerror errno)))))))
+ ((sb-sys:fd-stream-p object)
+ (values (sb-sys:fd-stream-fd object) nil))
+ ((streamp object)
+ (ecase direction
+ (:input
+ ;; FIXME: We could use a better way of setting up
+ ;; temporary files, both here and in LOAD-FOREIGN.
+ (dotimes (count
+ 256
+ (error "could not open a temporary file in /tmp"))
+ (let* ((name (coerce (format nil "/tmp/.run-program-~D" count) 'base-string))
+ (fd (sb-unix:unix-open name
+ (logior sb-unix:o_rdwr
+ sb-unix:o_creat
+ sb-unix:o_excl)
+ #o666)))
+ (sb-unix:unix-unlink name)
+ (when fd
+ (let ((newline (string #\Newline)))
+ (loop
+ (multiple-value-bind
+ (line no-cr)
+ (read-line object nil nil)
+ (unless line
+ (return))
+ (sb-unix:unix-write
- (if no-cr
- (return)
- (sb-unix:unix-write fd newline 0 1)))))
- (sb-unix:unix-lseek fd 0 sb-unix:l_set)
- (push fd *close-in-parent*)
- (return (values fd nil))))))
- (:output
- (multiple-value-bind (read-fd write-fd)
- (sb-unix:unix-pipe)
- (unless read-fd
- (error "couldn't create pipe: ~S" (strerror write-fd)))
- (copy-descriptor-to-stream read-fd object cookie)
- (push read-fd *close-on-error*)
- (push write-fd *close-in-parent*)
- (values write-fd nil)))))
- (t
- (error "invalid option to RUN-PROGRAM: ~S" object))))
+ (if no-cr
+ (return)
+ (sb-unix:unix-write fd newline 0 1)))))
+ (sb-unix:unix-lseek fd 0 sb-unix:l_set)
+ (push fd *close-in-parent*)
+ (return (values fd nil))))))
+ (:output
+ (multiple-value-bind (read-fd write-fd)
+ (sb-unix:unix-pipe)
+ (unless read-fd
+ (error "couldn't create pipe: ~S" (strerror write-fd)))
+ (copy-descriptor-to-stream read-fd object cookie)
+ (push read-fd *close-on-error*)
+ (push write-fd *close-in-parent*)
+ (values write-fd nil)))))
+ (t
+ (error "invalid option to RUN-PROGRAM: ~S" object))))