From 4b0b27d0018f040bda6a2ec885fa54c666d9c083 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Tue, 10 Feb 2015 18:26:23 +0200 Subject: [PATCH] Fix invocation of commands whose file name includes extension (Bug#19817) nt/cmdproxy.c (get_next_token): Don't make backslashes disappear without a trace when they are not followed by a quote. (search_dir): Support searching programs whose file name already has an arbitrary extension. (main): When passing a command line to the shell, use cmd.exe rules for quoting command-line tail. --- nt/ChangeLog | 9 ++++++++ nt/cmdproxy.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 6 deletions(-) diff --git a/nt/ChangeLog b/nt/ChangeLog index cac8e054068..d1f953f9ac5 100644 --- a/nt/ChangeLog +++ b/nt/ChangeLog @@ -1,3 +1,12 @@ +2015-02-10 Eli Zaretskii + + * cmdproxy.c (get_next_token): Don't make backslashes disappear + without a trace when they are not followed by a quote. + (search_dir): Support searching programs whose file name already + has an arbitrary extension. (Bug#19817) + (main): When passing a command line to the shell, use cmd.exe + rules for quoting command-line tail. + 2014-11-17 Oscar Fuentes * inc/ms-w32.h: Define MINGW_W64. diff --git a/nt/cmdproxy.c b/nt/cmdproxy.c index 7dbb529a6c9..ce5815291df 100644 --- a/nt/cmdproxy.c +++ b/nt/cmdproxy.c @@ -135,7 +135,10 @@ skip_nonspace (const char *str) return str; } -int escape_char = '\\'; +/* This value is never changed by the code. We keep the code that + supports also the value of '"', but let's allow the compiler to + optimize it out, until someone actually uses that. */ +const int escape_char = '\\'; /* Get next token from input, advancing pointer. */ int @@ -196,11 +199,31 @@ get_next_token (char * buf, const char ** pSrc) /* End of string, but no ending quote found. We might want to flag this as an error, but for now will consider the end as the end of the token. */ + if (escape_char == '\\') + { + /* Output literal backslashes. Note that if the + token ends with an unpaired backslash, we eat it + up here. But since this case invokes undefined + behavior anyway, it's okay. */ + while (escape_char_run > 1) + { + *o++ = escape_char; + escape_char_run -= 2; + } + } *o = '\0'; break; } else { + if (escape_char == '\\') + { + /* Output literal backslashes. Note that we don't + treat a backslash as an escape character here, + since it doesn't preceed a quote. */ + for ( ; escape_char_run > 0; escape_char_run--) + *o++ = escape_char; + } *o++ = *p++; } } @@ -229,13 +252,44 @@ search_dir (const char *dir, const char *exec, int bufsize, char *buffer) int n_exts = sizeof (exts) / sizeof (char *); char *dummy; int i, rc; + const char *pext = strrchr (exec, '\\'); + + /* Does EXEC already include an extension? */ + if (!pext) + pext = exec; + pext = strchr (pext, '.'); /* Search the directory for the program. */ - for (i = 0; i < n_exts; i++) + if (pext) { - rc = SearchPath (dir, exec, exts[i], bufsize, buffer, &dummy); + /* SearchPath will not append an extension if the file already + has an extension, so we must append it ourselves. */ + char exec_ext[MAX_PATH], *p; + + p = strcpy (exec_ext, exec) + strlen (exec); + + /* Search first without any extension; if found, we are done. */ + rc = SearchPath (dir, exec_ext, NULL, bufsize, buffer, &dummy); if (rc > 0) return rc; + + /* Try the known extensions. */ + for (i = 0; i < n_exts; i++) + { + strcpy (p, exts[i]); + rc = SearchPath (dir, exec_ext, NULL, bufsize, buffer, &dummy); + if (rc > 0) + return rc; + } + } + else + { + for (i = 0; i < n_exts; i++) + { + rc = SearchPath (dir, exec, exts[i], bufsize, buffer, &dummy); + if (rc > 0) + return rc; + } } return 0; @@ -769,7 +823,7 @@ main (int argc, char ** argv) quotes, since they are illegal in path names). */ remlen = maxlen = - strlen (progname) + extra_arg_space + strlen (cmdline) + 16; + strlen (progname) + extra_arg_space + strlen (cmdline) + 16 + 2; buf = p = alloca (maxlen + 1); /* Quote progname in case it contains spaces. */ @@ -784,10 +838,16 @@ main (int argc, char ** argv) remlen = maxlen - (p - buf); } + /* Now that we know we will be invoking the shell, quote the + command line after the "/c" switch as the shell expects: + a single pair of quotes enclosing the entire command + tail, no matter whether quotes are used in the command + line, and how many of them are there. See the output of + "cmd /?" for how cmd.exe treats quotes. */ if (run_command_dot_com) - _snprintf (p, remlen, " /e:%d /c %s", envsize, cmdline); + _snprintf (p, remlen, " /e:%d /c \"%s\"", envsize, cmdline); else - _snprintf (p, remlen, " /c %s", cmdline); + _snprintf (p, remlen, " /c \"%s\"", cmdline); cmdline = buf; } else -- 2.11.4.GIT