To:
[email protected]
Subject: Patch 7.2a.013
Fcc: outbox
From: Bram Moolenaar <
[email protected]>
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
------------
Patch 7.2a.013
Problem: shellescape() does not escape "%" and "#" characters.
Solution: Add find_cmdline_var() and use it when the second argument to
shellescape() is non-zero.
Files: runtime/doc/eval.txt, src/eval.c, src/ex_docmd.c,
src/proto/ex_docmd.pro, src/proto/misc2.pro, src/misc2.c
*** ../vim-7.2a.012/runtime/doc/eval.txt Tue Jun 24 23:55:33 2008
--- runtime/doc/eval.txt Thu Jul 3 18:03:08 2008
***************
*** 1856,1862 ****
settabwinvar( {tabnr}, {winnr}, {varname}, {val}) set {varname} in window
{winnr} in tab page {tabnr} to {val}
setwinvar( {nr}, {varname}, {val}) set {varname} in window {nr} to {val}
! shellescape( {string}) String escape {string} for use as shell
command argument
simplify( {filename}) String simplify filename as much as possible
sin( {expr}) Float sine of {expr}
--- 1856,1863 ----
settabwinvar( {tabnr}, {winnr}, {varname}, {val}) set {varname} in window
{winnr} in tab page {tabnr} to {val}
setwinvar( {nr}, {varname}, {val}) set {varname} in window {nr} to {val}
! shellescape( {string} [, {special}])
! String escape {string} for use as shell
command argument
simplify( {filename}) String simplify filename as much as possible
sin( {expr}) Float sine of {expr}
***************
*** 4018,4027 ****
<
*mode()*
mode([expr]) Return a string that indicates the current mode.
! If [expr] is supplied and it evaluates to a non-zero number or
! a non-empty string, then the full mode is returned, otherwise
! only the first letter is returned. Note that " " and "0" are
! also non-empty strings.
n Normal
no Operator-pending
--- 4021,4030 ----
<
*mode()*
mode([expr]) Return a string that indicates the current mode.
! If [expr] is supplied and it evaluates to a non-zero Number or
! a non-empty String (|non-zero-arg|), then the full mode is
! returned, otherwise only the first letter is returned. Note
! that " " and "0" are also non-empty strings.
n Normal
no Operator-pending
***************
*** 4941,4959 ****
:call setwinvar(1, "&list", 0)
:call setwinvar(2, "myvar", "foobar")
! shellescape({string}) *shellescape()*
Escape {string} for use as shell command argument.
On MS-Windows and MS-DOS, when 'shellslash' is not set, it
! will enclose {string} double quotes and double all double
quotes within {string}.
For other systems, it will enclose {string} in single quotes
and replace all "'" with "'\''".
! Example: >
! :echo shellescape('c:\program files\vim')
! < results in:
! "c:\program files\vim" ~
! Example usage: >
! :call system("chmod +x -- " . shellescape(expand("%")))
simplify({filename}) *simplify()*
--- 4946,4968 ----
:call setwinvar(1, "&list", 0)
:call setwinvar(2, "myvar", "foobar")
! shellescape({string} [, {special}]) *shellescape()*
Escape {string} for use as shell command argument.
On MS-Windows and MS-DOS, when 'shellslash' is not set, it
! will enclose {string} in double quotes and double all double
quotes within {string}.
For other systems, it will enclose {string} in single quotes
and replace all "'" with "'\''".
! When the {special} argument is present and it's a non-zero
! Number or a non-empty String (|non-zero-arg|), then special
! items such as "%", "#" and "<cword>" will be preceded by a
! backslash. This backslash will be removed again by the |:!|
! command.
! Example of use with a |:!| command: >
! :exe '!dir ' . shellescape(expand('<cfile>'), 1)
! < This results in a directory listing for the file under the
! cursor. Example of use with |system()|: >
! :call system("chmod +w -- " . shellescape(expand("%")))
simplify({filename}) *simplify()*
***************
*** 5333,5345 ****
passed as stdin to the command. The string is written as-is,
you need to take care of using the correct line separators
yourself. Pipes are not used.
! Note: newlines in {expr} may cause the command to fail. The
! characters in 'shellquote' and 'shellxquote' may also cause
! trouble.
This is not to be used for interactive commands.
- The result is a String. Example: >
! :let files = system("ls")
< To make the result more system-independent, the shell output
is filtered to replace <CR> with <NL> for Macintosh, and
--- 5343,5356 ----
passed as stdin to the command. The string is written as-is,
you need to take care of using the correct line separators
yourself. Pipes are not used.
! Note: Use |shellescape()| to escape special characters in a
! command argument. Newlines in {expr} may cause the command to
! fail. The characters in 'shellquote' and 'shellxquote' may
! also cause trouble.
This is not to be used for interactive commands.
! The result is a String. Example: >
! :let files = system("ls " . shellescape(expand('%:h')))
< To make the result more system-independent, the shell output
is filtered to replace <CR> with <NL> for Macintosh, and
***************
*** 5559,5569 ****
Visual mode that was used.
If Visual mode is active, use |mode()| to get the Visual mode
(e.g., in a |:vmap|).
!
! If [expr] is supplied and it evaluates to a non-zero number or
! a non-empty string, then the Visual mode will be cleared and
the old value is returned. Note that " " and "0" are also
! non-empty strings, thus cause the mode to be cleared.
*winbufnr()*
winbufnr({nr}) The result is a Number, which is the number of the buffer
--- 5570,5582 ----
Visual mode that was used.
If Visual mode is active, use |mode()| to get the Visual mode
(e.g., in a |:vmap|).
! *non-zero-arg*
! If [expr] is supplied and it evaluates to a non-zero Number or
! a non-empty String, then the Visual mode will be cleared and
the old value is returned. Note that " " and "0" are also
! non-empty strings, thus cause the mode to be cleared. A List,
! Dictionary or Float is not a Number or String, thus does not
! cause the mode to be cleared.
*winbufnr()*
winbufnr({nr}) The result is a Number, which is the number of the buffer
***************
*** 6738,6745 ****
Be careful to correctly escape special characters in
file names. The |fnameescape()| function can be used
! for this. Example: >
:execute "e " . fnameescape(filename)
<
Note: The executed string may be any command-line, but
you cannot start or end a "while", "for" or "if"
--- 6751,6760 ----
Be careful to correctly escape special characters in
file names. The |fnameescape()| function can be used
! for Vim commands, |shellescape()| for |:!| commands.
! Examples: >
:execute "e " . fnameescape(filename)
+ :execute "!ls " . shellescape(expand('%:h'), 1)
<
Note: The executed string may be any command-line, but
you cannot start or end a "while", "for" or "if"
*** ../vim-7.2a.012/src/eval.c Sat Jun 28 15:09:03 2008
--- src/eval.c Thu Jul 3 18:12:41 2008
***************
*** 462,467 ****
--- 462,468 ----
static int get_func_tv __ARGS((char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict));
static int call_func __ARGS((char_u *name, int len, typval_T *rettv, int argcount, typval_T *argvars, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict));
static void emsg_funcname __ARGS((char *ermsg, char_u *name));
+ static int non_zero_arg __ARGS((typval_T *argvars));
#ifdef FEAT_FLOAT
static void f_abs __ARGS((typval_T *argvars, typval_T *rettv));
***************
*** 7611,7617 ****
{"setreg", 2, 3, f_setreg},
{"settabwinvar", 4, 4, f_settabwinvar},
{"setwinvar", 3, 3, f_setwinvar},
! {"shellescape", 1, 1, f_shellescape},
{"simplify", 1, 1, f_simplify},
#ifdef FEAT_FLOAT
{"sin", 1, 1, f_sin},
--- 7612,7618 ----
{"setreg", 2, 3, f_setreg},
{"settabwinvar", 4, 4, f_settabwinvar},
{"setwinvar", 3, 3, f_setwinvar},
! {"shellescape", 1, 2, f_shellescape},
{"simplify", 1, 1, f_simplify},
#ifdef FEAT_FLOAT
{"sin", 1, 1, f_sin},
***************
*** 8094,8099 ****
--- 8095,8114 ----
vim_free(p);
}
+ /*
+ * Return TRUE for a non-zero Number and a non-empty String.
+ */
+ static int
+ non_zero_arg(argvars)
+ typval_T *argvars;
+ {
+ return ((argvars[0].v_type == VAR_NUMBER
+ && argvars[0].vval.v_number != 0)
+ || (argvars[0].v_type == VAR_STRING
+ && argvars[0].vval.v_string != NULL
+ && *argvars[0].vval.v_string != NUL));
+ }
+
/*********************************************
* Implementation of the built-in functions
*/
***************
*** 13480,13489 ****
buf[1] = 'o';
}
! /* A zero number or empty string argument: return only major mode. */
! if (!(argvars[0].v_type == VAR_NUMBER && argvars[0].vval.v_number != 0)
! && !(argvars[0].v_type == VAR_STRING
! && *get_tv_string(&argvars[0]) != NUL))
buf[1] = NUL;
rettv->vval.v_string = vim_strsave(buf);
--- 13495,13503 ----
buf[1] = 'o';
}
! /* Clear out the minor mode when the argument is not a non-zero number or
! * non-empty string. */
! if (!non_zero_arg(&argvars[0]))
buf[1] = NUL;
rettv->vval.v_string = vim_strsave(buf);
***************
*** 15684,15690 ****
typval_T *argvars;
typval_T *rettv;
{
! rettv->vval.v_string = vim_strsave_shellescape(get_tv_string(&argvars[0]));
rettv->v_type = VAR_STRING;
}
--- 15698,15705 ----
typval_T *argvars;
typval_T *rettv;
{
! rettv->vval.v_string = vim_strsave_shellescape(
! get_tv_string(&argvars[0]), non_zero_arg(&argvars[1]));
rettv->v_type = VAR_STRING;
}
***************
*** 17273,17281 ****
rettv->vval.v_string = vim_strsave(str);
/* A non-zero number or non-empty string argument: reset mode. */
! if ((argvars[0].v_type == VAR_NUMBER && argvars[0].vval.v_number != 0)
! || (argvars[0].v_type == VAR_STRING
! && *get_tv_string(&argvars[0]) != NUL))
curbuf->b_visual_mode_eval = NUL;
#else
rettv->vval.v_number = 0; /* return anything, it won't work anyway */
--- 17288,17294 ----
rettv->vval.v_string = vim_strsave(str);
/* A non-zero number or non-empty string argument: reset mode. */
! if (non_zero_arg(&argvars[0]))
curbuf->b_visual_mode_eval = NUL;
#else
rettv->vval.v_number = 0; /* return anything, it won't work anyway */
*** ../vim-7.2a.012/src/ex_docmd.c Tue Jun 24 22:28:02 2008
--- src/ex_docmd.c Tue Jul 1 22:23:25 2008
***************
*** 7059,7066 ****
# ifdef FEAT_QUICKFIX
/* A ":split" in the quickfix window works like ":new". Don't want two
! * quickfix windows. */
! if (bt_quickfix(curbuf))
{
if (eap->cmdidx == CMD_split)
eap->cmdidx = CMD_new;
--- 7059,7066 ----
# ifdef FEAT_QUICKFIX
/* A ":split" in the quickfix window works like ":new". Don't want two
! * quickfix windows. But it's OK when doing ":tab split". */
! if (bt_quickfix(curbuf) && cmdmod.tab == 0)
{
if (eap->cmdidx == CMD_split)
eap->cmdidx = CMD_new;
***************
*** 9321,9326 ****
--- 9321,9378 ----
}
/*
+ * Check "str" for starting with a special cmdline variable.
+ * If found return one of the SPEC_ values and set "*usedlen" to the length of
+ * the variable. Otherwise return -1 and "*usedlen" is unchanged.
+ */
+ int
+ find_cmdline_var(src, usedlen)
+ char_u *src;
+ int *usedlen;
+ {
+ int len;
+ int i;
+ static char *(spec_str[]) = {
+ "%",
+ #define SPEC_PERC 0
+ "#",
+ #define SPEC_HASH 1
+ "<cword>", /* cursor word */
+ #define SPEC_CWORD 2
+ "<cWORD>", /* cursor WORD */
+ #define SPEC_CCWORD 3
+ "<cfile>", /* cursor path name */
+ #define SPEC_CFILE 4
+ "<sfile>", /* ":so" file name */
+ #define SPEC_SFILE 5
+ #ifdef FEAT_AUTOCMD
+ "<afile>", /* autocommand file name */
+ # define SPEC_AFILE 6
+ "<abuf>", /* autocommand buffer number */
+ # define SPEC_ABUF 7
+ "<amatch>", /* autocommand match name */
+ # define SPEC_AMATCH 8
+ #endif
+ #ifdef FEAT_CLIENTSERVER
+ "<client>"
+ # define SPEC_CLIENT 9
+ #endif
+ };
+ #define SPEC_COUNT (sizeof(spec_str) / sizeof(char *))
+
+ for (i = 0; i < SPEC_COUNT; ++i)
+ {
+ len = (int)STRLEN(spec_str[i]);
+ if (STRNCMP(src, spec_str[i], len) == 0)
+ {
+ *usedlen = len;
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /*
* Evaluate cmdline variables.
*
* change '%' to curbuf->b_ffname
***************
*** 9360,9393 ****
#ifdef FEAT_MODIFY_FNAME
int skip_mod = FALSE;
#endif
- static char *(spec_str[]) =
- {
- "%",
- #define SPEC_PERC 0
- "#",
- #define SPEC_HASH 1
- "<cword>", /* cursor word */
- #define SPEC_CWORD 2
- "<cWORD>", /* cursor WORD */
- #define SPEC_CCWORD 3
- "<cfile>", /* cursor path name */
- #define SPEC_CFILE 4
- "<sfile>", /* ":so" file name */
- #define SPEC_SFILE 5
- #ifdef FEAT_AUTOCMD
- "<afile>", /* autocommand file name */
- # define SPEC_AFILE 6
- "<abuf>", /* autocommand buffer number */
- # define SPEC_ABUF 7
- "<amatch>", /* autocommand match name */
- # define SPEC_AMATCH 8
- #endif
- #ifdef FEAT_CLIENTSERVER
- "<client>"
- # define SPEC_CLIENT 9
- #endif
- };
- #define SPEC_COUNT (sizeof(spec_str) / sizeof(char *))
#if defined(FEAT_AUTOCMD) || defined(FEAT_CLIENTSERVER)
char_u strbuf[30];
--- 9412,9417 ----
***************
*** 9400,9412 ****
/*
* Check if there is something to do.
*/
! for (spec_idx = 0; spec_idx < SPEC_COUNT; ++spec_idx)
! {
! *usedlen = (int)STRLEN(spec_str[spec_idx]);
! if (STRNCMP(src, spec_str[spec_idx], *usedlen) == 0)
! break;
! }
! if (spec_idx == SPEC_COUNT) /* no match */
{
*usedlen = 1;
return NULL;
--- 9424,9431 ----
/*
* Check if there is something to do.
*/
! spec_idx = find_cmdline_var(src, usedlen);
! if (spec_idx < 0) /* no match */
{
*usedlen = 1;
return NULL;
*** ../vim-7.2a.012/src/proto/ex_docmd.pro Sat Nov 24 21:49:59 2007
--- src/proto/ex_docmd.pro Tue Jul 1 22:23:30 2008
***************
*** 46,51 ****
--- 46,52 ----
FILE *open_exfile __ARGS((char_u *fname, int forceit, char *mode));
void update_topline_cursor __ARGS((void));
void exec_normal_cmd __ARGS((char_u *cmd, int remap, int silent));
+ int find_cmdline_var __ARGS((char_u *src, int *usedlen));
char_u *eval_vars __ARGS((char_u *src, char_u *srcstart, int *usedlen, linenr_T *lnump, char_u **errormsg, int *escaped));
char_u *expand_sfile __ARGS((char_u *arg));
int put_eol __ARGS((FILE *fd));
*** ../vim-7.2a.012/src/proto/misc2.pro Wed Jun 25 00:45:23 2008
--- src/proto/misc2.pro Wed Jul 2 22:39:49 2008
***************
*** 29,35 ****
char_u *vim_strnsave __ARGS((char_u *string, int len));
char_u *vim_strsave_escaped __ARGS((char_u *string, char_u *esc_chars));
char_u *vim_strsave_escaped_ext __ARGS((char_u *string, char_u *esc_chars, int cc, int bsl));
! char_u *vim_strsave_shellescape __ARGS((char_u *string));
char_u *vim_strsave_up __ARGS((char_u *string));
char_u *vim_strnsave_up __ARGS((char_u *string, int len));
void vim_strup __ARGS((char_u *p));
--- 29,35 ----
char_u *vim_strnsave __ARGS((char_u *string, int len));
char_u *vim_strsave_escaped __ARGS((char_u *string, char_u *esc_chars));
char_u *vim_strsave_escaped_ext __ARGS((char_u *string, char_u *esc_chars, int cc, int bsl));
! char_u *vim_strsave_shellescape __ARGS((char_u *string, int do_special));
char_u *vim_strsave_up __ARGS((char_u *string));
char_u *vim_strnsave_up __ARGS((char_u *string, int len));
void vim_strup __ARGS((char_u *p));
*** ../vim-7.2a.012/src/misc2.c Tue Jun 24 23:27:23 2008
--- src/misc2.c Fri Jul 4 11:37:55 2008
***************
*** 1262,1277 ****
* Escape "string" for use as a shell argument with system().
* This uses single quotes, except when we know we need to use double qoutes
* (MS-DOS and MS-Windows without 'shellslash' set).
* Returns the result in allocated memory, NULL if we have run out.
*/
char_u *
! vim_strsave_shellescape(string)
char_u *string;
{
unsigned length;
char_u *p;
char_u *d;
char_u *escaped_string;
/* First count the number of extra bytes required. */
length = (unsigned)STRLEN(string) + 3; /* two quotes and a trailing NUL */
--- 1262,1280 ----
* Escape "string" for use as a shell argument with system().
* This uses single quotes, except when we know we need to use double qoutes
* (MS-DOS and MS-Windows without 'shellslash' set).
+ * Also replace "%", "#" and things like "<cfile>" when "do_special" is TRUE.
* Returns the result in allocated memory, NULL if we have run out.
*/
char_u *
! vim_strsave_shellescape(string, do_special)
char_u *string;
+ int do_special;
{
unsigned length;
char_u *p;
char_u *d;
char_u *escaped_string;
+ int l;
/* First count the number of extra bytes required. */
length = (unsigned)STRLEN(string) + 3; /* two quotes and a trailing NUL */
***************
*** 1287,1292 ****
--- 1290,1300 ----
# endif
if (*p == '\'')
length += 3; /* ' => '\'' */
+ if (do_special && find_cmdline_var(p, &l) >= 0)
+ {
+ ++length; /* insert backslash */
+ p += l - 1;
+ }
}
/* Allocate memory for the result and fill it. */
***************
*** 1320,1332 ****
# endif
if (*p == '\'')
{
! *d++='\'';
! *d++='\\';
! *d++='\'';
! *d++='\'';
++p;
continue;
}
MB_COPY_CHAR(p, d);
}
--- 1328,1346 ----
# endif
if (*p == '\'')
{
! *d++ = '\'';
! *d++ = '\\';
! *d++ = '\'';
! *d++ = '\'';
++p;
continue;
}
+ if (do_special && find_cmdline_var(p, &l) >= 0)
+ {
+ *d++ = '\\'; /* insert backslash */
+ while (--l >= 0) /* copy the var */
+ *d++ = *p++;
+ }
MB_COPY_CHAR(p, d);
}
***************
*** 2776,2782 ****
return 0;
}
! #ifdef FEAT_CMDL_COMPL
char_u *
get_key_name(i)
int i;
--- 2790,2796 ----
return 0;
}
! #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
char_u *
get_key_name(i)
int i;
***************
*** 2787,2793 ****
}
#endif
! #ifdef FEAT_MOUSE
/*
* Look up the given mouse code to return the relevant information in the other
* arguments. Return which button is down or was released.
--- 2801,2807 ----
}
#endif
! #if defined(FEAT_MOUSE) || defined(PROTO)
/*
* Look up the given mouse code to return the relevant information in the other
* arguments. Return which button is down or was released.
*** ../vim-7.2a.012/src/version.c Wed Jul 2 21:04:36 2008
--- src/version.c Fri Jul 4 11:27:40 2008
***************
*** 678,679 ****
--- 678,681 ----
{ /* Add new patch number below this line */
+ /**/
+ 13,
/**/
--
Although the scythe isn't pre-eminent among the weapons of war, anyone who
has been on the wrong end of, say, a peasants' revolt will know that in
skilled hands it is fearsome.
-- (Terry Pratchett, Mort)
/// Bram Moolenaar --
[email protected] --
http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ \\\
\\\ download, build and distribute --
http://www.A-A-P.org ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///