- (let (;; Clear various specials used by GET-DESCRIPTOR-FOR to
- ;; 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)))
- (unwind-protect
- (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*))))))))))
- (dolist (fd *close-in-parent*)
- (sb-unix:unix-close fd))
- (unless proc
- (dolist (fd *close-on-error*)
+ (labels (;; It's friendly to allow the caller to pass any string
+ ;; designator, but internally we'd like SIMPLE-STRINGs.
+ ;;
+ ;; Huh? We let users pass in symbols and characters for
+ ;; the arguments, but call NAMESTRING on the program
+ ;; name... -- RMK
+ (simplify-args (args)
+ (loop for arg in args
+ as escaped-arg = (escape-arg arg)
+ collect (coerce escaped-arg 'simple-string)))
+ (escape-arg (arg)
+ #-win32 arg
+ ;; Apparently any spaces or double quotes in the arguments
+ ;; need to be escaped on win32.
+ #+win32 (if (position-if
+ (lambda (c) (find c '(#\" #\Space))) arg)
+ (write-to-string arg)
+ arg)))
+ (let (;; Clear various specials used by GET-DESCRIPTOR-FOR to
+ ;; communicate cleanup info.
+ *close-on-error*
+ *close-in-parent*
+ ;; Some other binding used only on non-Win32. FIXME:
+ ;; nothing seems to set this.
+ #-win32 *handlers-installed*
+ ;; Establish PROC at this level so that we can return it.
+ proc
+ (simple-args (simplify-args args))
+ (progname (native-namestring program))
+ ;; Gag.
+ (cookie (list 0)))
+ (unwind-protect
+ ;; Note: despite the WITH-* names, these macros don't
+ ;; expand into UNWIND-PROTECT forms. They're just
+ ;; syntactic sugar to make the rest of the routine slightly
+ ;; easier to read.
+ (macrolet ((with-fd-and-stream-for (((fd stream) which &rest args)
+ &body body)
+ `(multiple-value-bind (,fd ,stream)
+ ,(ecase which
+ ((:input :output)
+ `(get-descriptor-for ,@args))
+ (:error
+ `(if (eq ,(first args) :output)
+ ;; kludge: we expand into
+ ;; hard-coded symbols here.
+ (values stdout output-stream)
+ (get-descriptor-for ,@args))))
+ ,@body))
+ (with-open-pty (((pty-name pty-stream) (pty cookie))
+ &body body)
+ #+win32 `(declare (ignore ,pty ,cookie))
+ #+win32 `(let (,pty-name ,pty-stream) ,@body)
+ #-win32 `(multiple-value-bind (,pty-name ,pty-stream)
+ (open-pty ,pty ,cookie)
+ ,@body))
+ (with-args-vec ((vec args) &body body)
+ `(with-c-strvec (,vec ,args)
+ ,@body))
+ (with-environment-vec ((vec env) &body body)
+ #+win32 `(let (,vec) ,@body)
+ #-win32 `(with-c-strvec (,vec ,env) ,@body)))
+ (with-fd-and-stream-for ((stdin input-stream) :input
+ input cookie
+ :direction :input
+ :if-does-not-exist if-input-does-not-exist
+ :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 external-format)
+ (with-fd-and-stream-for ((stderr error-stream) :error
+ error cookie
+ :direction :output
+ :if-exists if-error-exists
+ :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
+ ;; structure in *ACTIVE-PROCESSES*.
+ (let (child)
+ (with-active-processes-lock ()
+ (with-args-vec (args-vec simple-args)
+ (with-environment-vec (environment-vec environment)
+ (setq child (without-gcing
+ (spawn progname args-vec
+ stdin stdout stderr
+ (if search 1 0)
+ environment-vec pty-name
+ (if wait 1 0))))
+ (unless (= child -1)
+ (setf proc
+ (apply
+ #'make-process
+ :pid child
+ :input input-stream
+ :output output-stream
+ :error error-stream
+ :status-hook status-hook
+ :cookie cookie
+ #-win32 (list :pty pty-stream
+ :%status :running)
+ #+win32 (if wait
+ (list :%status :exited
+ :exit-code child)
+ (list :%status :running))))
+ (push proc *active-processes*)))))
+ ;; Report the error outside the lock.
+ (when (= child -1)
+ (error "couldn't fork child process: ~A"
+ (strerror)))))))))
+ (dolist (fd *close-in-parent*)