Improve Windows quoting robustness
This commit is contained in:
parent
0c6b7b19e5
commit
8f91bf9345
4 changed files with 148 additions and 38 deletions
|
@ -1,3 +1,7 @@
|
|||
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>
|
||||
|
||||
|
|
70
lisp/subr.el
70
lisp/subr.el
|
@ -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.
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
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
|
||||
|
|
106
nt/cmdproxy.c
106
nt/cmdproxy.c
|
@ -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. */
|
||||
const char *args;
|
||||
|
||||
static char copout_chars[] = "|<>&";
|
||||
/* 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");
|
||||
|
||||
if (strpbrk (cmdline, copout_chars) == NULL)
|
||||
{
|
||||
const char *args;
|
||||
canon_filename (path);
|
||||
progname = make_absolute (path);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
/* 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:
|
||||
|
|
Loading…
Add table
Reference in a new issue