Function: server-process-filter
server-process-filter is a byte-compiled function defined in
server.el.gz.
Signature
(server-process-filter PROC STRING)
Documentation
Process a request from the server to edit some files.
PROC is the server process. STRING consists of a sequence of
commands prefixed by a dash. Some commands have arguments;
these are &-quoted and need to be decoded by server-unquote-arg.
The filter parses and executes these commands.
To illustrate the protocol, here is an example command that emacsclient sends to create a new X frame (note that the whole sequence is sent on a single line):
-env HOME=/home/lorentey
-env DISPLAY=:0.0
... lots of other -env commands
-display :0.0
-window-system
The following commands are accepted by the server:
-auth AUTH-STRING
Authenticate the client using the secret authentication string
AUTH-STRING.
-env NAME=VALUE
An environment variable on the client side.
-dir DIRNAME
The current working directory of the client process.
-current-frame
Forbid the creation of new frames.
-frame-parameters ALIST
Set the parameters of the created frame.
-nowait
Request that the next frame created should not be
associated with this client.
-display DISPLAY
Set the display name to open X frames on.
-position +LINE[:COLUMN]
Go to the given line and column number
in the next file opened.
-file FILENAME
Load the given file in the current frame.
-eval EXPR
Evaluate EXPR as a Lisp expression and return the
result in -print commands.
-window-system
Open a new X frame.
-tty DEVICENAME TYPE
Open a new tty frame at the client.
-suspend
Suspend this tty frame. The client sends this string in
response to SIGTSTP and SIGTTOU. The server must cease all I/O
on this tty until it gets a -resume command.
-resume
Resume this tty frame. The client sends this string when it
gets the SIGCONT signal and it is the foreground process on its
controlling tty.
-ignore COMMENT
Do nothing, but put the comment in the server log.
Useful for debugging.
The following commands are accepted by the client:
-emacs-pid PID
Describes the process id of the Emacs process;
used to forward window change signals to it.
-window-system-unsupported
Signals that the server does not support creating X frames;
the client must try again with a tty frame.
-print STRING
Print STRING on stdout. Used to send values
returned by -eval.
-print-nonl STRING
Print STRING on stdout. Used to continue a
preceding -print command that would be too big to send
in a single message.
-error DESCRIPTION
Signal an error and delete process PROC.
-suspend
Suspend this terminal, i.e., stop the client process.
Sent when the user presses C-z (suspend-frame).
Source Code
;; Defined in /usr/src/emacs/lisp/server.el.gz
(cl-defun server-process-filter (proc string)
"Process a request from the server to edit some files.
PROC is the server process. STRING consists of a sequence of
commands prefixed by a dash. Some commands have arguments;
these are &-quoted and need to be decoded by `server-unquote-arg'.
The filter parses and executes these commands.
To illustrate the protocol, here is an example command that
emacsclient sends to create a new X frame (note that the whole
sequence is sent on a single line):
-env HOME=/home/lorentey
-env DISPLAY=:0.0
... lots of other -env commands
-display :0.0
-window-system
The following commands are accepted by the server:
`-auth AUTH-STRING'
Authenticate the client using the secret authentication string
AUTH-STRING.
`-env NAME=VALUE'
An environment variable on the client side.
`-dir DIRNAME'
The current working directory of the client process.
`-current-frame'
Forbid the creation of new frames.
`-frame-parameters ALIST'
Set the parameters of the created frame.
`-nowait'
Request that the next frame created should not be
associated with this client.
`-display DISPLAY'
Set the display name to open X frames on.
`-position +LINE[:COLUMN]'
Go to the given line and column number
in the next file opened.
`-file FILENAME'
Load the given file in the current frame.
`-eval EXPR'
Evaluate EXPR as a Lisp expression and return the
result in -print commands.
`-window-system'
Open a new X frame.
`-tty DEVICENAME TYPE'
Open a new tty frame at the client.
`-suspend'
Suspend this tty frame. The client sends this string in
response to SIGTSTP and SIGTTOU. The server must cease all I/O
on this tty until it gets a -resume command.
`-resume'
Resume this tty frame. The client sends this string when it
gets the SIGCONT signal and it is the foreground process on its
controlling tty.
`-ignore COMMENT'
Do nothing, but put the comment in the server log.
Useful for debugging.
The following commands are accepted by the client:
`-emacs-pid PID'
Describes the process id of the Emacs process;
used to forward window change signals to it.
`-window-system-unsupported'
Signals that the server does not support creating X frames;
the client must try again with a tty frame.
`-print STRING'
Print STRING on stdout. Used to send values
returned by -eval.
`-print-nonl STRING'
Print STRING on stdout. Used to continue a
preceding -print command that would be too big to send
in a single message.
`-error DESCRIPTION'
Signal an error and delete process PROC.
`-suspend'
Suspend this terminal, i.e., stop the client process.
Sent when the user presses \\[suspend-frame]."
(server-log (concat "Received " string) proc)
;; First things first: let's check the authentication
(unless (process-get proc :authenticated)
(if (and (string-match "-auth \\([!-~]+\\)\n?" string)
(equal (match-string 1 string) (process-get proc :auth-key)))
(progn
(setq string (substring string (match-end 0)))
(process-put proc :authenticated t)
(server-log "Authentication successful" proc))
(server-log "Authentication failed" proc)
(server-send-string
proc (concat "-error " (server-quote-arg "Authentication failed")))
;; Before calling `delete-process', give emacsclient time to
;; receive the error string and shut down on its own.
(sit-for 1)
(delete-process proc)
;; We return immediately.
(cl-return-from server-process-filter)))
(let ((prev (process-get proc 'previous-string)))
(when prev
(setq string (concat prev string))
(process-put proc 'previous-string nil)))
(condition-case err
(progn
(server-add-client proc)
;; Send our pid
(server-send-string proc (concat "-emacs-pid "
(number-to-string (emacs-pid)) "\n"))
(if (not (string-match "\n" string))
;; Save for later any partial line that remains.
(when (> (length string) 0)
(process-put proc 'previous-string string))
;; In earlier versions of server.el (where we used an `emacsserver'
;; process), there could be multiple lines. Nowadays this is not
;; supported any more.
(cl-assert (eq (match-end 0) (length string)))
(let ((request (substring string 0 (match-beginning 0)))
(coding-system (or file-name-coding-system
default-file-name-coding-system))
nowait ; t if emacsclient does not want to wait for us.
frame ; Frame opened for the client (if any).
display ; Open frame on this display.
parent-id ; Window ID for XEmbed
dontkill ; t if client should not be killed.
commands
dir
use-current-frame
frame-parameters ;parameters for newly created frame
tty-name ; nil, `window-system', or the tty name.
tty-type ; string.
files
filepos
args-left)
;; Remove this line from STRING.
(setq string (substring string (match-end 0)))
(setq args-left
(mapcar #'server-unquote-arg (split-string request " " t)))
(while args-left
(pcase (pop args-left)
;; -version CLIENT-VERSION: obsolete at birth.
("-version" (pop args-left))
;; -nowait: Emacsclient won't wait for a result.
("-nowait" (setq nowait t))
;; -current-frame: Don't create frames.
("-current-frame" (setq use-current-frame t))
;; -frame-parameters: Set frame parameters
("-frame-parameters"
(let ((alist (pop args-left)))
(if coding-system
(setq alist (decode-coding-string alist coding-system)))
(setq frame-parameters (car (read-from-string alist)))))
;; -display DISPLAY:
;; Open X frames on the given display instead of the default.
("-display"
(setq display (pop args-left))
(if (zerop (length display)) (setq display nil)))
;; -parent-id ID:
;; Open X frame within window ID, via XEmbed.
("-parent-id"
(setq parent-id (pop args-left))
(if (zerop (length parent-id)) (setq parent-id nil)))
;; -window-system: Open a new X frame.
("-window-system"
(if (fboundp 'x-create-frame)
(setq dontkill t
tty-name 'window-system)))
;; -resume: Resume a suspended tty frame.
("-resume"
(let ((terminal (process-get proc 'terminal)))
(setq dontkill t)
(push (lambda ()
(when (eq (terminal-live-p terminal) t)
(resume-tty terminal)))
commands)))
;; -suspend: Suspend the client's frame. (In case we
;; get out of sync, and a C-z sends a SIGTSTP to
;; emacsclient.)
("-suspend"
(let ((terminal (process-get proc 'terminal)))
(setq dontkill t)
(push (lambda ()
(when (eq (terminal-live-p terminal) t)
(suspend-tty terminal)))
commands)))
;; -ignore COMMENT: Noop; useful for debugging emacsclient.
;; (The given comment appears in the server log.)
("-ignore"
(setq dontkill t)
(pop args-left))
;; -tty DEVICE-NAME TYPE: Open a new tty frame.
;; (But if we see -window-system later, use that.)
("-tty"
(setq tty-name (pop args-left)
tty-type (pop args-left)
dontkill (or dontkill
(not use-current-frame)))
;; On Windows, emacsclient always asks for a tty
;; frame. If running a GUI server, force the frame
;; type to GUI. (Cygwin is perfectly happy with
;; multi-tty support, so don't override the user's
;; choice there.) In daemon mode on Windows, we can't
;; make tty frames, so force the frame type to GUI
;; there too.
(when (or (and (eq system-type 'windows-nt)
(or (daemonp)
(eq window-system 'w32)))
;; Client runs on Windows, but the server
;; runs on a Posix host.
(equal tty-name "CONOUT$"))
(push "-window-system" args-left)))
;; -position +LINE[:COLUMN]: Set point to the given
;; position in the next file.
("-position"
(if (not (string-match "\\+\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)?"
(car args-left)))
(error "Invalid -position command in client args"))
(let ((arg (pop args-left)))
(setq filepos
(cons (string-to-number (match-string 1 arg))
(string-to-number (or (match-string 2 arg)
""))))))
;; -file FILENAME: Load the given file.
("-file"
(let ((file (pop args-left)))
(if coding-system
(setq file (decode-coding-string file coding-system)))
;; Allow Cygwin's emacsclient to be used as a file
;; handler on MS-Windows, in which case FILENAME
;; might start with a drive letter.
(when (and (fboundp 'cygwin-convert-file-name-from-windows)
(string-match "\\`[A-Za-z]:" file))
(setq file (cygwin-convert-file-name-from-windows file)))
(setq file (expand-file-name file dir))
(push (cons file filepos) files)
(server-log (format "New file: %s %s"
file (or filepos ""))
proc))
(setq filepos nil))
;; -eval EXPR: Evaluate a Lisp expression.
("-eval"
(if use-current-frame
(setq use-current-frame 'always))
(let ((expr (pop args-left)))
(if coding-system
(setq expr (decode-coding-string expr coding-system)))
(push (lambda () (server-eval-and-print expr proc))
commands)
(setq filepos nil)))
;; -env NAME=VALUE: An environment variable.
("-env"
(let ((var (pop args-left)))
;; XXX Variables should be encoded as in getenv/setenv.
(process-put proc 'env
(cons var (process-get proc 'env)))))
;; -dir DIRNAME: The cwd of the emacsclient process.
("-dir"
(setq dir (pop args-left))
(if coding-system
(setq dir (decode-coding-string dir coding-system)))
(setq dir (command-line-normalize-file-name dir))
(process-put proc 'server-client-directory dir))
;; Unknown command.
(arg (error "Unknown command: %s" arg))))
;; If both -no-wait and -tty are given with file or sexp
;; arguments, use an existing frame.
(and nowait
(not (eq tty-name 'window-system))
(or files commands)
(setq use-current-frame t))
(setq frame
(cond
((and use-current-frame
(or (eq use-current-frame 'always)
;; We can't use the Emacs daemon's
;; terminal frame.
(not (and (daemonp)
(null (cdr (frame-list)))
(eq (selected-frame)
terminal-frame)))))
(setq tty-name nil tty-type nil)
(if display (server-select-display display)))
((equal tty-type "dumb")
(server-create-dumb-terminal-frame nowait proc
frame-parameters))
((or (and (eq system-type 'windows-nt)
(daemonp)
(setq display "w32"))
(eq tty-name 'window-system))
(server-create-window-system-frame display nowait proc
parent-id
frame-parameters))
;; When resuming on a tty, tty-name is nil.
(tty-name
(server-create-tty-frame tty-name tty-type proc
frame-parameters))
;; If there won't be a current frame to use, fall
;; back to trying to create a new one.
((and use-current-frame
(daemonp)
(null (cdr (frame-list)))
(eq (selected-frame) terminal-frame)
display)
(setq tty-name nil tty-type nil)
(server-select-display display))))
(process-put
proc 'continuation
(lambda ()
(with-current-buffer (get-buffer-create server-buffer)
;; Use the same cwd as the emacsclient, if possible, so
;; relative file names work correctly, even in `eval'.
(let ((default-directory
(if (and dir (file-directory-p dir))
dir default-directory)))
(server-execute proc files nowait commands
dontkill frame tty-name)))))
(when (or frame files)
(server-goto-toplevel proc))
(server-execute-continuation proc))))
;; condition-case
(t (server-return-error proc err))))