bug-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

bug#65902: 29.0.92; emacsclient-mail.desktop fails due to complicated es


From: sbaugh
Subject: bug#65902: 29.0.92; emacsclient-mail.desktop fails due to complicated escaping
Date: Tue, 24 Oct 2023 16:27:33 +0000 (UTC)
User-agent: Gnus/5.13 (Gnus v5.13)

Jim Porter <jporterbugs@gmail.com> writes:
> On 10/22/2023 12:53 PM, sbaugh@catern.com wrote:
>> Andreas Schwab <schwab@linux-m68k.org> writes:
>> 
>>> On Okt 22 2023, sbaugh@catern.com wrote:
>>>
>>>> +Exec=emacsclient --alternate-editor= --eval '(message-mailto (pop 
>>>> server-eval-args-left))' %u
>>>
>>> This line is not valid according to the specs.
>> Is that because of the single quotes surrounding spaces?  Are quoted
>> strings not permitted?
>
> According to the spec[1], you should use double quotes instead:
>
>> Quoting must be done by enclosing the argument between double quotes and 
>> escaping the double quote character, backtick character ("`"), dollar sign 
>> ("$") and backslash character ("\") by preceding it with an additional 
>> backslash character.
>
> Otherwise, I think what you have above is ok.
>
> [1]
> https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s07.html

Thanks.  OK, spec-compliant version:

>From 59d780f3297134d97cd39a8a4b0fb722d3d42765 Mon Sep 17 00:00:00 2001
From: Spencer Baugh <sbaugh@catern.com>
Date: Thu, 21 Sep 2023 21:35:50 -0400
Subject: [PATCH] Add server-eval-args-left to server.el

Passing arbitrary arguments to functions through emacsclient --eval
requires complicated escaping to avoid them being parsed as Lisp (as
seen in emacsclient-mail.desktop before this change).

This new variable server-eval-args-left allows access to the arguments
before they are parsed as Lisp.  By removing arguments from the
variable before they're parsed, a snippet of Lisp can consume
arguments, as in emacsclient-mail.desktop.

org-protocol might be able to use this as well, which might allow it
to drop its current advice on server-visit-files.

* etc/emacsclient-mail.desktop: Use server-eval-args-left. (bug#65902)
* lisp/server.el (server-eval-args-left): Add.
(server-process-filter, server-execute): Make -eval arguments
available through server-eval-args-left.
* lisp/startup.el (argv): Mention server-eval-args-left in docstring.
* etc/NEWS: Announce server-eval-args-left.
* doc/emacs/misc.texi (emacsclient Options): Document
server-eval-args-left.
---
 doc/emacs/misc.texi          |  9 +++++++++
 etc/NEWS                     | 10 ++++++++++
 etc/emacsclient-mail.desktop |  7 ++-----
 lisp/server.el               | 27 ++++++++++++++++++++-------
 lisp/startup.el              |  5 ++++-
 5 files changed, 45 insertions(+), 13 deletions(-)

diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi
index d7168fa1ca0..9c7c5dcd5da 100644
--- a/doc/emacs/misc.texi
+++ b/doc/emacs/misc.texi
@@ -2078,6 +2078,15 @@ emacsclient Options
 @command{emacsclient} are interpreted as a list of expressions to
 evaluate, @emph{not} as a list of files to visit.
 
+@vindex server-eval-args-left
+If you have arbitrary data which you want to provide as input to one
+of your expressions, you can pass the data as another argument to
+@command{emacsclient} and use @var{server-eval-args-left} in the
+expression to access the data.  Be careful to have your expression
+remove the data from @var{server-eval-args-left} regardless of whether
+your code succeeds, such as by using @code{pop}, otherwise Emacs will
+attempt to evaluate the data as a Lisp expression.
+
 @item -f @var{server-file}
 @itemx --server-file=@var{server-file}
 Specify a server file (@pxref{TCP Emacs server}) for connecting to an
diff --git a/etc/NEWS b/etc/NEWS
index e6c47660522..75da416c5f3 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -212,6 +212,16 @@ to enter the file you want to modify.
 It can be used to customize the look of the appointment notification
 displayed on the mode line when 'appt-display-mode-line' is non-nil.
 
+** Emacs Server and Client
+
+---
+*** 'server-eval-args-left' can be used to pop subsequent eval args
+When '--eval' is passed to emacsclient and Emacs is evaluating each
+argument, this variable is set to those which have not yet been
+evaluated.  It can be used to 'pop' arguments to prevent them from
+being evaluated, which is useful when those arguments contain
+arbitrary data.
+
 
 * Editing Changes in Emacs 30.1
 
diff --git a/etc/emacsclient-mail.desktop b/etc/emacsclient-mail.desktop
index 0a2420ddead..4f7f00ebefd 100644
--- a/etc/emacsclient-mail.desktop
+++ b/etc/emacsclient-mail.desktop
@@ -1,10 +1,7 @@
 [Desktop Entry]
 Categories=Network;Email;
 Comment=GNU Emacs is an extensible, customizable text editor - and more
-# We want to pass the following commands to the shell wrapper:
-# u=$(echo "$1" | sed 's/[\"]/\\&/g'); exec emacsclient --alternate-editor= 
--display="$DISPLAY" --eval "(message-mailto \"$u\")"
-# Special chars '"', '$', and '\' must be escaped as '\\"', '\\$', and '\\\\'.
-Exec=sh -c "u=\\$(echo \\"\\$1\\" | sed 's/[\\\\\\"]/\\\\\\\\&/g'); exec 
emacsclient --alternate-editor= --display=\\"\\$DISPLAY\\" --eval 
\\"(message-mailto \\\\\\"\\$u\\\\\\")\\"" sh %u
+Exec=emacsclient --alternate-editor= --eval "(message-mailto (pop 
server-eval-args-left))" %u
 Icon=emacs
 Name=Emacs (Mail, Client)
 MimeType=x-scheme-handler/mailto;
@@ -16,7 +13,7 @@ Actions=new-window;new-instance;
 
 [Desktop Action new-window]
 Name=New Window
-Exec=sh -c "u=\\$(echo \\"\\$1\\" | sed 's/[\\\\\\"]/\\\\\\\\&/g'); exec 
emacsclient --alternate-editor= --create-frame --eval \\"(message-mailto 
\\\\\\"\\$u\\\\\\")\\"" sh %u
+Exec=emacsclient --alternate-editor= --create-frame --eval "(message-mailto 
(pop server-eval-args-left))" %u
 
 [Desktop Action new-instance]
 Name=New Instance
diff --git a/lisp/server.el b/lisp/server.el
index ce68e9aebc9..a2671165bfc 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -1199,6 +1199,7 @@ server-process-filter
                parent-id  ; Window ID for XEmbed
                dontkill   ; t if client should not be killed.
                commands
+               evalexprs
                dir
                use-current-frame
                frame-parameters  ;parameters for newly created frame
@@ -1332,8 +1333,7 @@ server-process-filter
                  (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)
+                   (push expr evalexprs)
                    (setq filepos nil)))
 
                 ;; -env NAME=VALUE:  An environment variable.
@@ -1358,7 +1358,7 @@ server-process-filter
            ;; arguments, use an existing frame.
            (and nowait
                 (not (eq tty-name 'window-system))
-                (or files commands)
+                (or files commands evalexprs)
                 (setq use-current-frame t))
 
            (setq frame
@@ -1407,7 +1407,7 @@ server-process-filter
                  (let ((default-directory
                          (if (and dir (file-directory-p dir))
                              dir default-directory)))
-                   (server-execute proc files nowait commands
+                   (server-execute proc files nowait commands evalexprs
                                    dontkill frame tty-name)))))
 
             (when (or frame files)
@@ -1417,22 +1417,35 @@ server-process-filter
     ;; condition-case
     (t (server-return-error proc err))))
 
-(defun server-execute (proc files nowait commands dontkill frame tty-name)
+(defvar server-eval-args-left nil
+  "List of eval args not yet processed.
+
+Adding or removing strings from this variable while the Emacs
+server is processing a series of eval requests will affect what
+Emacs evaluates.
+
+See also `argv' for a similar variable which works for
+invocations of \"emacs\".")
+
+(defun server-execute (proc files nowait commands evalexprs dontkill frame 
tty-name)
   ;; This is run from timers and process-filters, i.e. "asynchronously".
   ;; But w.r.t the user, this is not really asynchronous since the timer
   ;; is run after 0s and the process-filter is run in response to the
   ;; user running `emacsclient'.  So it is OK to override the
-  ;; inhibit-quit flag, which is good since `commands' (as well as
+  ;; inhibit-quit flag, which is good since `evalexprs' (as well as
   ;; find-file-noselect via the major-mode) can run arbitrary code,
   ;; including code that needs to wait.
   (with-local-quit
     (condition-case err
         (let ((buffers (server-visit-files files proc nowait)))
           (mapc 'funcall (nreverse commands))
+          (let ((server-eval-args-left (nreverse evalexprs)))
+            (while server-eval-args-left
+              (server-eval-and-print (pop server-eval-args-left) proc)))
          ;; If we were told only to open a new client, obey
          ;; `initial-buffer-choice' if it specifies a file
           ;; or a function.
-          (unless (or files commands)
+          (unless (or files commands evalexprs)
             (let ((buf
                    (cond ((stringp initial-buffer-choice)
                          (find-file-noselect initial-buffer-choice))
diff --git a/lisp/startup.el b/lisp/startup.el
index 6329e3ea8d0..37843eab176 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -120,7 +120,10 @@ command-switch-alist
     "List of command-line args not yet processed.
 This is a convenience alias, so that one can write (pop argv)
 inside of --eval command line arguments in order to access
-following arguments."))
+following arguments.
+
+See also `server-eval-args-left' for a similar variable which
+works for invocations of \"emacsclient --eval\"."))
 (internal-make-var-non-special 'argv)
 
 (defvar command-line-args-left nil
-- 
2.41.0


reply via email to

[Prev in Thread] Current Thread [Next in Thread]