[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: |
Wed, 13 Sep 2023 02:30:08 +0000 (UTC) |
User-agent: |
Gnus/5.13 (Gnus v5.13) |
tags 65902 + patch
quit
This patch avoids the complicated scripting needed for
emacsclient-mail.desktop by adding a new flag to emacsclient, --funcall,
which mirrors emacs --funcall and allows emacsclient-mail.desktop to be
basically the same as emacs-mail.desktop.
I expect this to also be useful in other places; the need to escape
arbitrary inputs before passing them to emacsclient is frequently
annoying.
>From 6c9dbda3aa5e3f1e3a5003bc0a15cf662b880d99 Mon Sep 17 00:00:00 2001
From: Spencer Baugh <sbaugh@catern.com>
Date: Tue, 12 Sep 2023 22:20:15 -0400
Subject: [PATCH] Add --funcall to emacsclient to avoid escaping arguments
Passing arguments to functions through emacsclient --eval requires
complicated escaping (as seen in emacsclient-mail.desktop before this
change). The new --funcall argument behaves like emacs -f, and just
passes the arguments as uninterpreted strings to the specified
function. This simplifies use cases where arbitrary input needs to be
passed to emacsclient.
* etc/emacsclient-mail.desktop: Use --funcall. (bug#65902)
* lib-src/emacsclient.c (longopts, decode_options, main): Add support
for --funcall.
* lisp/server.el (server-funcall-and-print): Add.
(server-process-filter): Add support for -funcall and -funcallargs
---
etc/emacsclient-mail.desktop | 7 ++-----
lib-src/emacsclient.c | 36 ++++++++++++++++++++++++++++++++++--
lisp/server.el | 34 ++++++++++++++++++++++++++++++++++
3 files changed, 70 insertions(+), 7 deletions(-)
diff --git a/etc/emacsclient-mail.desktop b/etc/emacsclient-mail.desktop
index 0a2420ddead..fc6773a963b 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= --funcall message-mailto %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 --funcall message-mailto %u
[Desktop Action new-instance]
Name=New Instance
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index 698bf9b50ae..5bf6c05ef37 100644
--- a/lib-src/emacsclient.c
+++ b/lib-src/emacsclient.c
@@ -116,6 +116,9 @@ #define DEFAULT_TIMEOUT (30)
/* True means args are expressions to be evaluated. --eval. */
static bool eval;
+/* The function to call. Other arguments are passed as strings. --funcall.
*/
+static char *funcall;
+
/* True means open a new frame. --create-frame etc. */
static bool create_frame;
@@ -169,6 +172,7 @@ #define DEFAULT_TIMEOUT (30)
{ "quiet", no_argument, NULL, 'q' },
{ "suppress-output", no_argument, NULL, 'u' },
{ "eval", no_argument, NULL, 'e' },
+ { "funcall", required_argument, NULL, 'l' },
{ "help", no_argument, NULL, 'H' },
{ "version", no_argument, NULL, 'V' },
{ "tty", no_argument, NULL, 't' },
@@ -552,6 +556,10 @@ decode_options (int argc, char **argv)
eval = true;
break;
+ case 'l':
+ funcall = optarg;
+ break;
+
case 'q':
quiet = true;
break;
@@ -690,6 +698,7 @@ print_help_and_exit (void)
-F ALIST, --frame-parameters=ALIST\n\
Set the parameters of a new frame\n\
-e, --eval Evaluate the FILE arguments as ELisp expressions\n\
+-l, --funcall FUNC Call ELisp FUNC, passing FILE arguments as strings\n\
-n, --no-wait Don't wait for the server to return\n\
-w, --timeout=SECONDS Seconds to wait before timing out\n\
-q, --quiet Don't display messages on success\n\
@@ -1953,7 +1962,7 @@ main (int argc, char **argv)
/* Process options. */
decode_options (argc, argv);
- if (! (optind < argc || eval || create_frame))
+ if (! (optind < argc || eval || funcall || create_frame))
{
message (true, ("%s: file name or argument required\n"
"Try '%s --help' for more information\n"),
@@ -1961,6 +1970,14 @@ main (int argc, char **argv)
exit (EXIT_FAILURE);
}
+ if (eval && funcall)
+ {
+ message (true, ("%s: can't pass both --eval and --funcall\n"
+ "Try '%s --help' for more information\n"),
+ progname, progname);
+ exit (EXIT_FAILURE);
+ }
+
#ifdef SOCKETS_IN_FILE_SYSTEM
if (tty)
{
@@ -2080,6 +2097,13 @@ main (int argc, char **argv)
send_to_emacs (emacs_socket, " ");
continue;
}
+ else if (funcall)
+ {
+ send_to_emacs (emacs_socket, "-funcallarg ");
+ quote_argument (emacs_socket, argv[i]);
+ send_to_emacs (emacs_socket, " ");
+ continue;
+ }
char *p = argv[i];
if (*p == '+')
@@ -2136,10 +2160,18 @@ main (int argc, char **argv)
send_to_emacs (emacs_socket, " ");
}
+ if (funcall)
+ {
+ send_to_emacs (emacs_socket, "-funcall ");
+ quote_argument (emacs_socket, funcall);
+ send_to_emacs (emacs_socket, " ");
+ }
+
+
send_to_emacs (emacs_socket, "\n");
/* Wait for an answer. */
- if (!eval && !tty && !nowait && !quiet && 0 <= process_grouping ())
+ if (!eval && !funcall && !tty && !nowait && !quiet && 0 <= process_grouping
())
{
printf ("Waiting for Emacs...");
skiplf = false;
diff --git a/lisp/server.el b/lisp/server.el
index c3325e5a24c..da3319581ea 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -873,6 +873,17 @@ server-eval-and-print
(point-min) (point-max))))
(server-reply-print (server-quote-arg text) proc)))))))
+(defun server-funcall-and-print (func args proc)
+ "Call FUNC on ARGS and send the result back to client PROC."
+ (let ((v (with-local-quit (eval (apply (intern func) args) t))))
+ (when proc
+ (with-temp-buffer
+ (let ((standard-output (current-buffer)))
+ (pp v)
+ (let ((text (buffer-substring-no-properties
+ (point-min) (point-max))))
+ (server-reply-print (server-quote-arg text) proc)))))))
+
(defconst server-msg-size 1024
"Maximum size of a message sent to a client.")
@@ -1196,6 +1207,7 @@ server-process-filter
tty-type ; string.
files
filepos
+ funcallargs
args-left)
;; Remove this line from STRING.
(setq string (substring string (match-end 0)))
@@ -1323,6 +1335,28 @@ server-process-filter
commands)
(setq filepos nil)))
+ ;; -funcall FUNC: Call a function on arguments.
+ ("-funcall"
+ (if use-current-frame
+ (setq use-current-frame 'always))
+ (let ((func (pop args-left)))
+ (if coding-system
+ (setq func (decode-coding-string func coding-system)))
+ (push (lambda () (server-funcall-and-print func funcallargs
proc))
+ commands)
+ (setq funcallargs nil)
+ (setq filepos nil)))
+
+ ;; -funcallarg ARG: Add an argument for later -funcall.
+ ("-funcallarg"
+ (if use-current-frame
+ (setq use-current-frame 'always))
+ (let ((arg (pop args-left)))
+ (if coding-system
+ (setq arg (decode-coding-string arg coding-system)))
+ (push arg funcallargs)
+ (setq filepos nil)))
+
;; -env NAME=VALUE: An environment variable.
("-env"
(let ((var (pop args-left)))
--
2.41.0