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    ///