Commit 8f91bf93 authored by Daniel Colascione's avatar Daniel Colascione

Improve Windows quoting robustness

parent 0c6b7b19
2011-04-26 Daniel Colascione <dan.colascione@gmail.com>
* subr.el (shell-quote-argument): Escape correctly under Windows.
2011-04-25 Stefan Monnier <monnier@iro.umontreal.ca>
* emulation/cua-base.el (cua-selection-mode): Make it toggle again.
......@@ -50,6 +54,7 @@
* net/network-stream.el (network-stream-open-starttls): Give host
parameter to `gnutls-negotiate'.
(gnutls-negotiate): Adjust `gnutls-negotiate' declaration.
* subr.el (shell-quote-argument): Escape correctly under Windows.
2011-04-24 Daniel Colascione <dan.colascione@gmail.com>
......
......@@ -2505,27 +2505,63 @@ Note: :data and :device are currently not supported on Windows."
(defun shell-quote-argument (argument)
"Quote ARGUMENT for passing as argument to an inferior shell."
(if (or (eq system-type 'ms-dos)
(and (eq system-type 'windows-nt) (w32-shell-dos-semantics)))
;; Quote using double quotes, but escape any existing quotes in
;; the argument with backslashes.
(let ((result "")
(start 0)
end)
(if (or (null (string-match "[^\"]" argument))
(< (match-end 0) (length argument)))
(while (string-match "[\"]" argument start)
(setq end (match-beginning 0)
result (concat result (substring argument start end)
"\\" (substring argument end (1+ end)))
start (1+ end))))
(concat "\"" result (substring argument start) "\""))
(cond
((eq system-type 'ms-dos)
;; Quote using double quotes, but escape any existing quotes in
;; the argument with backslashes.
(let ((result "")
(start 0)
end)
(if (or (null (string-match "[^\"]" argument))
(< (match-end 0) (length argument)))
(while (string-match "[\"]" argument start)
(setq end (match-beginning 0)
result (concat result (substring argument start end)
"\\" (substring argument end (1+ end)))
start (1+ end))))
(concat "\"" result (substring argument start) "\"")))
((and (eq system-type 'windows-nt) (w32-shell-dos-semantics))
;; First, quote argument so that CommandLineToArgvW will
;; understand it. See
;; http://msdn.microsoft.com/en-us/library/17w5ykft%28v=vs.85%29.aspx
;; After we perform that level of quoting, escape shell
;; metacharacters so that cmd won't mangle our argument. If the
;; argument contains no double quote characters, we can just
;; surround it with double quotes. Otherwise, we need to prefix
;; each shell metacharacter with a caret.
(setq argument
;; escape backslashes at end of string
(replace-regexp-in-string
"\\(\\\\*\\)$"
"\\1\\1"
;; escape backslashes and quotes in string body
(replace-regexp-in-string
"\\(\\\\*\\)\""
"\\1\\1\\\\\""
argument)))
(if (string-match "\"" argument)
(concat
"^\""
(replace-regexp-in-string
"\\([%!()\"<>&|^]\\)"
"^\\1"
argument)
"^\"")
(concat "\"" argument "\"")))
(t
(if (equal argument "")
"''"
;; Quote everything except POSIX filename characters.
;; This should be safe enough even for really weird shells.
(replace-regexp-in-string "\n" "'\n'"
(replace-regexp-in-string "[^-0-9a-zA-Z_./\n]" "\\\\\\&" argument)))))
(replace-regexp-in-string
"\n" "'\n'"
(replace-regexp-in-string "[^-0-9a-zA-Z_./\n]" "\\\\\\&" argument))))
))
(defun string-or-null-p (object)
"Return t if OBJECT is a string or nil.
......
2011-04-26 Daniel Colascione <dan.colascione@gmail.com>
* cmdproxy.c (try_dequote_cmdline): New function.
(main): Use it.
2011-04-24 Teodor Zlatanov <tzz@lifelogs.com>
* configure.bat: New options --without-gnutls and --lib, new build
......
......@@ -309,6 +309,74 @@ make_absolute (const char *prog)
return NULL;
}
/* Try to decode the given command line the way cmd would do it. On
success, return 1 with cmdline dequoted. Otherwise, when we've
found constructs only cmd can properly interpret, return 0 and
leave cmdline unchanged. */
int
try_dequote_cmdline (char* cmdline)
{
/* Dequoting can only subtract characters, so the length of the
original command line is a bound on the amount of scratch space
we need. This length, in turn, is bounded by the 32k
CreateProces limit. */
char * old_pos = cmdline;
char * new_cmdline = alloca (strlen(cmdline));
char * new_pos = new_cmdline;
char c;
enum {
NORMAL,
AFTER_CARET,
INSIDE_QUOTE
} state = NORMAL;
while ((c = *old_pos++))
{
switch (state)
{
case NORMAL:
switch(c)
{
case '"':
*new_pos++ = c;
state = INSIDE_QUOTE;
break;
case '^':
state = AFTER_CARET;
break;
case '<': case '>':
case '&': case '|':
case '(': case ')':
case '%': case '!':
/* We saw an unquoted shell metacharacter and we don't
understand it. Bail out. */
return 0;
default:
*new_pos++ = c;
break;
}
break;
case AFTER_CARET:
*new_pos++ = c;
state = NORMAL;
break;
case INSIDE_QUOTE:
*new_pos++ = c;
if (c == '"')
state = NORMAL;
break;
}
}
/* We were able to dequote the entire string. Copy our scratch
buffer on top of the original buffer and return success. */
memcpy (cmdline, new_cmdline, new_pos - new_cmdline);
cmdline[new_pos - new_cmdline] = '\0';
return 1;
}
/*****************************************************************/
#if 0
......@@ -574,30 +642,26 @@ main (int argc, char ** argv)
execute the command directly ourself. */
if (cmdline)
{
/* If no redirection or piping, and if program can be found, then
run program directly. Otherwise invoke a real shell. */
static char copout_chars[] = "|<>&";
if (strpbrk (cmdline, copout_chars) == NULL)
{
const char *args;
/* The program name is the first token of cmdline. Since
filenames cannot legally contain embedded quotes, the value
of escape_char doesn't matter. */
args = cmdline;
if (!get_next_token (path, &args))
fail ("error: no program name specified.\n");
canon_filename (path);
progname = make_absolute (path);
/* If we found the program, run it directly (if not found it
might be an internal shell command, so don't fail). */
if (progname != NULL)
need_shell = FALSE;
}
const char *args;
/* The program name is the first token of cmdline. Since
filenames cannot legally contain embedded quotes, the value
of escape_char doesn't matter. */
args = cmdline;
if (!get_next_token (path, &args))
fail ("error: no program name specified.\n");
canon_filename (path);
progname = make_absolute (path);
/* If we found the program and the rest of the command line does
not contain unquoted shell metacharacters, run the program
directly (if not found it might be an internal shell command,
so don't fail). */
if (progname != NULL && try_dequote_cmdline (cmdline))
need_shell = FALSE;
else
progname = NULL;
}
pass_to_shell:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment