To: vim-dev@vim.org Subject: Patch 7.2a.013 Fcc: outbox From: Bram Moolenaar 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 "" 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(''), 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 with 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 with 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 + "", /* cursor word */ + #define SPEC_CWORD 2 + "", /* cursor WORD */ + #define SPEC_CCWORD 3 + "", /* cursor path name */ + #define SPEC_CFILE 4 + "", /* ":so" file name */ + #define SPEC_SFILE 5 + #ifdef FEAT_AUTOCMD + "", /* autocommand file name */ + # define SPEC_AFILE 6 + "", /* autocommand buffer number */ + # define SPEC_ABUF 7 + "", /* autocommand match name */ + # define SPEC_AMATCH 8 + #endif + #ifdef FEAT_CLIENTSERVER + "" + # 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 - "", /* cursor word */ - #define SPEC_CWORD 2 - "", /* cursor WORD */ - #define SPEC_CCWORD 3 - "", /* cursor path name */ - #define SPEC_CFILE 4 - "", /* ":so" file name */ - #define SPEC_SFILE 5 - #ifdef FEAT_AUTOCMD - "", /* autocommand file name */ - # define SPEC_AFILE 6 - "", /* autocommand buffer number */ - # define SPEC_ABUF 7 - "", /* autocommand match name */ - # define SPEC_AMATCH 8 - #endif - #ifdef FEAT_CLIENTSERVER - "" - # 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 "" 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 -- Bram@Moolenaar.net -- 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 ///