diff options
| author | Reuben Thomas | 2016-12-01 15:21:57 +0000 |
|---|---|---|
| committer | Reuben Thomas | 2017-08-30 21:59:38 +0100 |
| commit | 98f01a13a3bf2a4db2dcc82a342ee017326de732 (patch) | |
| tree | 70aac99762c0ee95026ec72fc307304b6700d372 | |
| parent | dc313922d826b9f53cf1426ff36c8cc3f71d64c6 (diff) | |
| download | emacs-98f01a13a3bf2a4db2dcc82a342ee017326de732.tar.gz emacs-98f01a13a3bf2a4db2dcc82a342ee017326de732.zip | |
Add support for arguments in emacsclient's ALTERNATE_EDITOR (Bug #25082)
* lib-src/emacsclient.c (fail): Parse ALTERNATE_EDITOR, or
corresponding command-line argument, into quote- or space-separated
tokens. If a token starts with a quote, then it naturally is expected
to end with a quote; escaping is not supported. This is enough to cope
with the typical case of requiring the initial path to be quoted,
common on Windows where it may contain spaces.
* etc/NEWS: Document.
* doc/emacs/misc.texi: Likewise.
* doc/man/emacsclient.1: Tweak to remove the implication that only an
editor can be specified (the manual already mentions a “command”).
Fix a small error where “EDITOR” is referred to rather than
“ALTERNATE_EDITOR”.
* test/lib-src/emacsclient-tests.el: Add tests.
| -rw-r--r-- | doc/emacs/misc.texi | 4 | ||||
| -rw-r--r-- | doc/man/emacsclient.1 | 6 | ||||
| -rw-r--r-- | etc/NEWS | 7 | ||||
| -rw-r--r-- | lib-src/emacsclient.c | 84 | ||||
| -rw-r--r-- | test/lib-src/emacsclient-tests.el | 50 |
5 files changed, 129 insertions, 22 deletions
diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi index 73a6bae767a..7602fbb7454 100644 --- a/doc/emacs/misc.texi +++ b/doc/emacs/misc.texi | |||
| @@ -1821,8 +1821,10 @@ listed below: | |||
| 1821 | @table @samp | 1821 | @table @samp |
| 1822 | @item -a @var{command} | 1822 | @item -a @var{command} |
| 1823 | @itemx --alternate-editor=@var{command} | 1823 | @itemx --alternate-editor=@var{command} |
| 1824 | Specify a command to run if @code{emacsclient} fails to contact Emacs. | 1824 | Specify a shell command to run if @code{emacsclient} fails to contact Emacs. |
| 1825 | This is useful when running @code{emacsclient} in a script. | 1825 | This is useful when running @code{emacsclient} in a script. |
| 1826 | The command may include arguments, which may be quoted "like this". | ||
| 1827 | Currently, escaping of quotes is not supported. | ||
| 1826 | 1828 | ||
| 1827 | As a special exception, if @var{command} is the empty string, then | 1829 | As a special exception, if @var{command} is the empty string, then |
| 1828 | @code{emacsclient} starts Emacs in daemon mode (as @command{emacs | 1830 | @code{emacsclient} starts Emacs in daemon mode (as @command{emacs |
diff --git a/doc/man/emacsclient.1 b/doc/man/emacsclient.1 index 010eeba19c1..daaacab7f3e 100644 --- a/doc/man/emacsclient.1 +++ b/doc/man/emacsclient.1 | |||
| @@ -62,10 +62,10 @@ A missing | |||
| 62 | is treated as column 1. | 62 | is treated as column 1. |
| 63 | This option applies only to the next file specified. | 63 | This option applies only to the next file specified. |
| 64 | .TP | 64 | .TP |
| 65 | .B \-a, \-\-alternate-editor=EDITOR | 65 | .B \-a, \-\-alternate-editor=COMMAND |
| 66 | if the Emacs server is not running, run the specified editor instead. | 66 | if the Emacs server is not running, run the specified shell command instead. |
| 67 | This can also be specified via the ALTERNATE_EDITOR environment variable. | 67 | This can also be specified via the ALTERNATE_EDITOR environment variable. |
| 68 | If the value of EDITOR is the empty string, run "emacs \-\-daemon" to | 68 | If the value of ALTERNATE_EDITOR is the empty string, run "emacs \-\-daemon" to |
| 69 | start Emacs in daemon mode, and try to connect to it. | 69 | start Emacs in daemon mode, and try to connect to it. |
| 70 | .TP | 70 | .TP |
| 71 | .B -c, \-\-create-frame | 71 | .B -c, \-\-create-frame |
| @@ -485,6 +485,13 @@ Linum mode and all similar packages are henceforth becoming obsolete. | |||
| 485 | Users and developers are encouraged to switch to this new feature | 485 | Users and developers are encouraged to switch to this new feature |
| 486 | instead. | 486 | instead. |
| 487 | 487 | ||
| 488 | +++ | ||
| 489 | ** emacsclient now accepts command-line options in ALTERNATE_EDITOR | ||
| 490 | and --alternate-editor. For example, ALTERNATE_EDITOR="emacs -Q -nw". | ||
| 491 | Arguments may be quoted "like this", so that for example an absolute | ||
| 492 | path containing a space may be specified; quote escaping is not | ||
| 493 | supported. | ||
| 494 | |||
| 488 | 495 | ||
| 489 | * Editing Changes in Emacs 26.1 | 496 | * Editing Changes in Emacs 26.1 |
| 490 | 497 | ||
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c index f1d4e8976da..5e181ccacb1 100644 --- a/lib-src/emacsclient.c +++ b/lib-src/emacsclient.c | |||
| @@ -110,6 +110,9 @@ char *w32_getenv (const char *); | |||
| 110 | /* Name used to invoke this program. */ | 110 | /* Name used to invoke this program. */ |
| 111 | const char *progname; | 111 | const char *progname; |
| 112 | 112 | ||
| 113 | /* The first argument to main. */ | ||
| 114 | int main_argc; | ||
| 115 | |||
| 113 | /* The second argument to main. */ | 116 | /* The second argument to main. */ |
| 114 | char **main_argv; | 117 | char **main_argv; |
| 115 | 118 | ||
| @@ -201,6 +204,35 @@ xmalloc (size_t size) | |||
| 201 | return result; | 204 | return result; |
| 202 | } | 205 | } |
| 203 | 206 | ||
| 207 | /* Like realloc but get fatal error if memory is exhausted. */ | ||
| 208 | |||
| 209 | static void * | ||
| 210 | xrealloc (void *ptr, size_t size) | ||
| 211 | { | ||
| 212 | void *result = realloc (ptr, size); | ||
| 213 | if (result == NULL) | ||
| 214 | { | ||
| 215 | perror ("realloc"); | ||
| 216 | exit (EXIT_FAILURE); | ||
| 217 | } | ||
| 218 | return result; | ||
| 219 | } | ||
| 220 | |||
| 221 | /* Like strdup but get a fatal error if memory is exhausted. */ | ||
| 222 | char *xstrdup (const char *); | ||
| 223 | |||
| 224 | char * | ||
| 225 | xstrdup (const char *s) | ||
| 226 | { | ||
| 227 | char *result = strdup (s); | ||
| 228 | if (result == NULL) | ||
| 229 | { | ||
| 230 | perror ("strdup"); | ||
| 231 | exit (EXIT_FAILURE); | ||
| 232 | } | ||
| 233 | return result; | ||
| 234 | } | ||
| 235 | |||
| 204 | /* From sysdep.c */ | 236 | /* From sysdep.c */ |
| 205 | #if !defined (HAVE_GET_CURRENT_DIR_NAME) || defined (BROKEN_GET_CURRENT_DIR_NAME) | 237 | #if !defined (HAVE_GET_CURRENT_DIR_NAME) || defined (BROKEN_GET_CURRENT_DIR_NAME) |
| 206 | 238 | ||
| @@ -264,21 +296,6 @@ get_current_dir_name (void) | |||
| 264 | 296 | ||
| 265 | #ifdef WINDOWSNT | 297 | #ifdef WINDOWSNT |
| 266 | 298 | ||
| 267 | /* Like strdup but get a fatal error if memory is exhausted. */ | ||
| 268 | char *xstrdup (const char *); | ||
| 269 | |||
| 270 | char * | ||
| 271 | xstrdup (const char *s) | ||
| 272 | { | ||
| 273 | char *result = strdup (s); | ||
| 274 | if (result == NULL) | ||
| 275 | { | ||
| 276 | perror ("strdup"); | ||
| 277 | exit (EXIT_FAILURE); | ||
| 278 | } | ||
| 279 | return result; | ||
| 280 | } | ||
| 281 | |||
| 282 | #define REG_ROOT "SOFTWARE\\GNU\\Emacs" | 299 | #define REG_ROOT "SOFTWARE\\GNU\\Emacs" |
| 283 | 300 | ||
| 284 | char *w32_get_resource (HKEY, const char *, LPDWORD); | 301 | char *w32_get_resource (HKEY, const char *, LPDWORD); |
| @@ -673,7 +690,7 @@ Report bugs with M-x report-emacs-bug.\n"); | |||
| 673 | } | 690 | } |
| 674 | 691 | ||
| 675 | /* Try to run a different command, or --if no alternate editor is | 692 | /* Try to run a different command, or --if no alternate editor is |
| 676 | defined-- exit with an errorcode. | 693 | defined-- exit with an error code. |
| 677 | Uses argv, but gets it from the global variable main_argv. */ | 694 | Uses argv, but gets it from the global variable main_argv. */ |
| 678 | 695 | ||
| 679 | static _Noreturn void | 696 | static _Noreturn void |
| @@ -681,9 +698,38 @@ fail (void) | |||
| 681 | { | 698 | { |
| 682 | if (alternate_editor) | 699 | if (alternate_editor) |
| 683 | { | 700 | { |
| 684 | int i = optind - 1; | 701 | size_t extra_args_size = (main_argc - optind + 1) * sizeof (char *); |
| 702 | size_t new_argv_size = extra_args_size; | ||
| 703 | char **new_argv = NULL; | ||
| 704 | char *s = xstrdup (alternate_editor); | ||
| 705 | unsigned toks = 0; | ||
| 706 | |||
| 707 | /* Unpack alternate_editor's space-separated tokens into new_argv. */ | ||
| 708 | for (char *tok = s; tok != NULL && *tok != '\0';) | ||
| 709 | { | ||
| 710 | /* Allocate new token. */ | ||
| 711 | ++toks; | ||
| 712 | new_argv = xrealloc (new_argv, new_argv_size + toks * sizeof (char *)); | ||
| 713 | |||
| 714 | /* Skip leading delimiters, and set separator, skipping any | ||
| 715 | opening quote. */ | ||
| 716 | size_t skip = strspn (tok, " \""); | ||
| 717 | tok += skip; | ||
| 718 | char sep = (skip > 0 && tok[-1] == '"') ? '"' : ' '; | ||
| 719 | |||
| 720 | /* Record start of token. */ | ||
| 721 | new_argv[toks - 1] = tok; | ||
| 722 | |||
| 723 | /* Find end of token and overwrite it with NUL. */ | ||
| 724 | tok = strchr (tok, sep); | ||
| 725 | if (tok != NULL) | ||
| 726 | *tok++ = '\0'; | ||
| 727 | } | ||
| 728 | |||
| 729 | /* Append main_argv arguments to new_argv. */ | ||
| 730 | memcpy (&new_argv[toks], main_argv + optind, extra_args_size); | ||
| 685 | 731 | ||
| 686 | execvp (alternate_editor, main_argv + i); | 732 | execvp (*new_argv, new_argv); |
| 687 | message (true, "%s: error executing alternate editor \"%s\"\n", | 733 | message (true, "%s: error executing alternate editor \"%s\"\n", |
| 688 | progname, alternate_editor); | 734 | progname, alternate_editor); |
| 689 | } | 735 | } |
| @@ -696,6 +742,7 @@ fail (void) | |||
| 696 | int | 742 | int |
| 697 | main (int argc, char **argv) | 743 | main (int argc, char **argv) |
| 698 | { | 744 | { |
| 745 | main_argc = argc; | ||
| 699 | main_argv = argv; | 746 | main_argv = argv; |
| 700 | progname = argv[0]; | 747 | progname = argv[0]; |
| 701 | message (true, "%s: Sorry, the Emacs server is supported only\n" | 748 | message (true, "%s: Sorry, the Emacs server is supported only\n" |
| @@ -1629,6 +1676,7 @@ main (int argc, char **argv) | |||
| 1629 | int start_daemon_if_needed; | 1676 | int start_daemon_if_needed; |
| 1630 | int exit_status = EXIT_SUCCESS; | 1677 | int exit_status = EXIT_SUCCESS; |
| 1631 | 1678 | ||
| 1679 | main_argc = argc; | ||
| 1632 | main_argv = argv; | 1680 | main_argv = argv; |
| 1633 | progname = argv[0]; | 1681 | progname = argv[0]; |
| 1634 | 1682 | ||
diff --git a/test/lib-src/emacsclient-tests.el b/test/lib-src/emacsclient-tests.el new file mode 100644 index 00000000000..ea757f69144 --- /dev/null +++ b/test/lib-src/emacsclient-tests.el | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | ;;; emacsclient-tests.el --- Test emacsclient | ||
| 2 | |||
| 3 | ;; Copyright (C) 2016 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; This program is free software; you can redistribute it and/or modify | ||
| 6 | ;; it under the terms of the GNU General Public License as published by | ||
| 7 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 8 | ;; (at your option) any later version. | ||
| 9 | |||
| 10 | ;; This program is distributed in the hope that it will be useful, | ||
| 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | ;; GNU General Public License for more details. | ||
| 14 | |||
| 15 | ;; You should have received a copy of the GNU General Public License | ||
| 16 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | |||
| 18 | ;;; Commentary: | ||
| 19 | |||
| 20 | ;; | ||
| 21 | |||
| 22 | ;;; Code: | ||
| 23 | |||
| 24 | (require 'ert) | ||
| 25 | |||
| 26 | (defconst emacsclient-test-emacs | ||
| 27 | (expand-file-name "emacsclient" (concat | ||
| 28 | (file-name-directory | ||
| 29 | (directory-file-name | ||
| 30 | (file-name-directory invocation-directory))) | ||
| 31 | "lib-src")) | ||
| 32 | "Path to emacsclient binary in build tree.") | ||
| 33 | |||
| 34 | (ert-deftest emacsclient-test-alternate-editor-allows-arguments () | ||
| 35 | (let (process-environment process-environment) | ||
| 36 | (setenv "ALTERNATE_EDITOR" (concat | ||
| 37 | (expand-file-name invocation-name invocation-directory) | ||
| 38 | " --batch")) | ||
| 39 | (should (= 0 (call-process emacsclient-test-emacs nil nil nil "foo"))))) | ||
| 40 | |||
| 41 | (ert-deftest emacsclient-test-alternate-editor-allows-quotes () | ||
| 42 | (let (process-environment process-environment) | ||
| 43 | (setenv "ALTERNATE_EDITOR" (concat | ||
| 44 | "\"" | ||
| 45 | (expand-file-name invocation-name invocation-directory) | ||
| 46 | "\"" " --batch")) | ||
| 47 | (should (= 0 (call-process emacsclient-test-emacs nil nil nil "foo"))))) | ||
| 48 | |||
| 49 | (provide 'emacsclient-tests) | ||
| 50 | ;;; emacsclient-tests.el ends here | ||