From: sbaugh@catern.com
To: Jim Porter <jporterbugs@gmail.com>
Cc: 65902@debbugs.gnu.org
Subject: bug#65902: 29.0.92; emacsclient-mail.desktop fails due to complicated escaping
Date: Wed, 13 Sep 2023 12:57:40 +0000 (UTC) [thread overview]
Message-ID: <87r0n2tem4.fsf@catern.com> (raw)
In-Reply-To: <d796795f-7900-9140-8443-12f812cf8983@gmail.com> (Jim Porter's message of "Tue, 12 Sep 2023 20:46:54 -0700")
[-- Attachment #1: Type: text/plain, Size: 1750 bytes --]
Jim Porter <jporterbugs@gmail.com> writes:
> On 9/12/2023 7:30 PM, sbaugh@catern.com wrote:
>> 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 think this is actually the same as the (very long) bug#57752, so
> thanks for working on this. (It was on my list of things to get to,
> but I just haven't had time.)
>
> Over there, we agreed that something like your patch is wanted, albeit
> with two caveats:
>
> 1. Since "--funcall" for the regular "emacs" binary doesn't pass
> arguments to the function, how about we call this option "--apply"
> instead?
Ah, for some reason I thought --funcall passed arguments. Done in
attached patch.
> 2. It would be great if we could get "--apply" for the regular "emacs"
> binary too, so that both programs work the same way (at least in this
> regard).
Done.
Although the behavior is slightly different: emacs --apply calls the
function with subsequent FILE arguments, and emacsclient --apply calls
the function with all FILE arguments. The "subsequent FILE arguments"
behavior is probably better, but I don't know a way to do that with
getopt (which emacsclient uses).
> Even better, if you could forward "--apply" from
> "emacsclient" to the alternate editor (which would be "emacs" 99% of
> the time) automatically. That works, in a roundabout way, for the
> Emacs daemon, but not if the alternate editor is "emacs".
This happens automatically anyway: When emacsclient starts the daemon,
it sends the --apply request. I think that's exactly what you'd want.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-apply-argument-to-avoid-escaping-arguments.patch --]
[-- Type: text/x-patch, Size: 12161 bytes --]
From 4881017055ea6831ee7fe2d722eb79856946d907 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 --apply argument to avoid escaping arguments
Passing arguments to functions through emacs --eval or emacsclient
--eval requires complicated escaping (as seen in
emacsclient-mail.desktop before this change).
The new --apply argument for both emacs and emacsclient passes command
line arguments as uninterpreted strings to the specified function.
This simplifies use cases where arbitrary input needs to be passed to
Emacs.
Note that there's a minor difference in behavior between emacs --apply
and emacsclient --apply: emacs --apply func calls func with only
command line arguments occuring after --apply, emacsclient --apply
func calls func with all command line arguments, even those which
occurred before --apply. This is hard to avoid since emacsclient uses
getopt instead of fancier custom Lisp argument parsing in Emacs.
* etc/emacsclient-mail.desktop: Use --apply. (bug#65902)
* lib-src/emacsclient.c (longopts, decode_options, main): Add support
for --apply.
* lisp/server.el (server-apply-and-print): Add.
(server-process-filter): Add support for -apply and -applyargs
* lisp/startup.el (command-line-1): Add support for --apply.
* src/emacs.c (usage_message, standard_args): Add support for --apply.
---
etc/emacsclient-mail.desktop | 9 +++------
lib-src/emacsclient.c | 36 ++++++++++++++++++++++++++++++++++--
lisp/server.el | 34 ++++++++++++++++++++++++++++++++++
lisp/startup.el | 18 +++++++++++++++---
src/emacs.c | 3 +++
5 files changed, 89 insertions(+), 11 deletions(-)
diff --git a/etc/emacsclient-mail.desktop b/etc/emacsclient-mail.desktop
index 0a2420ddead..750fcddacdc 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= --apply message-mailto -- %u
Icon=emacs
Name=Emacs (Mail, Client)
MimeType=x-scheme-handler/mailto;
@@ -16,8 +13,8 @@ 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 --apply message-mailto -- %u
[Desktop Action new-instance]
Name=New Instance
-Exec=emacs -f message-mailto %u
+Exec=emacs --apply message-mailto -- %u
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index 698bf9b50ae..159c22d1ae9 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. --apply. */
+static char *apply;
+
/* 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' },
+ { "apply", required_argument, NULL, 'y' },
{ "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 'y':
+ apply = 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\
+-y, --apply FUNC Call ELisp FUNC, passing all 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 || apply || 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 && apply)
+ {
+ message (true, ("%s: can't pass both --eval and --apply\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 (apply)
+ {
+ send_to_emacs (emacs_socket, "-applyarg ");
+ 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 (apply)
+ {
+ send_to_emacs (emacs_socket, "-apply ");
+ quote_argument (emacs_socket, apply);
+ 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 && !apply && !tty && !nowait && !quiet && 0 <= process_grouping ())
{
printf ("Waiting for Emacs...");
skiplf = false;
diff --git a/lisp/server.el b/lisp/server.el
index c3325e5a24c..5981e90625d 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-apply-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
+ applyargs
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)))
+ ;; -apply FUNC: Call a function on arguments.
+ ("-apply"
+ (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-apply-and-print func applyargs proc))
+ commands)
+ (setq applyargs nil)
+ (setq filepos nil)))
+
+ ;; -applyarg ARG: Add an argument for later -apply.
+ ("-applyarg"
+ (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 applyargs)
+ (setq filepos nil)))
+
;; -env NAME=VALUE: An environment variable.
("-env"
(let ((var (pop args-left)))
diff --git a/lisp/startup.el b/lisp/startup.el
index 7f601668369..bb8da76bdf1 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -2531,10 +2531,11 @@ command-line-1
;; straight away upon any --directory/-L option.
splice
just-files ;; t if this follows the magic -- option.
+ applysym applyargs ;; function and arguments for --apply
;; This includes our standard options' long versions
;; and long versions of what's on command-switch-alist.
(longopts
- (append '("--funcall" "--load" "--insert" "--kill"
+ (append '("--funcall" "--apply" "--load" "--insert" "--kill"
"--dump-file" "--seccomp"
"--directory" "--eval" "--execute" "--no-splash"
"--find-file" "--visit" "--file" "--no-desktop")
@@ -2632,6 +2633,11 @@ command-line-1
(command-execute tem)
(funcall tem)))
+ ((member argi '("-y" "-apply"))
+ (setq inhibit-startup-screen t)
+ ;; Subsequent file args will be accumulated into applyargs.
+ (setq applysym (intern (or argval (pop command-line-args-left)))))
+
((member argi '("-eval" "-execute"))
(setq inhibit-startup-screen t)
(let* ((str-expr (or argval (pop command-line-args-left)))
@@ -2763,13 +2769,19 @@ command-line-1
;; screen for -nw?
(unless initial-window-system
(setq inhibit-startup-screen t))
- (funcall process-file-arg orig-argi)))))
+ (if applysym
+ (push orig-argi applyargs)
+ (funcall process-file-arg orig-argi))))))
;; In unusual circumstances, the execution of Lisp code due
;; to command-line options can cause the last visible frame
;; to be deleted. In this case, kill emacs to avoid an
;; abort later.
- (unless (frame-live-p (selected-frame)) (kill-emacs nil)))))))
+ (unless (frame-live-p (selected-frame)) (kill-emacs nil))))
+
+ ;; Call the function specified with --apply, if any.
+ (when applysym
+ (apply applysym (nreverse applyargs))))))
(when (eq initial-buffer-choice t)
;; When `initial-buffer-choice' equals t make sure that *scratch*
diff --git a/src/emacs.c b/src/emacs.c
index 80a013b68df..8de40936250 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -323,6 +323,8 @@ #define MAIN_PROGRAM
--file FILE visit FILE\n\
--find-file FILE visit FILE\n\
--funcall, -f FUNC call Emacs Lisp function FUNC with no arguments\n\
+--apply, -y FUNC call Emacs Lisp function FUNC, passing\n\
+ subsequent positional FILE arguments as strings\n\
--insert FILE insert contents of FILE into current buffer\n\
--kill exit without asking for confirmation\n\
--load, -l FILE load Emacs Lisp FILE using the load function\n\
@@ -2648,6 +2650,7 @@ main (int argc, char **argv)
option. In any case, this is entirely an internal option. */
{ "-scriptload", NULL, 0, 1 },
{ "-f", "--funcall", 0, 1 },
+ { "-y", "--apply", 0, 1 },
{ "-funcall", 0, 0, 1 },
{ "-eval", "--eval", 0, 1 },
{ "-execute", "--execute", 0, 1 },
--
2.41.0
next prev parent reply other threads:[~2023-09-13 12:57 UTC|newest]
Thread overview: 59+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-09-13 2:24 bug#65902: 29.0.92; emacsclient-mail.desktop fails due to complicated escaping sbaugh
2023-09-13 2:30 ` sbaugh
2023-09-13 3:46 ` Jim Porter
2023-09-13 8:00 ` Robert Pluim
2023-09-13 13:06 ` Eli Zaretskii
2023-09-13 14:22 ` Robert Pluim
2023-09-13 12:41 ` Eli Zaretskii
2023-09-13 12:57 ` sbaugh [this message]
2023-09-13 12:41 ` Eli Zaretskii
2023-09-13 13:01 ` sbaugh
2023-09-13 13:26 ` Eli Zaretskii
2023-09-16 13:30 ` Björn Bidar via Bug reports for GNU Emacs, the Swiss army knife of text editors
[not found] <fe2cc764-86c6-4840-80b7-8f3a3778b374@email.android.com>
2023-09-13 14:50 ` Eli Zaretskii
2023-09-13 15:01 ` Andreas Schwab
2023-09-13 15:23 ` Spencer Baugh
2023-09-13 16:19 ` Jim Porter
2023-09-13 19:13 ` Eli Zaretskii
2023-09-13 19:33 ` Jim Porter
2023-09-13 20:00 ` Spencer Baugh
2023-09-13 20:16 ` Jim Porter
2023-09-14 5:10 ` Eli Zaretskii
2023-09-14 11:03 ` sbaugh
2023-09-14 11:18 ` sbaugh
2023-09-14 11:35 ` sbaugh
2023-09-14 13:36 ` Eli Zaretskii
2023-09-14 14:04 ` Spencer Baugh
2023-09-14 14:31 ` Eli Zaretskii
2023-09-14 19:16 ` Jim Porter
2023-09-15 5:33 ` Eli Zaretskii
2023-09-16 13:43 ` Björn Bidar via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-09-16 14:02 ` Eli Zaretskii
2023-09-16 15:54 ` Björn Bidar via Bug reports for GNU Emacs, the Swiss army knife of text editors
[not found] <80d8aeb0-c9f1-410f-b83d-60f83ca5b3af@email.android.com>
2023-09-14 14:57 ` Eli Zaretskii
2023-09-14 15:10 ` Spencer Baugh
2023-09-15 6:29 ` Eli Zaretskii
2023-09-22 1:36 ` sbaugh
2023-09-22 6:36 ` Eli Zaretskii
2023-09-23 20:24 ` sbaugh
2023-09-24 5:18 ` Eli Zaretskii
2023-09-24 14:20 ` sbaugh
2023-10-21 15:20 ` sbaugh
2023-10-22 5:27 ` Eli Zaretskii
2023-10-22 14:15 ` sbaugh
2023-10-22 16:09 ` Andreas Schwab
2023-10-22 19:53 ` sbaugh
2023-10-23 16:38 ` Andreas Schwab
2023-10-23 16:52 ` Jim Porter
2023-10-24 16:27 ` sbaugh
2023-10-29 12:20 ` Eli Zaretskii
2023-10-22 5:39 ` Jim Porter
2023-09-22 7:05 ` Stefan Kangas
2023-09-22 7:14 ` Eli Zaretskii
2023-09-22 9:29 ` Andreas Schwab
2023-09-22 11:32 ` Eli Zaretskii
2023-09-22 12:37 ` Andreas Schwab
2023-09-22 12:56 ` Eli Zaretskii
2023-09-22 13:23 ` Andreas Schwab
2023-09-22 14:51 ` Eli Zaretskii
2023-09-22 14:52 ` Andreas Schwab
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87r0n2tem4.fsf@catern.com \
--to=sbaugh@catern.com \
--cc=65902@debbugs.gnu.org \
--cc=jporterbugs@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).