From a91ff4f4b1d835cc2c723429c06ddcb357f807c8 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Sat, 25 Oct 2014 12:12:01 +0300 Subject: [PATCH] Fix bug #18745 with invoking Windows batch files with embedded whitespace. src/w32proc.c (create_child): If calling a quoted batch file, pass NULL for exe. nt/cmdproxy.c (batch_file_p): New function. (spawn): If calling a quoted batch file pass NULL for progname. test/automated/process-tests.el (process-test-quoted-batfile): New test. --- nt/ChangeLog | 6 ++++++ nt/cmdproxy.c | 29 +++++++++++++++++++++++++++++ src/ChangeLog | 5 +++++ src/w32proc.c | 10 ++++++++++ test/ChangeLog | 5 +++++ test/automated/process-tests.el | 22 ++++++++++++++++++++++ 6 files changed, 77 insertions(+) diff --git a/nt/ChangeLog b/nt/ChangeLog index 632c3739493..c4e01a38c21 100644 --- a/nt/ChangeLog +++ b/nt/ChangeLog @@ -1,3 +1,9 @@ +2014-10-22 Noam Postavsky + + * nt/cmdproxy.c (batch_file_p): New function. + (spawn): If calling a quoted batch file pass NULL for progname. + (Bug#18745) + 2014-10-20 Glenn Morris * Merge in all changes up to 24.4 release. diff --git a/nt/cmdproxy.c b/nt/cmdproxy.c index e48ca63a257..d8f7ae3c41e 100644 --- a/nt/cmdproxy.c +++ b/nt/cmdproxy.c @@ -220,6 +220,28 @@ get_next_token (char * buf, const char ** pSrc) return o - buf; } +/* Return TRUE if PROGNAME is a batch file. */ +BOOL +batch_file_p (const char *progname) +{ + const char *exts[] = {".bat", ".cmd"}; + int n_exts = sizeof (exts) / sizeof (char *); + int i; + + const char *ext = strrchr (progname, '.'); + + if (ext) + { + for (i = 0; i < n_exts; i++) + { + if (stricmp (ext, exts[i]) == 0) + return TRUE; + } + } + + return FALSE; +} + /* Search for EXEC file in DIR. If EXEC does not have an extension, DIR is searched for EXEC with the standard extensions appended. */ int @@ -470,6 +492,13 @@ spawn (const char *progname, char *cmdline, const char *dir, int *retcode) memset (&start, 0, sizeof (start)); start.cb = sizeof (start); + /* CreateProcess handles batch files as progname specially. This + special handling fails when both the batch file and arguments are + quoted. We pass NULL as progname to avoid the special + handling. */ + if (progname != NULL && cmdline[0] == '"' && batch_file_p (progname)) + progname = NULL; + if (CreateProcess (progname, cmdline, &sec_attrs, NULL, TRUE, 0, envblock, dir, &start, &child)) { diff --git a/src/ChangeLog b/src/ChangeLog index a1b70b63755..fb724fb583f 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,8 @@ +2014-10-25 Noam Postavsky + + * src/w32proc.c (create_child): If calling a quoted batch file, + pass NULL for exe. (Bug#18745) + 2014-10-24 Eli Zaretskii * bidi.c (bidi_resolve_explicit, bidi_find_bracket_pairs) diff --git a/src/w32proc.c b/src/w32proc.c index 38452917add..09e0c0530a4 100644 --- a/src/w32proc.c +++ b/src/w32proc.c @@ -1078,6 +1078,7 @@ create_child (char *exe, char *cmdline, char *env, int is_gui_app, DWORD flags; char dir[ MAX_PATH ]; char *p; + const char *ext; if (cp == NULL) emacs_abort (); @@ -1116,6 +1117,15 @@ create_child (char *exe, char *cmdline, char *env, int is_gui_app, if (*p == '/') *p = '\\'; + /* CreateProcess handles batch files as exe specially. This special + handling fails when both the batch file and arguments are quoted. + We pass NULL as exe to avoid the special handling. */ + if (exe && cmdline[0] == '"' && + (ext = strrchr (exe, '.')) && + (xstrcasecmp (ext, ".bat") == 0 + || xstrcasecmp (ext, ".cmd") == 0)) + exe = NULL; + flags = (!NILP (Vw32_start_process_share_console) ? CREATE_NEW_PROCESS_GROUP : CREATE_NEW_CONSOLE); diff --git a/test/ChangeLog b/test/ChangeLog index bb4b2fbdc49..fb19252cd34 100644 --- a/test/ChangeLog +++ b/test/ChangeLog @@ -1,3 +1,8 @@ +2014-10-22 Noam Postavsky + + * test/automated/process-tests.el (process-test-quoted-batfile): + New test. + 2014-10-20 Glenn Morris * Merge in all changes up to 24.4 release. diff --git a/test/automated/process-tests.el b/test/automated/process-tests.el index cd209bacaa6..6783a61d595 100644 --- a/test/automated/process-tests.el +++ b/test/automated/process-tests.el @@ -50,4 +50,26 @@ (should (process-test-sentinel-wait-function-working-p (lambda () (sit-for 0.01 t))))) +(when (eq system-type 'windows-nt) + (ert-deftest process-test-quoted-batfile () + "Check that Emacs hides CreateProcess deficiency (bug#18745)." + (let (batfile) + (unwind-protect + (progn + ;; CreateProcess will fail when both the bat file and 1st + ;; argument are quoted, so include spaces in both of those + ;; to force quoting. + (setq batfile (make-temp-file "echo args" nil ".bat")) + (with-temp-file batfile + (insert "@echo arg1 = %1, arg2 = %2\n")) + (with-temp-buffer + (call-process batfile nil '(t t) t "x &y") + (should (string= (buffer-string) "arg1 = \"x &y\", arg2 = \n"))) + (with-temp-buffer + (call-process-shell-command + (mapconcat #'shell-quote-argument (list batfile "x &y") " ") + nil '(t t) t) + (should (string= (buffer-string) "arg1 = \"x &y\", arg2 = \n")))) + (when batfile (delete-file batfile)))))) + (provide 'process-tests) -- 2.11.4.GIT