Function: vc-do-command
vc-do-command is an autoloaded and byte-compiled function defined in
vc-dispatcher.el.gz.
Signature
(vc-do-command DESTINATION OKSTATUS COMMAND FILE-OR-LIST &rest FLAGS)
Documentation
Execute an inferior command, notifying user and checking for errors.
DESTINATION specifies what to do with COMMAND's output. It can be a buffer or the name of a buffer to insert output there, t to mean the current buffer, or nil to discard output. DESTINATION can also have the form (REAL-BUFFER STDERR-FILE); in that case, REAL-BUFFER says what to do with standard output, as above, while STDERR-FILE says what to do with standard error in the child. STDERR-FILE may only be nil which means to discard standard error output or t which means to mix it with standard output. If the destination for standard output is a buffer that is not the current buffer, set up the buffer properly and erase it.
OKSTATUS async means not to wait for termination of the subprocess and
return the process object. Otherwise, OKSTATUS determines when to
signal an error instead of returning a numeric exit status or signal
description string. OKSTATUS an integer means to signal an error if the
command's exit status exceeds that value or the command is killed by a
signal, nil means to signal an error only if the command is killed by a
signal, and t means never to signal an error.
FILE-OR-LIST is the name of a working file; it may be a list of files or be nil (to execute commands that don't expect a file name or set of files). If an optional list of FLAGS is present, that is inserted into the command line before the filename.
Source Code
;; Defined in /usr/src/emacs/lisp/vc/vc-dispatcher.el.gz
;;;###autoload
(defun vc-do-command (destination okstatus command file-or-list &rest flags)
"Execute an inferior command, notifying user and checking for errors.
DESTINATION specifies what to do with COMMAND's output. It can be a
buffer or the name of a buffer to insert output there, t to mean the
current buffer, or nil to discard output.
DESTINATION can also have the form (REAL-BUFFER STDERR-FILE); in that
case, REAL-BUFFER says what to do with standard output, as above, while
STDERR-FILE says what to do with standard error in the child.
STDERR-FILE may only be nil which means to discard standard error
output or t which means to mix it with standard output.
If the destination for standard output is a buffer that is not the
current buffer, set up the buffer properly and erase it.
OKSTATUS `async' means not to wait for termination of the subprocess and
return the process object. Otherwise, OKSTATUS determines when to
signal an error instead of returning a numeric exit status or signal
description string. OKSTATUS an integer means to signal an error if the
command's exit status exceeds that value or the command is killed by a
signal, nil means to signal an error only if the command is killed by a
signal, and t means never to signal an error.
FILE-OR-LIST is the name of a working file; it may be a list of
files or be nil (to execute commands that don't expect a file
name or set of files). If an optional list of FLAGS is present,
that is inserted into the command line before the filename."
;; STDERR-FILE is limited to nil or t, instead of also supporting
;; putting stderr output into a buffer or file, because of how we
;; support both synchronous and asynchronous execution.
;; `call-process' supports STDERR-FILE being a file name but not a
;; buffer, while `make-process' with `:file-handler' non-nil supports
;; putting stderr output in a buffer but not in a file (see Info node
;; `(elisp) Asynchronous Processes' for this detail). I.e. the only
;; options supported by both `call-process' and `make-process' are
;; discarding stderr output or mixing it with stdout.
(cl-assert (or (atom destination)
(and (length= destination 2)
(memq (cadr destination) '(t nil))))
nil
"Invalid DESTINATION argument to `vc-do-command': %s"
destination)
(pcase-let (;; Keep entire commands in *Messages* but avoid resizing the
;; echo area. Messages in this function are formatted in
;; a such way that the important parts are at the beginning,
;; due to potential truncation of long messages.
(message-truncate-lines t)
(vc-inhibit-message
(or (eq vc-command-messages 'log)
(eq (selected-window) (active-minibuffer-window))))
(`(,command ,file-or-list ,flags)
(funcall vc-filter-command-function
command file-or-list flags))
((or `(,stdout ,stderr) (and stdout (let stderr t)))
destination))
(save-current-buffer
(unless (or (memq stdout '(t nil))
(eq (current-buffer) (get-buffer stdout)))
(vc-setup-buffer stdout)
(setq stdout t))
(when vc-tor
(push command flags)
(setq command "torsocks"))
(let* (;; FIXME: file-relative-name can return a bogus result
;; because it doesn't look at the actual file-system to
;; see if symlinks come into play.
(files
(mapcar (lambda (f)
(file-relative-name (expand-file-name f)))
(ensure-list file-or-list)))
(full-command
(concat (if (equal (substring command -1) "\n")
(substring command 0 -1)
command)
" " (vc-delistify flags)
(and files (concat " " (vc-delistify files)))))
(squeezed (remq nil flags))
(inhibit-read-only t)
(status 0))
;; If there's some previous async process still running,
;; just kill it.
(when files
(setq squeezed (nconc squeezed files)))
(let (;; Since some functions need to parse the output
;; from external commands, set LC_MESSAGES to C.
(process-environment
(cons "LC_MESSAGES=C" process-environment))
(w32-quote-process-args t))
(if (eq okstatus 'async)
;; Run asynchronously.
(let* ((stderr-buf
(and (not stderr)
(generate-new-buffer " *temp*" t)))
(proc
(make-process :name command
:buffer (and stdout (current-buffer))
:command (cons command squeezed)
:connection-type 'pipe
:filter #'vc-process-filter
:sentinel #'ignore
:stderr stderr-buf
:file-handler t)))
(when stderr-buf
(vc-run-delayed (kill-buffer stderr-buf)))
(when vc-command-messages
(let ((inhibit-message vc-inhibit-message))
(message "Running in background: %s"
full-command)))
(setq status proc)
(when vc-command-messages
(vc-run-delayed
(let ((message-truncate-lines t)
(inhibit-message vc-inhibit-message))
(message "Done in background: %s"
full-command)))))
;; Run synchronously
(when vc-command-messages
(let ((inhibit-message vc-inhibit-message))
(message "Running in foreground: %s" full-command)))
(let ((buffer-undo-list t))
(setq status (apply #'process-file command nil
(list stdout stderr) nil squeezed)))
(when (and (not (eq t okstatus))
(or (not (integerp status))
(and okstatus (< okstatus status))))
(unless (eq ?\s (aref (buffer-name (current-buffer)) 0))
(pop-to-buffer (current-buffer))
(goto-char (point-min))
(shrink-window-if-larger-than-buffer))
(when-let* (noninteractive
(out (string-trim (buffer-string))))
(unless (string-empty-p out)
(message "%s" out)))
(error "Failed (%s): %s"
(if (integerp status)
(format "status %d" status)
status)
full-command))
(when vc-command-messages
(let ((inhibit-message vc-inhibit-message))
(message "Done (status=%d): %s"
status full-command)))))
(vc-run-delayed
(run-hook-with-args 'vc-post-command-functions
command file-or-list flags))
status))))