Fix invocation of Windows batch files with whitespace in their names.
authorEli Zaretskii <eliz@gnu.org>
Fri, 3 May 2013 13:24:24 +0000 (16:24 +0300)
committerEli Zaretskii <eliz@gnu.org>
Fri, 3 May 2013 13:24:24 +0000 (16:24 +0300)
 w32/subproc/sub_proc.c: Include makeint.h.  Remove a private
 incompatible prototype of xmalloc.
 (batch_file_with_spaces): New function, detects Windows batch
 files whose names include whitespace characters.
 (process_begin): If exec_name is a batch file with whitespace
 characters in its name, pass NULL as the first argument to
 CreateProcess.  This avoids weird failures due to buggy quoting by
 CreateProcess.  For the details, see the discussion starting at
 http://lists.gnu.org/archive/html/make-w32/2013-04/msg00008.html.

ChangeLog
w32/subproc/sub_proc.c

index 786ae4db8e7cbc44042db96428f1eb99b0461ff5..80035cf37e712c6644e8c50431c6a1421a662a4f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 2013-05-03  Eli Zaretskii  <eliz@gnu.org>
 
+       * w32/subproc/sub_proc.c: Include makeint.h.  Remove a private
+       incompatible prototype of xmalloc.
+       (batch_file_with_spaces): New function, detects Windows batch
+       files whose names include whitespace characters.
+       (process_begin): If exec_name is a batch file with whitespace
+       characters in its name, pass NULL as the first argument to
+       CreateProcess.  This avoids weird failures due to buggy quoting by
+       CreateProcess.  For the details, see the discussion starting at
+       http://lists.gnu.org/archive/html/make-w32/2013-04/msg00008.html.
+
        * load.c (load_object, load_file): Accept an additional argument
        DLP and return in it a pointer that can be used to unload the
        dynamic object.
index 2c3677731732960d33fba7285c2562ccd2bfd1d5..68329dfd1ad9f4a16b5514047f5875111c739c01 100644 (file)
@@ -23,17 +23,18 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  */
 #else
 # include <stdint.h>
 #endif
+#include <string.h>
 #include <process.h>  /* for msvc _beginthreadex, _endthreadex */
 #include <signal.h>
 #include <windows.h>
 
+#include "makeint.h"
 #include "sub_proc.h"
 #include "proc.h"
 #include "w32err.h"
 #include "debug.h"
 
 static char *make_command_line(char *shell_name, char *exec_path, char **argv);
-extern char *xmalloc (unsigned int);
 
 typedef struct sub_process_t {
        intptr_t sv_stdin[2];
@@ -540,6 +541,26 @@ find_file(const char *exec_path, const char *path_var,
        return INVALID_HANDLE_VALUE;
 }
 
+/*
+ * Return non-zero of FNAME specifies a batch file and its name
+ * includes embedded whitespace.
+ */
+
+static int
+batch_file_with_spaces(const char *fname)
+{
+       size_t fnlen = strlen(fname);
+
+       return (fnlen > 4
+               && (_strnicmp(fname + fnlen - 4, ".bat", 4) == 0
+                   || _strnicmp(fname + fnlen - 4, ".cmd", 4) == 0)
+               /* The set of characters in the 2nd arg to strpbrk
+                  should be the same one used by make_command_line
+                  below to decide whether an argv[] element needs
+                  quoting.  */
+               && strpbrk(fname, " \t") != NULL);
+}
+
 
 /*
  * Description:   Create the child process to be helped
@@ -570,6 +591,7 @@ process_begin(
        STARTUPINFO startInfo;
        PROCESS_INFORMATION procInfo;
        char *envblk=NULL;
+       int pass_null_exec_path = 0;
 
        /*
         *  Shell script detection...  if the exec_path starts with #! then
@@ -651,8 +673,28 @@ process_begin(
 
        if (file_not_found)
                command_line = make_command_line( shell_name, exec_path, argv);
-       else
+       else {
+               /* If exec_fname includes whitespace, CreateProcess
+                  behaves erratically and unreliably, and often fails
+                  if argv[0] also includes whitespace (and thus will
+                  be quoted by make_command_line below).  So in that
+                  case, we don't pass exec_fname as the 1st arg to
+                  CreateProcess, but instead replace argv[0] with
+                  exec_fname (to keep its leading directories and
+                  extension as found by find_file), and pass NULL to
+                  CreateProcess as its 1st arg.  This works around
+                  the bugs in CreateProcess, which are probably
+                  caused by its passing the command to cmd.exe with
+                  some incorrect quoting.  */
+               if (!shell_name
+                   && batch_file_with_spaces(exec_fname)
+                   && _stricmp(exec_path, argv[0]) == 0) {
+                       pass_null_exec_path = 1;
+                       free (argv[0]);
+                       argv[0] = xstrdup(exec_fname);
+               }
                command_line = make_command_line( shell_name, exec_fname, argv);
+       }
 
        if ( command_line == NULL ) {
                pproc->last_err = 0;
@@ -669,7 +711,7 @@ process_begin(
                }
        }
 
-       if ((shell_name) || (file_not_found)) {
+       if (shell_name || file_not_found || pass_null_exec_path) {
                exec_path = 0;  /* Search for the program in %Path% */
        } else {
                exec_path = exec_fname;