Function: eshell-gather-process-output

eshell-gather-process-output is a byte-compiled function defined in esh-proc.el.gz.

Signature

(eshell-gather-process-output COMMAND ARGS)

Documentation

Gather the output from COMMAND + ARGS.

Source Code

;; Defined in /usr/src/emacs/lisp/eshell/esh-proc.el.gz
(defun eshell-gather-process-output (command args)
  "Gather the output from COMMAND + ARGS."
  (require 'esh-var)
  (declare-function eshell-environment-variables "esh-var" ())
  (unless (and (file-executable-p command)
	       (file-regular-p (file-truename command)))
    (error "%s: not an executable file" command))
  (let* ((real-path (getenv "PATH"))
         (tramp-remote-path (bound-and-true-p tramp-remote-path))
         (delete-exited-processes
	  (if eshell-current-subjob-p
	      eshell-delete-exited-processes
	    delete-exited-processes))
	 (process-environment (eshell-environment-variables))
         (coding-system-for-read coding-system-for-read)
         (coding-system-for-write coding-system-for-write)
	 proc stderr-proc decoding encoding changed)
    ;; HACK: We want to supply our subprocess with the all the
    ;; environment variables we've set in Eshell.  However, supplying
    ;; a remote PATH this way can break Tramp, which needs the *local*
    ;; PATH for calling "ssh", etc.  Instead, set the local path in
    ;; our `process-environment' and pass the remote PATH via
    ;; `tramp-remote-path'.  (If we handle this some better way in the
    ;; future, remember to remove `tramp-remote-path' above, too.)
    (when (file-remote-p default-directory)
      (push (concat "PATH=" real-path) process-environment)
      (setq tramp-remote-path (eshell-get-path t)))
    ;; MS-Windows needs special setting of encoding/decoding, because
    ;; (a) non-ASCII text in command-line arguments needs to be
    ;; encoded in the system's codepage; and (b) because many Windows
    ;; programs will always interpret any non-ASCII input as encoded
    ;; in the system codepage.
    (when (eq system-type 'windows-nt)
      (or coding-system-for-read        ; Honor manual decoding settings
          (setq coding-system-for-read
                (coding-system-change-eol-conversion locale-coding-system
                                                     'dos)))
      (or coding-system-for-write       ; Honor manual encoding settings
          (setq coding-system-for-write
                (coding-system-change-eol-conversion locale-coding-system
                                                     'unix))))
    (cond
     (eshell-supports-asynchronous-processes
      (unless (or ;; FIXME: It's not currently possible to use a
                  ;; stderr process for remote files.
                  (file-remote-p default-directory)
                  (equal (car (aref eshell-current-handles
                                    eshell-output-handle))
                         (car (aref eshell-current-handles
                                    eshell-error-handle))))
        (eshell-protect-handles eshell-current-handles)
        (setq stderr-proc
              (make-pipe-process
               :name (concat (file-name-nondirectory command) "-stderr")
               :buffer (current-buffer)
               :filter (if (eshell-interactive-output-p eshell-error-handle)
                           #'eshell-interactive-process-filter
                         #'eshell-insertion-filter)
               :sentinel #'eshell-sentinel))
        (eshell-record-process-properties stderr-proc eshell-error-handle))
      (eshell-protect-handles eshell-current-handles)
      (setq proc
            (let ((command (file-local-name (expand-file-name command)))
                  (conn-type (pcase (bound-and-true-p eshell-in-pipeline-p)
                               ('first '(nil . pipe))
                               ('last  '(pipe . nil))
                               ('t     'pipe)
                               ('nil   nil))))
              (make-process
               :name (file-name-nondirectory command)
               :buffer (current-buffer)
               :command (cons command args)
               :filter (if (eshell-interactive-output-p)
                           #'eshell-interactive-process-filter
                         #'eshell-insertion-filter)
               :sentinel #'eshell-sentinel
               :connection-type conn-type
               :stderr stderr-proc
               :file-handler t)))
      (eshell-debug-command 'process
        "started external process `%s'\n\n%s" proc
        (mapconcat #'shell-quote-argument (process-command proc) " "))
      (eshell-record-process-object proc)
      (eshell-record-process-properties proc)
      ;; Don't set exit info for processes being piped elsewhere.
      (when (memq (bound-and-true-p eshell-in-pipeline-p) '(nil last))
        (process-put proc :eshell-set-exit-info t))
      (when stderr-proc
        ;; Provide a shared flag between the primary and stderr
        ;; processes.  This lets the primary process wait to clean up
        ;; until stderr is totally finished (see `eshell-sentinel').
        (let ((stderr-live (list t)))
          (process-put proc :eshell-stderr-live stderr-live)
          (process-put stderr-proc :eshell-stderr-live stderr-live)))
      (run-hook-with-args 'eshell-exec-hook proc)
      (when (fboundp 'process-coding-system)
	(let ((coding-systems (process-coding-system proc)))
	  (setq decoding (car coding-systems)
		encoding (cdr coding-systems)))
	;; If `make-process' decided to use some coding system for
	;; decoding data sent from the process and the coding system
	;; doesn't specify EOL conversion, we had better convert CRLF
	;; to LF.
	(if (vectorp (coding-system-eol-type decoding))
	    (setq decoding (coding-system-change-eol-conversion decoding 'dos)
		  changed t))
	;; Even if `make-process' left the coding system for encoding
	;; data sent to the process undecided, we had better use the
	;; same one as what we use for decoding.  But, we should
	;; suppress EOL conversion.
	(if (and decoding (not encoding))
	    (setq encoding (coding-system-change-eol-conversion decoding 'unix)
		  changed t))
	(if changed
	    (set-process-coding-system proc decoding encoding))))
     (t
      ;; No async subprocesses...
      (let ((oldbuf (current-buffer))
	    (interact-p (eshell-interactive-output-p))
	    lbeg lend line proc-buf exit-status)
	(and (not (markerp eshell-last-sync-output-start))
	     (setq eshell-last-sync-output-start (point-marker)))
	(setq proc-buf
	      (set-buffer (get-buffer-create eshell-scratch-buffer)))
	(erase-buffer)
	(set-buffer oldbuf)
	(run-hook-with-args 'eshell-exec-hook command)
        ;; XXX: This doesn't support sending stdout and stderr to
        ;; separate places.
	(setq exit-status
	      (apply #'call-process-region
		     (append (list eshell-last-sync-output-start (point)
				   command t
				   eshell-scratch-buffer nil)
			     args)))
	;; When in a pipeline, record the place where the output of
	;; this process will begin.
	(and (bound-and-true-p eshell-in-pipeline-p)
	     (set-marker eshell-last-sync-output-start (point)))
	;; Simulate the effect of the process filter.
	(when (numberp exit-status)
	  (set-buffer proc-buf)
	  (goto-char (point-min))
	  (setq lbeg (point))
	  (while (eq 0 (forward-line 1))
	    (setq lend (point)
		  line (buffer-substring-no-properties lbeg lend))
	    (set-buffer oldbuf)
	    (if interact-p
		(eshell-interactive-process-filter nil line)
	      (eshell-output-object line))
	    (setq lbeg lend)
	    (set-buffer proc-buf))
	  (set-buffer oldbuf))
        ;; Simulate the effect of `eshell-sentinel'.
        (eshell-set-exit-info
         (if (numberp exit-status) exit-status -1)
         (and (numberp exit-status) (= exit-status 0)))
	(run-hook-with-args 'eshell-kill-hook command exit-status)
	(or (bound-and-true-p eshell-in-pipeline-p)
	    (setq eshell-last-sync-output-start nil))
	(if (not (numberp exit-status))
	  (error "%s: external command failed: %s" command exit-status))
	(setq proc t))))
    proc))