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 (and (eq system-type 'windows-nt)
                            (or (daemonp)
                                (eq window-system 'w32)))
                   (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))

                   ;; 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))))